# UNITY - Adapty Documentation (Full Content) This file contains the complete content of all documentation pages for this platform. Locale: zh Generated on: 2026-07-24T13:01:53.540Z Total files: 41 --- # File: sdk-installation-unity --- --- title: "安装与配置 Unity SDK" description: "在 Unity 中为订阅类应用安装 Adapty SDK 的分步指南。" --- Adapty SDK 包含两个关键模块,可无缝集成到您的 Unity 应用中: - **Core Adapty**:这是 Adapty 正常运行所必需的核心 SDK。 - **AdaptyUI**:如果您使用 [Adapty 付费墙编辑工具](adapty-paywall-builder)(一款无需编写代码即可轻松创建跨平台付费墙的可视化工具),则需要此模块。 :::tip 想了解 Adapty SDK 是如何集成到移动应用中的真实案例吗?欢迎查看我们的[示例应用](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets),其中演示了完整的接入流程,包括展示付费墙、发起购买以及其他基础功能。 ::: ## 环境要求 \{#requirements\} Adapty SDK 支持 iOS 13.0+,但需要 iOS 15.0+ 才能与付费墙编辑工具中创建的付费墙配合使用。 :::info Adapty 兼容 Google Play Billing Library 最高至 8.x 版本。默认情况下,Adapty 使用 Google Play Billing Library v7.0.0。如需使用更新版本,请在 Android 构建中[覆盖 Billing 依赖项](https://developer.android.com/google/play/billing/integrate#dependency)。 ::: :::info 安装 SDK 是 Adapty 配置流程的第 5 步。在应用内购买正常运行之前,您还需要将应用连接到各应用商店,然后在 Adapty 看板中创建产品、付费墙和版位。[快速入门指南](quickstart)涵盖了所有必要步骤。 ::: ## 安装 Adapty SDK \{#install-adapty-sdk\} [![Release](https://img.shields.io/github/v/release/adaptyteam/AdaptySDK-Unity.svg?style=flat&logo=unity)](https://github.com/adaptyteam/AdaptySDK-Unity/releases) 选择你偏好的安装方式: 通过 Unity Package Manager 使用 Git URL 安装 Adapty SDK: 1. 在 Unity 中,打开 **Window → Package Manager**。 2. 点击左上角的 **+**,然后选择 **Add package from git URL...**。 3. 输入以下 URL 并点击 **Add**: ``` https://github.com/adaptyteam/AdaptySDK-Unity.git?path=Packages/com.adapty.unity-sdk#upm ``` 有关详细信息,请参阅 Unity 的[从 Git URL 安装 UPM 包](https://docs.unity3d.com/Manual/upm-ui-giturl.html)指南。 从 GitHub 下载 [`adapty-unity-plugin-*.unitypackage`](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Releases) 并将其导入到你的项目中。 安装 SDK 后,请完成以下步骤: 1. 安装 [External Dependency Manager (EDM) 插件](https://github.com/googlesamples/unity-jar-resolver#getting-started)。Adapty SDK 使用它来处理 iOS Cocoapods 依赖项和 Android gradle 依赖项。 2. 安装 EDM 后,您可能需要调用依赖管理器: `Assets -> External Dependency Manager -> Android Resolver -> Force Resolve` 以及 `Assets -> External Dependency Manager -> iOS Resolver -> Install Cocoapods` 3. 在为 iOS 构建 Unity 项目时,您会得到 `Unity-iPhone.xcworkspace` 文件,您必须打开该文件而非 `Unity-iPhone.xcodeproj`,否则 Cocoapods 依赖项将不会被使用。 ## 激活 Adapty SDK 的 Adapty 模块 \{#activate-adapty-module-of-adapty-sdk\} 在你的应用代码中激活 Adapty SDK。 :::note Adapty SDK 在你的应用中只需激活一次。 ::: 获取您的 **Public SDK Key**: 1. 打开 Adapty 看板,导航至 [**App settings → General**](https://app.adapty.io/settings/general)。 2. 在 **Api keys** 部分,复制 **Public SDK Key**(不是 Secret Key)。 3. 将代码中的 `"YOUR_PUBLIC_SDK_KEY"` 替换为实际值。 或者,使用 [Adapty CLI](developer-cli) 以编程方式获取: ``` npm install -g adapty adapty auth login adapty apps list ``` 或者,直接运行: ``` npx adapty auth login adapty apps list ``` - 请确保使用 **Public SDK key** 初始化 Adapty,**Secret key** 仅用于[服务端 API](getting-started-with-server-side-api)。 - **SDK keys** 对每个应用都是唯一的,如果您有多个应用,请确保选择正确的那个。 ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { void Start() { DontDestroyOnLoad(this.gameObject); Adapty.SetEventListener(this); var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); } public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` :::important 在调用任何其他 Adapty SDK 方法之前,请等待 `Activate` 完成回调。完整调用顺序请参阅 [Unity SDK 调用顺序](unity-sdk-call-order)。 ::: ## 设置事件监听 \{#set-up-event-listening\} 创建一个脚本来监听 Adapty 事件,在场景中将其命名为 `AdaptyListener`。建议对该对象使用 `DontDestroyOnLoad` 方法,确保它在应用程序的整个生命周期内持续存在。 Adapty 使用 `AdaptySDK` 命名空间。在使用 Adapty SDK 的脚本文件顶部,你可以添加: ```csharp showLineNumbers title="C#" using AdaptySDK; ``` 订阅 Adapty 事件: ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { public void OnLoadLatestProfile(AdaptyProfile profile) { // handle updated profile data } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` 我们建议调整脚本执行顺序,将 AdaptyListener 放在 Default Time 之前,以确保 Adapty 尽早完成初始化。 接下来,在应用中配置付费墙: - 如果您使用 [Adapty 付费墙编辑工具](adapty-paywall-builder),请先[激活 AdaptyUI 模块](#activate-adaptyui-module-of-adapty-sdk),然后按照[付费墙编辑工具快速入门](unity-quickstart-paywalls)进行操作。 - 如果您自行构建付费墙 UI,请参阅[自定义付费墙快速入门](unity-quickstart-manual)。 ## 激活 Adapty SDK 的 AdaptyUI 模块 \{#activate-adaptyui-module-of-adapty-sdk\} 如果您计划使用[付费墙编辑工具](adapty-paywall-builder)并已安装 AdaptyUI 模块,则需要激活 AdaptyUI。您可以在配置过程中激活它: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetActivateUI(true); ``` ## 可选设置 \{#optional-setup\} ### 日志记录 \{#logging\} #### 配置日志系统 \{#set-up-the-logging-system\} Adapty 会记录错误和其他重要信息,帮助你了解运行情况。以下是可用的日志级别: | Level | Description | | ---------- | ------------------------------------------------------------ | | `error` | 仅记录错误日志 | | `warn` | 记录错误以及 SDK 中不会导致严重错误但值得关注的消息 | | `info` | 记录错误、警告及各类信息消息 | | `verbose` | 记录所有可能在调试时有用的附加信息,例如函数调用、API 请求等 | 你可以在配置 Adapty 时设置应用的日志级别: ```csharp showLineNumbers title="C#" // 'verbose' is recommended for development and the first production release var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY"); builder.LogLevel = AdaptyLogLevel.Verbose; ``` 你也可以在运行时动态修改日志级别: ```csharp showLineNumbers title="C#" Adapty.SetLogLevel(AdaptyLogLevel.Verbose, (error) => { // handle result }); ``` ### 数据政策 \{#data-policies\} Adapty 不会存储用户的个人数据,除非您明确发送,但您可以实施额外的数据安全策略,以符合应用商店或所在国家/地区的法规要求。 #### 禁用 IP 地址采集与共享 \{#disable-ip-address-collection-and-sharing\} 在激活 Adapty 模块时,将 `SetIPAddressCollectionDisabled` 设置为 `true` 即可禁用用户 IP 地址的采集与共享。默认值为 `false`。 使用此参数可增强用户隐私保护、遵守地区性数据保护法规(如 GDPR 或 CCPA),或在应用不需要基于 IP 的功能时减少不必要的数据采集。 ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetIPAddressCollectionDisabled(true); ``` #### 禁止采集和共享广告 ID \{#disable-advertising-id-collection-and-sharing\} 在激活 Adapty 模块时,将 `SetAppleIDFACollectionDisabled` 和/或 `SetGoogleAdvertisingIdCollectionDisabled` 设置为 `true` 可禁用广告标识符的收集。默认值为 `false`。 如需遵守 App Store/Google Play 政策、避免触发 App 跟踪透明度提示,或者你的应用不需要基于广告 ID 的广告归因或分析功能,可使用此参数。 ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAppleIDFACollectionDisabled(true) .SetGoogleAdvertisingIdCollectionDisabled(true); ``` #### 为 AdaptyUI 配置媒体缓存 \{#set-up-media-cache-configuration-for-adaptyui\} 默认情况下,AdaptyUI 会缓存媒体内容(如图片和视频),以提升性能并减少网络流量。你可以通过提供自定义配置来调整缓存设置。 使用 `SetAdaptyUIMediaCache` 覆盖默认缓存设置: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAdaptyUIMediaCache( 100 * 1024 * 1024, // MemoryStorageTotalCostLimit 100MB null, // MemoryStorageCountLimit 100 * 1024 * 1024 // DiskStorageSizeLimit 100MB ); ``` 参数: | 参数 | 是否必填 | 描述 | |-----------------------------|----------|---------------------------------------------------| | memoryStorageTotalCostLimit | 可选 | 内存缓存大小(字节)。默认值因平台而异。 | | memoryStorageCountLimit | 可选 | 内存存储的条目数量上限。默认值因平台而异。 | | diskStorageSizeLimit | 可选 | 磁盘文件大小上限(字节)。默认值因平台而异。 | ### 启用本地访问等级(Android) \{#enable-local-access-levels-android\} 默认情况下,[本地访问等级](local-access-levels)在 iOS 上已启用,在 Android 上已禁用。若要在 Android 上同样启用,请将 `SetGoogleLocalAccessLevelAllowed` 设置为 `true`: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetGoogleLocalAccessLevelAllowed(true); ``` ### 备份恢复时清除数据 \{#clear-data-on-backup-restore\} 当 `SetAppleClearDataOnBackup` 设置为 `true` 时,SDK 会检测应用从 iCloud 备份恢复的情况,并删除所有本地存储的 SDK 数据,包括已缓存的用户画像信息、产品详情和付费墙。之后 SDK 将以全新状态完成初始化。默认值为 `false`。 :::note 仅删除本地 SDK 缓存。Apple 的交易记录及 Adapty 服务器上的用户数据不受影响。 ::: ```csharp showLineNumbers title="C#" var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetAppleClearDataOnBackup(true); ``` ## 故障排查 \{#troubleshooting\} #### Android 备份规则(Auto Backup 配置) \{#android-backup-rules-auto-backup-configuration\} 部分 SDK(包括 Adapty)会自带 Android 自动备份配置。如果多个 SDK 都定义了备份规则,Android 清单合并工具可能会报错,提示 `android:fullBackupContent`、`android:dataExtractionRules` 或 `android:allowBackup` 相关问题。 常见错误示例:`Manifest merger failed: Attribute application@dataExtractionRules value=(@xml/your_data_extraction_rules) is also present at [com.other.sdk:library:1.0.0] value=(@xml/other_sdk_data_extraction_rules)` :::note 以下更改应在你的 Android 平台目录(通常位于项目的 `android/` 文件夹)中进行。 ::: 要解决此问题,你需要: - 告知清单合并工具使用应用自身的备份相关属性值。 - 创建备份规则文件,将 Adapty 的规则与其他 SDK 的规则合并。 #### 1. 在清单中添加 `tools` 命名空间 \{#1-add-the-tools-namespace-to-your-manifest\} 在 `AndroidManifest.xml` 文件中,确保根标签 `` 包含 tools: ```xml ... ``` #### 2. 在 `` 中覆盖备份属性 \{#2-override-backup-attributes-in-application\} 在同一个 `AndroidManifest.xml` 文件中,更新 `` 标签,使应用提供最终属性值,并告知清单合并工具替换库中的值: ```xml ... ``` 如果某个 SDK 也设置了 `android:allowBackup`,请将其一并加入 `tools:replace`: ```xml tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules" ``` #### 3. 创建合并后的备份规则文件 \{#3-create-merged-backup-rules-files\} 在 Android 项目的 `res/xml/` 目录下创建 XML 文件,将 Adapty 的规则与其他 SDK 的规则合并。Android 根据系统版本使用不同的备份规则格式,同时创建两个文件可确保应用所支持的所有 Android 版本都能正常兼容。 :::note 以下示例以 AppsFlyer 作为第三方 SDK 的示例。请替换或添加你应用中实际使用的其他 SDK 的规则。 ::: **Android 12 及更高版本**(使用新的数据提取规则格式): ```xml title="sample_data_extraction_rules.xml" ``` **Android 11 及更低版本**(使用旧版完整备份内容格式): ```xml title="sample_backup_rules.xml" :::important 在 Unity 中,请将上述更改应用到 `Assets/Plugins/Android/AndroidManifest.xml`,并在 `Assets/Plugins/Android/res/xml/` 目录下创建备份规则文件。 ::: #### Android 中从其他应用返回后购买失败 \{#purchases-fail-after-returning-from-another-app-in-android\} 如果启动购买流程的 Activity 使用了非默认的 `launchMode`,当用户从 Google Play、银行应用或浏览器返回时,Android 可能会以错误的方式重建或复用该 Activity。这会导致购买结果丢失或被当作已取消处理。 为确保购买流程正常运行,请仅将启动购买流程的 Activity 的启动模式设置为 `standard` 或 `singleTop`,避免使用其他任何模式。 在 `AndroidManifest.xml` 中,确保启动购买流程的 Activity 设置为 `standard` 或 `singleTop`: ```xml ``` #### Android 上显示付费墙时应用崩溃 \{#app-crashes-when-a-paywall-is-displayed-on-android\} 如果应用在 Android 上显示付费墙时崩溃,可能是因为 Gradle 配置中缺少 Kotlin 插件。添加方法如下: 1. 在 **Player Settings** 中,确保已勾选 **Custom Launcher Gradle Template** 和 **Custom Base Gradle Template** 选项。 2. 将以下内容添加到 `/Assets/Plugins/Android/launcherTemplate.gradle`: ```groovy showLineNumbers apply plugin: 'com.android.application' // highlight-next-line apply plugin: 'kotlin-android' apply from: 'setupSymbols.gradle' apply from: '../shared/keepUnitySymbols.gradle' ``` 3. 将以下内容添加到 `/Assets/Plugins/Android/baseProjectTemplate.gradle`: ```groovy showLineNumbers plugins { // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version id 'com.android.application' version '8.3.0' apply false id 'com.android.library' version '8.3.0' apply false // highlight-next-line id 'org.jetbrains.kotlin.android' version '1.8.0' apply false **BUILD_SCRIPT_DEPS** } ``` --- # File: unity-quickstart-paywalls --- --- title: "通过 Unity SDK 中的付费墙启用购买功能" description: "了解如何在 Unity 应用中使用 Adapty SDK 展示付费墙。" --- 要启用应用内购买,您需要了解三个关键概念: - [**产品**](product) – 用户可以购买的任何内容(订阅、消耗型商品、永久授权) - [**付费墙**](paywalls) 是定义要提供哪些产品的配置。在 Adapty 中,付费墙是检索产品的唯一方式,但这种设计让您无需修改应用代码即可更改产品组合、定价和优惠内容。 - [**版位**](placements) – 在应用中展示付费墙的位置和时机(如 `main`、`onboarding`、`settings`)。您在看板中为版位配置付费墙,然后在代码中通过版位 ID 请求它们。这使得运行 A/B 测试以及向不同用户展示不同付费墙变得更加简单。 Adapty 为您提供三种在应用中启用购买功能的方式。请根据应用需求选择其中一种: | 实现方式 | 复杂度 | 适用场景 | |---|---|---| | Adapty 付费墙编辑工具 | ✅ 简单 | 您[在无代码编辑工具中创建完整的、可立即购买的付费墙](quickstart-paywalls)。Adapty 自动渲染付费墙,并在后台处理所有复杂的购买流程、收据验证和订阅管理。 | | 手动创建的付费墙 | 🟡 中等 | 您在应用代码中实现付费墙 UI,但仍从 Adapty 获取付费墙对象以保持产品组合的灵活性。请参阅[指南](unity-quickstart-manual)。 | | 观察者模式 | 🔴 困难 | 您已有自己的购买处理基础设施并希望继续使用。请注意,观察者模式在 Adapty 中有一定限制。请参阅[文章](observer-vs-full-mode)。 | :::important **以下步骤展示如何实现在 Adapty 付费墙编辑工具中创建的付费墙。** 如果您不想使用付费墙编辑工具,请参阅[处理手动创建付费墙中购买的指南](unity-making-purchases)。 ::: 要展示在 Adapty 付费墙编辑工具中创建的付费墙,在应用代码中您只需: 1. **获取付费墙**:从 Adapty 获取付费墙。 2. **展示付费墙,Adapty 将为您处理购买流程**:在应用中显示您获取到的付费墙容器。 3. **处理按钮操作**:将用户与付费墙的交互与应用的响应关联起来。例如,当用户点击按钮时打开链接或关闭付费墙。 ## 开始之前 \{#before-you-start\} 在开始之前,请完成以下步骤: 1. 在 Adapty 看板中将您的应用连接到 [App Store](initial_ios) 和/或 [Google Play](initial-android)。 2. 在 Adapty 中[创建产品](create-product)。 3. [创建付费墙并向其添加产品](create-paywall)。 4. [创建版位并将付费墙添加到其中](create-placement)。 5. 在应用代码中[安装并激活 Adapty SDK](sdk-installation-unity)。 :::tip 完成这些步骤最快的方式是按照[快速入门指南](quickstart)操作,或使用 [Developer CLI](developer-cli-quickstart) 创建付费墙和版位。 ::: ## 1. 获取付费墙 \{#1-get-the-paywall\} 您的付费墙与在看板中配置的版位关联。版位允许您为不同目标受众运行不同的付费墙,或运行 [A/B 测试](ab-tests)。 要获取在 Adapty 付费墙编辑工具中创建的付费墙,您需要: 1. 使用 `GetPaywall` 方法通过[版位](placements) ID 获取 `paywall` 对象,并使用 `HasViewConfiguration` 属性检查它是否是在编辑工具中创建的付费墙。 2. 使用 `CreatePaywallView` 方法创建付费墙视图。该视图包含展示付费墙所需的 UI 元素和样式。 :::important 要获取视图配置,您必须在付费墙编辑工具中开启 **Show on device** 开关。否则,您将获得空的视图配置,付费墙将无法显示。 ::: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if(error != null) { // handle the error return; } // Create paywall view parameters var parameters = new AdaptyUICreatePaywallViewParameters(); // Create the paywall view AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if(error != null) { // handle the error return; } // view - the paywall view ready to be presented }); }); ``` :::info 本快速入门提供展示付费墙所需的最低配置。有关高级配置详情,请参阅我们的[获取付费墙指南](unity-get-pb-paywalls)。 ::: ## 2. 展示付费墙 \{#2-display-the-paywall\} 现在,当您已获得付费墙配置后,只需添加几行代码即可展示付费墙。 要展示付费墙,请对由 `CreatePaywallView` 方法创建的 `view` 调用 `view.Present()` 方法。每个 `view` 只能使用一次。如果需要再次展示付费墙,请再次调用 `CreatePaywallView` 创建新的 `view` 实例。 ```csharp showLineNumbers title="Unity" view.Present((error) => { // handle the error }); ``` :::info 有关如何展示付费墙的更多详情,请参阅我们的[指南](unity-present-paywalls)。 ::: ## 3. 处理按钮操作 \{#3-handle-button-actions\} 当用户点击付费墙中的按钮时,Unity SDK 会自动处理购买和恢复操作。但是,其他按钮具有自定义或预定义的 ID,需要在您的代码中处理相应操作。 例如,您的付费墙可能有一个关闭按钮和需要打开的 URL(如使用条款和隐私政策)。要处理这些操作,您的类需要实现 `AdaptyPaywallsEventsListener` 接口并注册为监听器。 :::tip 请阅读我们关于如何处理按钮[操作](unity-handle-paywall-actions)和[事件](unity-handling-events)的指南。 ::: ```csharp showLineNumbers title="Unity" public class YourClass : MonoBehaviour, AdaptyPaywallsEventsListener { void Start() { // Register this class as the paywall events listener Adapty.SetPaywallsEventsListener(this); } // AdaptyPaywallsEventsListener method - handles button actions public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: view.Dismiss(null); break; case AdaptyUIUserActionType.OpenUrl: Application.OpenURL(action.Value); break; default: break; } } } ``` ## 后续步骤 \{#next-steps\} :::tip 有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助! ::: 您的付费墙已准备好在应用中展示。在 [App Store 沙盒](test-purchases-in-sandbox)或 [Google Play Store](testing-on-android) 中测试您的购买,以确保可以从付费墙完成测试购买。 接下来,您需要[检查用户的访问等级](unity-check-subscription-status),以确保向正确的用户展示付费墙或授予付费功能的访问权限。 ## 完整示例 \{#full-example\} 以下是如何将所有步骤整合到您的应用中的完整示例。 ```csharp showLineNumbers using System; using UnityEngine; using AdaptySDK; public class PaywallManager : MonoBehaviour, AdaptyPaywallsEventsListener { [SerializeField] private string placementId = "YOUR_PLACEMENT_ID"; private AdaptyUIPaywallView currentPaywallView; void Start() { // Register for paywall events Adapty.SetPaywallsEventsListener(this); GetAndDisplayPaywall(); } private void GetAndDisplayPaywall() { Adapty.GetPaywall(placementId, (paywall, error) => { if (error != null) { Debug.LogError("Error getting paywall: " + error.Message); return; } if (paywall.HasViewConfiguration) { CreateAndPresentPaywallView(paywall); } else { Debug.LogWarning("Paywall was not created using the builder"); } }); } private void CreateAndPresentPaywallView(AdaptyPaywall paywall) { var parameters = new AdaptyUICreatePaywallViewParameters(); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if (error != null) { Debug.LogError("Error creating paywall view: " + error.Message); return; } currentPaywallView = view; view.Present((presentError) => { if (presentError != null) { Debug.LogError("Error presenting paywall: " + presentError.Message); return; } Debug.Log("Paywall presented successfully"); }); }); } // AdaptyPaywallsEventsListener implementation public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: Debug.Log("Close button pressed"); view.Dismiss(null); break; case AdaptyUIUserActionType.OpenUrl: Application.OpenURL(action.Value); break; default: break; } } // Required interface methods (implement as needed) public void PaywallViewDidAppear(AdaptyUIPaywallView view) { } public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { } public void PaywallViewDidSelectProduct(AdaptyUIPaywallView view, string productId) { } public void PaywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) { } public void PaywallViewDidFinishPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyPurchaseResult purchasedResult) { } public void PaywallViewDidFailPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) { } public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { } public void PaywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) { } public void PaywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) { } public void PaywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error) { } public void ShowPaywall() { GetAndDisplayPaywall(); } void OnDestroy() { if (currentPaywallView != null) { currentPaywallView.Dismiss(null); } } } ``` --- # File: unity-check-subscription-status --- --- title: "在 Unity SDK 中检查订阅状态" description: "了解如何在 Unity 应用中使用 Adapty 检查订阅状态。" --- 要判断用户是否可以访问付费内容或查看付费墙,您需要在用户画像中检查其[访问等级](access-level)。 本文介绍如何访问用户画像状态,以决定向用户展示什么内容——是显示付费墙还是授予付费功能的访问权限。 ## 获取订阅状态 \{#get-subscription-status\} 当您需要决定是否向用户显示付费墙或付费内容时,需要检查其用户画像中的[访问等级](access-level)。您有两种选择: - 如果需要立即获取最新的用户画像数据(例如在应用启动时)或希望强制更新,请调用 `GetProfile`。 - 设置**自动用户画像更新**,以在订阅状态发生变化时自动刷新本地副本。 ### 获取用户画像 \{#get-profile\} 获取订阅状态最简单的方法是使用 `GetProfile` 方法访问用户画像: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access }); ``` ### 监听订阅更新 \{#listen-to-subscription-updates\} 要在应用中自动接收用户画像更新: 1. 继承 `AdaptyEventListener` 并实现 `OnLoadLatestProfile` 方法——每当用户的订阅状态发生变化时,Adapty 会自动调用此方法。 2. 在该方法被调用时存储更新后的用户画像数据,以便在整个应用中使用,无需发起额外的网络请求。 ```csharp public class SubscriptionManager : MonoBehaviour, AdaptyEventListener { private AdaptyProfile currentProfile; void Start() { // Register this object as an Adapty event listener Adapty.SetEventListener(this); } // Store the profile when it updates public void OnLoadLatestProfile(AdaptyProfile profile) { currentProfile = profile; // Update UI, unlock content, etc. } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } // Use stored profile instead of calling getProfile() public bool HasAccess() { if (currentProfile?.AccessLevels != null && currentProfile.AccessLevels.ContainsKey("premium")) { return currentProfile.AccessLevels["premium"].IsActive; } return false; } } ``` :::note 每当应用启动时,Adapty 会自动调用 `OnLoadLatestProfile`,即使设备处于离线状态,也能提供缓存的订阅数据。 ::: ## 将用户画像与付费墙逻辑关联 \{#connect-profile-with-paywall-logic\} 当您需要立即决定是否显示付费墙或授予付费功能访问权限时,可以直接检查用户的用户画像。此方法适用于以下场景:应用启动、进入付费区域,或在展示特定内容之前。 ```csharp private void CheckAccessLevel() { Adapty.GetProfile((profile, error) => { if (error != null) { Debug.LogError("Error checking access level: " + error.Message); // Show paywall if access check fails return; } var accessLevel = profile.AccessLevels["YOUR_ACCESS_LEVEL"]; if (accessLevel == null || !accessLevel.IsActive) { // Show paywall if no access } }); } private void InitializePaywall() { LoadPaywall(); CheckAccessLevel(); } ``` ## 后续步骤 \{#next-steps\} 现在您已了解如何追踪订阅状态,接下来请学习如何[使用用户画像](unity-quickstart-identify),以确保用户能够访问其已付费的内容。 --- # File: unity-quickstart-identify --- --- title: "在 Unity SDK 中识别用户" description: "在 Unity 中设置 Adapty 进行应用内订阅管理的快速入门指南。" --- :::important 本指南适用于有自己身份验证系统的开发者。你将了解如何在 Adapty 中管理用户画像,使其与你现有的身份验证系统保持一致。 ::: 用户购买行为的管理方式取决于你的应用身份验证模型: - 如果你的应用不使用后端身份验证且不存储用户数据,请参阅[匿名用户部分](#anonymous-users)。 - 如果你的应用已有(或将有)后端身份验证,请参阅[已识别用户部分](#identified-users)。 **核心概念**: - **用户画像**是 SDK 运行所必需的实体,由 Adapty 自动创建。 - 用户画像可以是匿名的**(不含 customer user ID)**,也可以是已识别的**(含 customer user ID)**。 - 您提供 **customer user ID** 是为了将 Adapty 中的用户画像与您内部的身份认证系统进行关联。 以下是匿名用户与已识别用户的区别: | | 匿名用户 | 已识别用户 | |-------------------------|-----------------------------------|-----------------------------------------------| | **购买管理** | 通过应用商店恢复购买 | 通过客户用户 ID 跨设备保留购买历史 | | **用户画像管理** | 每次重新安装都会创建新的用户画像 | 跨会话和设备共享同一用户画像 | | **数据持久性** | 匿名用户的数据与应用安装绑定 | 已识别用户的数据在应用重新安装后仍可保留 | ## 匿名用户 \{#anonymous-users\} 如果你没有后端身份验证,**则无需在应用代码中处理身份验证**: 1. 当 SDK 在应用首次启动时激活,Adapty 会**为该用户创建一个新的用户画像**。 2. 当用户在应用内购买任何商品时,该购买记录会**关联到其 Adapty 用户画像及其应用商店账户**。 3. 当用户**重新安装**应用或在**新设备**上安装时,Adapty 会**在激活时创建一个新的匿名用户画像**。 4. 如果用户之前在您的应用中有过购买记录,默认情况下,SDK 激活时会自动从 App Store 同步其购买历史。 因此,对于匿名用户,每次安装都会创建新的用户画像,但这不是问题,因为在 Adapty 分析中,你可以[配置什么会被视为新安装](general#4-installs-definition-for-analytics)。 对于匿名用户,你需要按**设备 ID** 统计安装量。在这种情况下,设备上的每次应用安装都会被计为一次安装,包括重新安装。 ## 已识别用户 \{#identified-users\} 您有两种方式在应用中识别用户: - [**在登录/注册时:**](#during-loginsignup) 如果用户在应用启动后才登录,请在他们完成身份验证时调用 `identify()`,并传入 customer user ID。 - [**在 SDK 激活时:**](#during-the-sdk-activation) 如果应用启动时已有存储的 customer user ID,请在调用 `activate()` 时直接传入。 :::important 默认情况下,当 Adapty 收到来自某个 Customer User ID 的购买请求,而该 ID 当前已与另一个 Customer User ID 关联时,访问等级将被共享,两个用户画像都拥有付费访问权限。你可以将此设置配置为将付费访问权从一个用户画像转移到另一个,或完全禁用共享。详情请参阅[文章](general#6-sharing-paid-access-between-user-accounts)。 ::: ### 登录/注册期间 \{#during-loginsignup\} 如果你在应用启动后才识别用户身份(例如用户登录或注册之后),请使用 `identify` 方法设置其 customer user ID。 - 如果你**之前从未使用过该 customer user ID**,Adapty 会自动将其关联到当前用户画像。 - 如果你**之前已使用该 customer user ID 识别过该用户**,Adapty 会切换到与该 customer user ID 关联的用户画像。 :::important Customer user ID 对每个用户必须唯一。如果将该参数硬编码为固定值,所有用户将被视为同一人。 ::: 在调用其他 SDK 方法之前,请等待 `Identify` 的完成回调。并发调用会产生 `#3006 profileWasChanged` 错误,或导致操作落到匿名用户画像上。详见 [Unity SDK 调用顺序](unity-sdk-call-order)。 ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { // Unique for each user if(error == null) { // successful identify } }); ``` ### 在 SDK 激活期间 \{#during-the-sdk-activation\} 如果在激活 SDK 时已经知道用户 ID,可以直接在 `activate` 方法中传入,无需单独调用 `identify`。 如果知道用户 ID,但在激活后才进行设置,那么在激活时 Adapty 会先创建一个匿名用户画像,等到调用 `identify` 后才会切换到已有的用户画像。 您可以传入已有的客户用户 ID(即之前使用过的 ID),也可以传入新的 ID。若传入新 ID,激活时创建的新用户画像将自动与该客户用户 ID 关联。 :::note 默认情况下,创建匿名用户画像不会影响分析看板,因为安装量是基于设备 ID 来统计的。 设备 ID 代表从应用商店在设备上安装的一次应用实例,仅在重新安装应用后才会重新生成。 它与首次安装还是重复安装无关,也与是否使用了已有的客户用户 ID 无关。 创建用户画像(在 SDK 激活或退出登录时)、登录,或在不重新安装应用的情况下升级应用,均不会产生额外的安装事件。 如果您希望根据唯一用户而非设备来统计安装量,请前往 **App settings**,配置 [**Installs definition for analytics**](general#4-installs-definition-for-analytics)。 ::: ```csharp showLineNumbers using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID"); // 每个用户的 Customer User ID 必须唯一。如果硬编码该参数值,所有用户将被视为同一个人。 Adapty.Activate(builder.Build(), (error) => { if (error != null) { // 处理错误 return; } }); ``` ### 用户退出登录 \{#log-users-out\} 如果您有供用户退出登录的按钮,请使用 `logout` 方法。 :::important 用户退出登录会为用户创建一个新的匿名用户画像。 ::: ```csharp showLineNumbers Adapty.Logout((error) => { if(error == null) { // successful logout } }); ``` :::info 要让用户重新登录应用,请使用 `identify` 方法。 ::: ### 允许未登录状态下进行购买 \{#allow-purchases-without-login\} 如果用户在登录前后都可以进行购买,你需要确保他们登录后仍能保留访问权限: 1. 当未登录用户发起购买时,Adapty 会将其关联到该用户的匿名用户画像 ID。 2. 当用户登录账号后,Adapty 会切换到使用其已识别的用户画像。 - 如果是新的 customer user ID(例如购买发生在注册之前),Adapty 会将该 customer user ID 分配给当前用户画像,从而保留所有购买历史记录。 - 如果是已存在的 customer user ID(该 customer user ID 已关联到某个用户画像),则需要在用户画像切换后获取实际的访问等级。你可以在识别完成后立即调用 [`getProfile`](unity-check-subscription-status),或[监听用户画像更新](unity-check-subscription-status)以使数据自动同步。 ## 下一步 \{#next-steps\} 恭喜你!你已经在应用中成功实现了应用内付费逻辑!祝你的应用变现一切顺利! 想从 Adapty 获得更多价值,可以进一步探索以下内容: - [**测试**](troubleshooting-test-purchases):确保一切按预期运行 - [**用户引导**](onboardings):通过用户引导吸引用户并提升留存 - [**集成**](configuration):只需一行代码即可与营销归因和数据分析服务完成集成 - [**设置自定义用户画像属性**](unity-setting-user-attributes):为用户画像添加自定义属性并创建市场细分,从而发起 A/B 测试或向不同用户展示不同的付费墙 --- # File: adapty-sdk-integration-skill-unity --- --- title: "通过 SDK 集成技能将 Adapty 接入 Unity 应用" description: "使用 adapty-sdk-integration 技能,借助 AI 编码工具将 Adapty SDK 端到端集成到你的 Unity 应用中。" --- [adapty-sdk-integration skill](https://github.com/adaptyteam/adapty-sdk-integration-skill) 可以端到端地自动完成 Adapty 集成:看板配置、SDK 安装、付费墙设置以及各阶段验证。它会自动检测你的平台,并在每个阶段获取相关的 Adapty 文档。 **支持的工具**:Claude Code、GitHub Copilot CLI、OpenAI Codex、Gemini CLI。 安装时,请选择适合你所用工具的方式。完整列表请参阅 [skill README](https://github.com/adaptyteam/adapty-sdk-integration-skill)。 **Claude Code** ``` claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill claude plugin install adapty-sdk-integration@adapty ``` **GitHub Copilot CLI** ``` gh skill install adaptyteam/adapty-sdk-integration-skill ``` **Gemini CLI** ``` gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill ``` **OpenAI Codex 或其他工具** — 使用 [skills CLI](https://skills.sh)(注意:通过此方式安装的 skill 不会自动更新): ``` npx skills add adaptyteam/adapty-sdk-integration-skill ``` 也可以克隆仓库,将 `skills/adapty-sdk-integration/` 复制到你所用工具的 skills 目录中。 安装完成后,在项目中运行该 skill: ``` /adapty-sdk-integration ``` skill 会提出几个配置问题,然后逐步引导你完成看板配置、SDK 安装、付费墙设置和验证。 :::important 该功能目前处于测试阶段。如果遇到卡顿或异常情况,请参考[分步集成指南](adapty-cursor-unity)——它会引导你的 AI 工具逐步完成每个阶段的正确文档操作。 ::: [adapty-sdk-integration skill](https://github.com/adaptyteam/adapty-sdk-integration-skill) 可以端到端地自动完成 Adapty 集成:看板配置、SDK 安装、付费墙设置以及各阶段验证。它会自动检测你的平台,并在每个阶段获取相关的 Adapty 文档。 **支持的工具**:Claude Code、GitHub Copilot CLI、OpenAI Codex、Gemini CLI。 安装时,请选择适合你所用工具的方式。完整列表请参阅 [skill README](https://github.com/adaptyteam/adapty-sdk-integration-skill)。 **Claude Code** ``` claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill claude plugin install adapty-sdk-integration@adapty ``` **GitHub Copilot CLI** ``` gh skill install adaptyteam/adapty-sdk-integration-skill ``` **Gemini CLI** ``` gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill ``` **OpenAI Codex 或其他工具** — 使用 [skills CLI](https://skills.sh)(注意:通过此方式安装的 skill 不会自动更新): ``` npx skills add adaptyteam/adapty-sdk-integration-skill ``` 也可以克隆仓库,将 `skills/adapty-sdk-integration/` 复制到你所用工具的 skills 目录中。 安装完成后,在项目中运行该 skill: ``` /adapty-sdk-integration ``` skill 会提出几个配置问题,然后逐步引导你完成看板配置、SDK 安装、付费墙设置和验证。 --- # File: adapty-cursor-unity --- --- title: "借助 AI 将 Adapty 集成到 Unity 应用" description: "使用 Cursor、Context7、ChatGPT、Claude 或其他 AI 工具,将 Adapty 集成到 Unity 应用的分步指南。" --- 本指南将带你一步一步地将 Adapty 集成到你的 Unity 应用中,借助 AI 编程工具——按正确的顺序向它提供合适的 Adapty 文档即可。 For a fully automated integration, use the [adapty-sdk-integration skill](https://github.com/adaptyteam/adapty-sdk-integration-skill): it runs the whole integration from your AI coding tool in one command. ## 开始之前:看板配置 \{#before-you-start-dashboard-setup\} Adapty 在您编写任何 SDK 代码之前,需要先完成一些看板配置。您可以通过交互式 LLM 技能,或手动通过看板来完成配置。 ### Skill 方法 \{#skill-approach\} Adapty CLI skill 让你的 LLM 直接设置应用、产品、访问等级、付费墙和版位,无需为每个步骤打开看板。你只需要在看板中[连接你的商店](integrate-payments)。 ``` npx skills add adaptyteam/adapty-cli --skill adapty-cli ``` 添加 skill 后,在你的 agent 中运行 `/adapty-cli`。它将引导你完成每个步骤,包括何时需要打开看板连接你的商店。 ### 看板配置方式 \{#dashboard-approach\} 如果你倾向于手动配置所有内容,以下是编写代码前需要准备的信息。LLM 无法自动查找看板中的配置值,需要你手动提供。 1. **连接应用商店**:在 Adapty 看板中,前往 **App settings → General**,连接 App Store 和 Google Play(如果你的 Unity 应用同时支持两个平台)。这是购买功能正常运行的必要条件。 [连接应用商店](integrate-payments) 2. **复制您的 Public SDK key**:在 Adapty 看板中,前往 **App settings → General**,找到 **API keys** 部分。在代码中,这是您传递给 Adapty 配置构建器的字符串。 3. **至少创建一个产品**:在 Adapty 看板中,前往 **Products** 页面。您无需在代码中直接引用产品——Adapty 会通过付费墙来分发它们。 [添加产品](quickstart-products) 4. **创建付费墙和版位**:在 Adapty 看板中,在 **Paywalls** 页面创建付费墙,然后在 **Placements** 页面将其分配到一个版位。在代码中,版位 ID 就是传递给 `Adapty.GetPaywall("YOUR_PLACEMENT_ID")` 的字符串。 [创建付费墙](quickstart-paywalls) 5. **设置访问等级**:在 Adapty 看板的 **Products** 页面中按产品进行配置。在代码中,通过 `profile.AccessLevels["premium"]?.IsActive` 检查对应字符串。默认的 `premium` 访问等级适用于大多数应用。如果付费用户根据所购产品获得不同功能的访问权限(例如 `basic` 方案与 `pro` 方案),请在开始编码前[创建额外的访问等级](assigning-access-level-to-a-product)。 :::tip 准备好这五项信息后,就可以开始写代码了。告诉你的 LLM:"我的 Public SDK key 是 X,我的版位 ID 是 Y",这样它就能生成正确的初始化和付费墙获取代码。 ::: ### 准备就绪后进行设置 \{#set-up-when-ready\} 这些内容不是开始编码的必要条件,但随着集成的成熟,你会需要它们: - **A/B 测试**:在 **Placements** 页面进行配置,无需更改代码。 [A/B 测试](ab-tests) - **更多付费墙和版位**:添加更多使用不同版位 ID 的 `GetPaywall` 调用。 - **分析集成**:在 **Integrations** 页面进行配置,具体设置因集成而异。请参阅[分析集成](analytics-integration)和[归因集成](attribution-integration)。 ## 将 Adapty 文档输入到您的 LLM \{#feed-adapty-docs-to-your-llm\} ### 使用 Context7(推荐) [Context7](https://context7.com) 是一个 MCP 服务器,让你的 LLM 可以直接访问最新的 Adapty 文档。LLM 会根据你的提问自动获取相关文档,无需手动粘贴 URL。 Context7 支持 **Cursor**、**Claude Code**、**Windsurf** 以及其他兼容 MCP 的工具。运行以下命令即可完成配置: ``` npx ctx7 setup ``` 该命令会自动检测你的编辑器并配置 Context7 服务器。如需手动配置,请参阅 [Context7 GitHub 仓库](https://github.com/upstash/context7)。 配置完成后,在提示词中引用 Adapty 库: ``` Use the adaptyteam/adapty-docs library to look up how to install the Unity SDK ``` :::warning 虽然 Context7 无需手动粘贴文档链接,但实现顺序很重要。请按照下方的[实现步骤](#implementation-walkthrough)逐步操作,以确保一切正常运行。 ::: ### 使用纯文本文档 \{#use-plain-text-docs\} 您可以以纯文本 Markdown 格式访问任意 Adapty 文档。只需在 URL 末尾添加 `.md`,或点击文章标题下方的 **Copy for LLM**。例如:[adapty-cursor-unity.md](https://adapty.io/docs/zh/adapty-cursor-unity.md)。 下方[实施演练](#implementation-walkthrough)中的每个阶段都包含一个"Send this to your LLM"区块,其中附有可粘贴的 `.md` 链接。 如需一次性获取更多文档,请参阅下方的[索引文件与平台专属子集](#plain-text-doc-index-files)。 ## 实施演练 \{#implementation-walkthrough\} 本指南的其余部分按实施顺序介绍 Adapty 集成流程。每个阶段包含需要发送给 LLM 的文档、完成后应看到的效果以及常见问题。 ### 规划集成方案 \{#plan-your-integration\} 在开始写代码之前,先让 LLM 分析你的项目并制定实现计划。如果你的 AI 工具支持规划模式(例如 Cursor 或 Claude Code 的计划模式),建议先使用该模式,让 LLM 在生成代码前同时读取你的项目结构和 Adapty 文档。 告诉 LLM 你使用的购买方式——这会决定它应该参考哪些指南: - [**Adapty 付费墙编辑工具**](adapty-paywall-builder):在 Adapty 的无代码编辑工具中创建付费墙,SDK 会自动渲染。 - [**手动创建付费墙**](unity-making-purchases):自行编写付费墙 UI 代码,但仍使用 Adapty 获取产品并处理购买流程。 - [**观察者模式**](observer-vs-full-mode):保留现有的购买基础设施,仅使用 Adapty 进行数据分析和集成。 不确定该选哪种?请查看[快速入门中的对比表格](unity-quickstart-paywalls)。 ### 安装并配置 SDK \{#install-and-configure-the-sdk\} 通过 Unity Package Manager 添加 Adapty SDK 包,并使用你的公共 SDK 密钥激活它。这是一切的基础——没有它,其他功能都无法正常工作。 **指南:** [安装并配置 Adapty SDK](sdk-installation-unity) 将以下内容发送给你的 LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/zh/sdk-installation-unity.md ``` :::tip[Checkpoint] - **预期结果:** 项目成功构建并运行,Unity 控制台显示 Adapty 激活日志。 - **常见问题:** "Public API key is missing" → 检查是否已将占位符替换为 **App settings** 中的真实密钥。 ::: ### 展示付费墙并处理购买 \{#show-paywalls-and-handle-purchases\} 通过版位 ID 获取付费墙、展示付费墙并处理购买事件。具体需要参考哪些指南,取决于你处理购买的方式。 每完成一步后,请在沙盒中测试购买流程,不要等到最后再测试。沙盒配置说明请参见[在沙盒中测试购买](test-purchases-in-sandbox)。 **指南:** - [使用付费墙启用购买(快速入门)](unity-quickstart-paywalls) - [获取付费墙编辑工具付费墙及其配置](unity-get-pb-paywalls) - [展示付费墙](unity-present-paywalls) - [处理付费墙事件](unity-handling-events) - [响应按钮操作](unity-handle-paywall-actions) 请将以下内容发送给您的 LLM: ``` 在编写代码之前,请先阅读以下 Adapty 文档: - https://adapty.io/docs/zh/unity-quickstart-paywalls.md - https://adapty.io/docs/zh/unity-get-pb-paywalls.md - https://adapty.io/docs/zh/unity-present-paywalls.md - https://adapty.io/docs/zh/unity-handling-events.md - https://adapty.io/docs/zh/unity-handle-paywall-actions.md ``` :::tip[检查点] - **预期效果:** 付费墙正常显示并包含已配置的产品。点击产品后触发沙盒购买弹窗。 - **注意事项:** 付费墙为空或出现 `GetPaywall` 错误 → 请确认版位 ID 与看板中完全一致,且该版位已分配目标受众。 ::: **指南:** - [在自定义付费墙中启用购买功能(快速入门)](unity-quickstart-manual) - [获取付费墙和产品](fetch-paywalls-and-products-unity) - [渲染通过远程配置设计的付费墙](present-remote-config-paywalls-unity) - [进行购买](unity-making-purchases) - [恢复购买](unity-restore-purchase) Read these Adapty docs before writing code: - https://adapty.io/docs/zh/unity-quickstart-manual.md - https://adapty.io/docs/zh/fetch-paywalls-and-products-unity.md - https://adapty.io/docs/zh/present-remote-config-paywalls-unity.md - https://adapty.io/docs/zh/unity-making-purchases.md - https://adapty.io/docs/zh/unity-restore-purchase.md :::tip[检查点] - **预期效果:** 自定义付费墙显示从 Adapty 获取的产品。点击产品后触发沙盒购买弹窗。 - **常见问题:** 产品数组为空 → 请确认付费墙已在看板中分配产品,且版位已设置目标受众。 ::: **相关指南:** - [Observer 模式概览](observer-vs-full-mode) - [实现 Observer 模式](implement-observer-mode-unity) - [在 Observer 模式中上报交易](report-transactions-observer-mode-unity) 将以下内容发送给你的 LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/zh/observer-vs-full-mode.md - https://adapty.io/docs/zh/implement-observer-mode-unity.md - https://adapty.io/docs/zh/report-transactions-observer-mode-unity.md ``` :::tip[检查点] - **预期结果:** 使用现有购买流程完成沙盒购买后,该交易会出现在 Adapty 看板的 **Event Feed** 中。 - **注意事项:** 没有事件 → 请确认你已向 Adapty 上报交易,并已为两个应用商店配置服务器通知。 ::: ### 检查订阅状态 \{#check-subscription-status\} 购买完成后,检查用户画像中是否存在有效的访问等级,以控制对高级内容的访问权限。 **指南:** [检查订阅状态](unity-check-subscription-status) 将以下内容发送给您的大语言模型: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/zh/unity-check-subscription-status.md ``` :::tip[检查点] - **预期结果:** 在沙盒购买完成后,`profile.AccessLevels["premium"]?.IsActive` 返回 `true`。 - **注意事项:** 购买后 `AccessLevels` 为空 → 请检查该产品是否已在看板中分配了访问等级。 ::: ### 识别用户 \{#identify-users\} 将应用的用户账号与 Adapty 用户画像关联,确保购买记录在多设备间同步。 :::important 如果你的应用无需登录,可跳过此步骤。 ::: **指南:**[识别用户](unity-quickstart-identify) 将以下内容发送给你的 LLM: ``` Read these Adapty docs before writing code: - https://adapty.io/docs/zh/unity-quickstart-identify.md ``` :::tip[Checkpoint] - **预期效果:** 调用 `Adapty.Identify("your-user-id")` 后,看板的 **Profiles** 部分会显示你的自定义用户 ID。 - **注意事项:** 请在激活之后、获取付费墙之前调用 `Identify`,以避免匿名用户画像归因问题。 ::: ### 准备发布 \{#prepare-for-release\} 沙盒中的集成测试通过后,请按照发布检查清单逐项确认,确保一切都已准备好上线。 **指南:** [发布检查清单](release-checklist) 将以下内容发送给你的 LLM: ``` Read these Adapty docs before releasing: - https://adapty.io/docs/zh/release-checklist.md ``` :::tip[Checkpoint] - **预期结果:** 所有检查项均已确认:商店连接、服务器通知、购买流程、访问等级检查以及隐私要求。 - **常见问题:** 缺少服务器通知 → 请在 **App settings → iOS SDK** 中配置 App Store 服务器通知,并在 **App settings → Android SDK** 中配置 Google Play 实时开发者通知。 ::: ## 纯文本文档索引文件 \{#plain-text-doc-index-files\} 如果你需要为 LLM 提供超出单个页面范围的更广泛上下文,我们提供了列出或汇总所有 Adapty 文档的索引文件: - [`llms.txt`](https://adapty.io/docs/zh/llms.txt):列出所有页面的 `.md` 链接。这是一项[新兴标准](https://llmstxt.org/),旨在让网站对 LLM 更易访问。请注意,对于某些 AI 助手(如 ChatGPT),你需要先下载 `llms.txt`,再将其作为文件上传到对话中。 - [`llms-full.txt`](https://adapty.io/docs/zh/llms-full.txt):将整个 Adapty 文档站合并为单个文件。体积较大——仅在需要完整内容时使用。 - Unity 专用的 [`unity-llms.txt`](https://adapty.io/docs/zh/unity-llms.txt) 和 [`unity-llms-full.txt`](https://adapty.io/docs/zh/unity-llms-full.txt):平台专属子集,相比完整站点可节省 token 消耗。 --- # File: unity-get-pb-paywalls --- --- title: "在 Unity SDK 中获取付费墙编辑工具付费墙及其配置" description: "了解如何在 Adapty 中检索付费墙编辑工具付费墙,以便更好地控制 Unity 应用中的订阅。" --- 在 [Adapty 看板中使用新版付费墙编辑工具完成付费墙的视觉设计](adapty-paywall-builder)后,您可以在移动应用中展示它。第一步是获取与版位关联的付费墙及其视图配置,具体步骤如下。 :::warning 新版付费墙编辑工具需要 Unity SDK 3.3.0 或更高版本。 ::: 请注意,本文介绍的是使用付费墙编辑工具自定义的付费墙。如果您是手动实现付费墙,请参阅[在移动应用中为远程配置付费墙获取付费墙和产品](fetch-paywalls-and-products-unity)。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 :::
在移动应用中展示付费墙之前(点击展开) 1. 在 Adapty 看板中[创建产品](create-product)。 2. 在 Adapty 看板中[创建付费墙并将产品添加到其中](create-paywall)。 3. 在 Adapty 看板中[创建版位并将付费墙添加到其中](create-placement)。 4. 在你的移动应用中安装 [Adapty SDK](sdk-installation-unity)。
## 获取使用付费墙编辑工具设计的付费墙 \{#fetch-paywall-designed-with-paywall-builder\} 如果你已经[使用付费墙编辑工具设计了付费墙](adapty-paywall-builder),则无需在移动应用代码中手动渲染并展示给用户。这类付费墙已包含展示内容和展示方式的完整配置。不过,你仍需通过版位获取其 ID 和视图配置,然后在移动应用中将其呈现出来。 为确保最佳性能,请尽早获取付费墙及其[视图配置](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder),以便在向用户展示之前留出足够时间完成图片下载。 使用 `GetPaywall` 方法获取付费墙: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` 参数: | 参数 | 是否必填 | 说明 | |---------|--------|-----------| | **placementId** | 必填 | 目标[版位](placements)的标识符。这是你在 Adapty 看板中创建版位时所指定的值。 | | **locale** |

可选

默认值:`en`

|

[付费墙本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符。该参数应为语言代码,由一个或两个子标签组成,中间用连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

有关语言代码及推荐使用方式,请参阅[本地化与语言代码](localizations-and-locale-codes)。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。

但如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在时直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。

请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清除时才会被清空。

Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加快付费墙的加载速度,并在 CDN 不可用时启用独立的备用服务器。该机制旨在确保你始终获取最新版本的付费墙,同时在网络连接受限的情况下也能保证可靠性。

| | **loadTimeout** | 默认值:5 秒 |

该值用于限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。

请注意,在少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为底层操作可能包含多个请求。

| 响应参数: | 参数 | 说明 | | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中启用了 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 获取付费墙后,检查其是否包含 `ViewConfiguration`——该字段表明该付费墙是通过付费墙编辑工具创建的,并将指引你如何展示该付费墙。如果存在 `ViewConfiguration`,则将其视为付费墙编辑工具付费墙;如果不存在,则[将其作为远程配置付费墙处理](present-remote-config-paywalls-unity)。 在 Unity SDK 中,直接调用 `CreatePaywallView` 方法,无需手动获取视图配置。 :::warning `CreatePaywallView` 方法的返回结果只能使用一次。如需再次使用,请重新调用 `CreatePaywallView` 方法。若不重新创建而直接调用两次,可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```csharp showLineNumbers var parameters = new AdaptyUICreatePaywallViewParameters() .SetPreloadProducts(preloadProducts) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` 参数: | 参数 | 是否必填 | 描述 | | :------------------ | :----------------- | :----------------------------------------------------------- | | **paywall** | 必填 | 一个 `AdaptyPaywall` 对象,用于获取目标付费墙的控制器。 | | **loadTimeout** | 默认值:5 秒 | 该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能包含多个请求。 | | **PreloadProducts** | 可选 | 提供一个 `AdaptyPaywallProducts` 数组,以优化产品在屏幕上的显示时机。若传入 `nil`,AdaptyUI 将自动获取所需产品。 | | **CustomTags** | 可选 | 定义一个自定义标签及其解析值的字典。自定义标签在付费墙内容中充当占位符,会被动态替换为特定字符串,从而在付费墙中实现个性化内容。详情请参阅付费墙编辑工具中的自定义标签相关说明。 | | **CustomTimers** | 可选 | 定义一个自定义计时器及其结束日期的字典。自定义计时器允许您在付费墙中展示倒计时。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder),以及如何正确使用语言区域代码([点击此处了解](localizations-and-locale-codes))。 ::: 获取视图后,[展示付费墙](unity-present-paywalls)。 ## 自定义资源 \{#customize-assets\} 要自定义付费墙中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你通过这些 ID 来定位并自定义相应元素的行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 为部分用户展示不同的图片或视频。 - 在远程主图加载时显示本地预览图。 - 在播放视频前先显示预览图。 :::important 要使用此功能,请将 Adapty Unity SDK 更新至 3.8.0 或更高版本。 ::: 以下是如何通过简单字典提供自定义资源的示例: ```csharp showLineNumbers var customAssets = new Dictionary { { "custom_image", AdaptyCustomAsset.LocalImageFile("custom_assets/images/custom_image.png") }, { "hero_video", AdaptyCustomAsset.LocalVideoFile("custom_assets/videos/custom_video.mp4") } }; var parameters = new AdaptyUICreatePaywallViewParameters() .SetCustomAssets(customAssets) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` :::note 如果找不到对应资源,付费墙将回退到默认外观。 ::: ## 设置开发者自定义计时器 \{#set-up-developer-defined-timers\} 要在 Unity 应用中使用自定义计时器,可以直接向 `SetCustomTimers` 方法传入一个包含计时器 ID 及其结束时间的字典。示例如下: ```csharp showLineNumbers var customTimers = new Dictionary { { "CUSTOM_TIMER_6H", DateTime.Now.AddHours(6) }, { "CUSTOM_TIMER_NY", new DateTime(2025, 1, 1) } }; var parameters = new AdaptyUICreatePaywallViewParameters() .SetCustomTimers(customTimers) .SetLoadTimeout(new TimeSpan(0, 0, 3)); AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { // handle the result }); ``` 在此示例中,`CUSTOM_TIMER_NY` 和 `CUSTOM_TIMER_6H` 是您在 Adapty 看板中设置的开发者自定义计时器的**计时器 ID**。计时器解析器确保您的应用为每个计时器动态更新正确的值。例如: - `CUSTOM_TIMER_NY`:距计时器结束时间(如元旦)的剩余时间。 - `CUSTOM_TIMER_6H`:从用户打开付费墙时开始的 6 小时倒计时的剩余时间。 ## 通过默认受众付费墙加速付费墙加载 \{#speed-up-paywall-fetching-with-default-audience-paywall\} 通常情况下,付费墙的加载几乎是即时完成的,无需担心速度问题。但如果你配置了大量目标受众和付费墙,且用户的网络连接较差,付费墙的加载时间可能会超出预期。在这种情况下,你可能希望先展示一个默认付费墙,以保证流畅的用户体验,而不是让用户看到空白页面。 要解决此问题,您可以使用 `GetPaywallForDefaultAudience` 方法,该方法会获取指定版位中**All Users**目标受众的付费墙。但请务必了解,推荐的方式是通过 `getPaywall` 方法获取付费墙,详情请参阅上方的[获取付费墙](#fetch-paywall)部分。 :::warning 建议使用 `GetPaywall` 而非 `GetPaywallForDefaultAudience`,因为后者存在以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要采用向后兼容的设计,否则旧版本可能显示异常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法根据国家、归因或自定义属性进行定向。 如果更快的获取速度对你的场景而言比这些缺点更重要,请按以下方式使用 `GetPaywallForDefaultAudience`。否则,请使用上文[介绍的](#fetch-paywall) `GetPaywall`。 ::: ```csharp showLineNumbers Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` 参数: | 参数 | 是否必填 | 说明 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 目标[版位](placements)的标识符。即你在 Adapty 看板中创建版位时指定的值。 | | **locale** |

可选

默认值:`en`

|

付费墙本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐使用此选项,因为它能确保用户始终获取最新数据。

但如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存数据存在时优先返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。

请注意,缓存在重启应用后仍会保留,只有在重新安装应用或手动清除时才会被清空。

Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及备用付费墙。我们还使用 CDN 加速付费墙的获取,并配备了独立的备用服务器以应对 CDN 不可用的情况。整套系统旨在确保你始终能获取最新版本的付费墙,同时在网络条件较差的情况下也能保证可靠性。

| --- # File: unity-present-paywalls --- --- title: "展示付费墙" description: "了解如何使用 Adapty SDK 在 Unity 应用中展示付费墙。" --- 如果你已经使用付费墙编辑工具自定义了付费墙,则无需在移动端代码中手动处理渲染逻辑来向用户展示它。这类付费墙已包含展示内容和展示方式的完整配置。 :::warning 本指南适用于**新版付费墙编辑工具**,需要 Adapty SDK 3.3.0 或更高版本。 如需展示远程配置付费墙,请参阅[渲染通过远程配置设计的付费墙](present-remote-config-paywalls)。 ::: 要展示付费墙,请对通过 [`CreatePaywallView`](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) 方法创建的 `view` 调用 `view.Present()` 方法。每个 `view` 只能使用一次。如需再次展示同一付费墙,请重新调用 `CreatePaywallView` 以创建新的 `view` 实例。 :::warning 复用同一个 `view` 而不重新创建,可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```csharp showLineNumbers title="Unity" view.Present((error) => { // handle the error }); ``` :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 显示对话框 \{#show-dialog\} 当付费墙视图在 Android 上展示时,请使用此方法代替原生弹窗。在 Android 上,普通弹窗会显示在付费墙视图后方,导致用户看不到。该方法可确保在所有平台上对话框正确显示于付费墙之上。 ```csharp showLineNumbers title="Unity" var dialog = new AdaptyUIDialogConfiguration() .SetTitle("Close paywall?") .SetContent("You will lose access to exclusive offers.") .SetDefaultActionTitle("Stay") .SetSecondaryActionTitle("Close"); AdaptyUI.ShowDialog(view, dialog, (action, error) => { if (error == null) { if (action == AdaptyUIDialogActionType.Secondary) { // User confirmed - close the paywall view.Dismiss(); } // If primary - do nothing, user stays } }); ``` ## 配置 iOS 展示样式 \{#configure-ios-presentation-style\} 通过向 `Present()` 方法传入 `iosPresentationStyle` 参数来配置付费墙在 iOS 上的展示方式。该参数接受 `AdaptyUIIOSPresentationStyle.FullScreen`(默认值)或 `AdaptyUIIOSPresentationStyle.PageSheet`。 ```csharp showLineNumbers title="Unity" view.Present(AdaptyUIIOSPresentationStyle.PageSheet, (error) => { // handle the error }); ``` --- # File: unity-handle-paywall-actions --- --- title: "在 Unity SDK 中响应按钮操作" description: "使用 Adapty 在 Unity 中处理付费墙按钮操作,提升应用变现效果。" --- 如果您正在使用 Adapty 付费墙编辑工具构建付费墙,正确设置按钮至关重要: 1. 在付费墙编辑工具中[添加按钮](paywall-buttons),并为其分配预设操作或创建自定义操作 ID。 2. 在您的应用代码中编写处理每个已分配操作的逻辑。 本指南介绍如何在代码中处理自定义操作和预设操作。 :::warning **只有购买和恢复操作会被自动处理。** 其他所有按钮操作(例如关闭付费墙或打开链接)都需要在应用代码中实现相应的响应逻辑。 ::: ## 关闭付费墙 \{#close-paywalls\} 要添加一个可关闭付费墙的按钮: 1. 在付费墙编辑工具中,添加一个按钮并为其分配 **Close** 操作。 2. 在您的应用代码中,为 `close` 操作实现一个处理程序,用于关闭付费墙。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Close: view.Dismiss(null); break; default: // handle other events break; } } ``` ## 从付费墙打开 URL \{#open-urls-from-paywalls\} :::tip 如果您想添加一组链接(例如使用条款和购买恢复),可以在付费墙编辑工具中添加 **Link** 元素,并以与带有 **Open URL** 操作的按钮相同的方式进行处理。 ::: 要添加一个从付费墙打开链接的按钮(例如**使用条款**或**隐私政策**): 1. 在付费墙编辑工具中,添加一个按钮,为其分配 **Open URL** 操作,并输入您想打开的 URL。 2. 在您的应用代码中,为 `openUrl` 操作实现一个处理程序,用于在浏览器中打开收到的 URL。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.OpenUrl: var urlString = action.Value; if(!string.IsNullOrWhiteSpace(urlString)) { Application.OpenURL(urlString); } break; default: // handle other events break; } } ``` ## 登录应用 \{#log-into-the-app\} 要添加一个让用户登录应用的按钮: 1. 在付费墙编辑工具中,添加一个按钮,并为其分配 ID 为 `login` 的 **Custom** 操作。 2. 在您的应用代码中,为 `login` 自定义操作实现一个处理程序,用于识别您的用户身份。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Custom: if (action.Value == "login") { // Navigate to login scene SceneManager.LoadScene("LoginScene"); } break; default: // handle other events break; } } ``` ## 处理自定义操作 \{#handle-custom-actions\} 要添加一个处理其他任意操作的按钮: 1. 在付费墙编辑工具中,添加一个按钮,为其分配 **Custom** 操作,并设置一个 ID。 2. 在您的应用代码中,为您创建的操作 ID 实现相应的处理程序。 例如,如果您有另一套订阅优惠或一次性购买,可以添加一个按钮来显示另一个付费墙: ```csharp showLineNumbers title="Unity" public void PaywallViewDidPerformAction( AdaptyUIPaywallView view, AdaptyUIUserAction action ) { switch (action.Type) { case AdaptyUIUserActionType.Custom: if (action.Value == "openNewPaywall") { // Display another paywall ShowAlternativePaywall(); } break; default: // handle other events break; } } private void ShowAlternativePaywall() { // Implement your logic to show alternative paywall } ``` --- # File: unity-handling-events --- --- title: "处理付费墙事件" description: "了解如何使用 Adapty SDK 在 Unity 应用中处理付费墙事件。" --- :::important 本指南涵盖购买、恢复、产品选择以及付费墙渲染的事件处理。你还必须实现按钮处理(关闭付费墙、打开链接等)。详情请参阅[处理按钮操作指南](unity-handle-paywall-actions)。 ::: 使用[付费墙编辑工具](adapty-paywall-builder)配置的付费墙无需额外代码即可完成购买和恢复操作。但它们会触发一些事件供应用响应,包括按钮点击(关闭按钮、URL、产品选择等)以及付费墙上购买相关操作的通知。以下介绍如何响应这些事件。 :::warning 本指南仅适用于**新版付费墙编辑工具付费墙**,需要 Adapty SDK v3.3.0 或更高版本。 ::: :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 处理事件 \{#handling-events\} 要控制或监控应用付费墙界面上发生的流程,请实现 `AdaptyPaywallsEventsListener` 接口: ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; public class PaywallEventsHandler : MonoBehaviour, AdaptyPaywallsEventsListener { void Start() { Adapty.SetPaywallsEventsListener(this); } // Implement all required interface methods below } ``` ### 用户触发的事件 \{#user-generated-events\} #### 付费墙已显示 \{#paywall-appeared\} 当付费墙视图呈现到屏幕上时触发。 :::note 在 iOS 上,当用户点击付费墙内的[网页付费墙按钮](web-paywall#step-2a-add-a-web-purchase-button)并在应用内浏览器中打开网页付费墙时,也会触发此事件。 ::: ```csharp showLineNumbers title="Unity" public void PaywallViewDidAppear(AdaptyUIPaywallView view) { } ``` #### 付费墙已消失 \{#paywall-disappeared\} 当付费墙视图从屏幕上关闭时触发。 :::note 在 iOS 上,当从付费墙打开的[网页付费墙](web-paywall#step-2a-add-a-web-purchase-button)在应用内浏览器中消失时,也会触发此事件。 ::: ```csharp showLineNumbers title="Unity" public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { } ``` #### 产品选择 \{#product-selection\} 当用户或系统选择要购买的产品时触发。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidSelectProduct( AdaptyUIPaywallView view, string productId ) { } ```
事件示例(点击展开) ```javascript { "productId": "premium_monthly" } ```
#### 开始购买 \{#started-purchase\} 当用户发起购买流程时触发。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidStartPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product ) { } ```
事件示例(点击展开) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" } } ```
#### 购买成功、已取消或待处理 \{#successful-canceled-or-pending-purchase\} 如果购买成功、用户取消购买,或购买处于待处理状态,此方法将被调用。用户取消和待处理付款(例如需要家长批准)会触发此方法,而不是 `PaywallViewDidFailPurchase`。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyPurchaseResult purchasedResult ) { } ```
事件示例(点击展开) ```javascript // Successful purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Success", "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } } } } } // Cancelled purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "UserCancelled" } } // Pending purchase { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "purchaseResult": { "type": "Pending" } } ```
在这种情况下,我们建议关闭该界面。 #### 购买失败 \{#failed-purchase\} 如果购买因错误而失败,此方法将被调用。这包括 StoreKit/Google Play Billing 错误(支付限制、无效产品、网络故障)、交易验证失败以及系统错误。请注意,用户取消会触发 `PaywallViewDidFinishPurchase` 并返回已取消结果,待处理付款不会触发此方法。 ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailPurchase( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error ) { } ```
事件示例(点击展开) ```javascript { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "purchase_failed", "message": "Purchase failed due to insufficient funds", "details": { "underlyingError": "Insufficient funds in account" } } } ```
#### 开始恢复 \{#started-restore\} 当用户发起恢复流程时触发: ```csharp showLineNumbers title="Unity" public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { } ``` #### 恢复成功 \{#successful-restore\} 当购买恢复成功时触发: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishRestore( AdaptyUIPaywallView view, AdaptyProfile profile ) { } ```
事件示例(点击展开) ```javascript { "profile": { "accessLevels": { "premium": { "id": "premium", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } }, "subscriptions": [ { "vendorProductId": "premium_monthly", "isActive": true, "expiresAt": "2024-02-15T10:30:00Z" } ] } } ```
如果用户已拥有所需的 `accessLevel`,我们建议关闭该界面。请参阅[订阅状态](unity-listen-subscription-changes)了解如何检查。 #### 恢复失败 \{#failed-restore\} 当购买恢复失败时触发: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailRestore( AdaptyUIPaywallView view, AdaptyError error ) { } ```
事件示例(点击展开) ```javascript { "error": { "code": "restore_failed", "message": "Purchase restoration failed", "details": { "underlyingError": "No previous purchases found" } } } ```
#### 网页支付导航完成 \{#finished-web-payment-navigation\} 尝试打开[网页付费墙](web-paywall)进行购买后(无论成功还是失败),此方法将被调用: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFinishWebPaymentNavigation( AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error ) { } ``` **参数:** - `product`:已打开(或尝试打开)网页付费墙的产品 - `error`:网页付费墙成功打开时为 `null`,失败时为 `AdaptyError`
事件示例(点击展开) ```javascript // Successful navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": null } // Failed navigation { "product": { "vendorProductId": "premium_monthly", "localizedTitle": "Premium Monthly", "localizedDescription": "Premium subscription for 1 month", "localizedPrice": "$9.99", "price": 9.99, "currencyCode": "USD" }, "error": { "code": "wrong_param", "message": "Current method is not available for this product", "details": { "underlyingError": "Product not configured for web purchases" } } } ```
### 数据获取与渲染 \{#data-fetching-and-rendering\} #### 产品加载错误 \{#product-loading-errors\} 当产品加载失败时触发,并提供 `AdaptyError`。如果你在初始化时未传入产品数组,AdaptyUI 会自行从服务器获取所需对象。该操作可能失败,AdaptyUI 将通过调用此方法报告错误: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailLoadingProducts( AdaptyUIPaywallView view, AdaptyError error ) { } ```
事件示例(点击展开) ```javascript { "error": { "code": "products_loading_failed", "message": "Failed to load products from the server", "details": { "underlyingError": "Network timeout" } } } ```
#### 渲染错误 \{#rendering-errors\} 当界面渲染过程中发生错误时触发,并提供 `AdaptyError`: ```csharp showLineNumbers title="Unity" public void PaywallViewDidFailRendering( AdaptyUIPaywallView view, AdaptyError error ) { } ```
事件示例(点击展开) ```javascript { "error": { "code": "rendering_failed", "message": "Failed to render paywall interface", "details": { "underlyingError": "Invalid paywall configuration" } } } ```
正常情况下不应出现此类错误,如果你遇到了,请告知我们。 --- # File: unity-web-paywalls --- --- title: "在 Unity SDK 中实现网页付费墙" description: "设置网页付费墙,无需支付 App Store 费用和审核即可收款。" --- :::important 开始之前,请确保您已[在看板中配置了网页付费墙](web-paywall),并已安装 Adapty SDK 3.14 或更高版本。 ::: ## 打开网页付费墙 \{#open-web-paywalls\} 如果你使用的是自行开发的付费墙,则需要通过 SDK 方法来处理网页付费墙。`Adapty.OpenWebPaywall` 方法会执行以下操作: 1. 生成一个唯一 URL,使 Adapty 能够将向特定用户展示的付费墙与其跳转到的网页关联起来。 2. 追踪用户返回应用的时机,然后以短间隔轮询 `Adapty.GetProfile`,以判断用户画像的访问权限是否已更新。 这样,一旦支付成功并更新了访问权限,订阅几乎会立即在应用中激活。 ```csharp showLineNumbers title="Unity" Adapty.OpenWebPaywall( product, (error) => { if (error != null) { Debug.LogError($"Failed to open web paywall: {error.Message}"); } else { Debug.Log("Web paywall opened successfully"); } } ); ``` :::note `OpenWebPaywall` 方法有两个版本: 1. `OpenWebPaywall(product)` — 根据付费墙生成 URL,并将产品数据附加到 URL 中。 2. `OpenWebPaywall(paywall)` — 根据付费墙生成 URL,但不附加产品数据。当 Adapty 付费墙中的产品与 Web 付费墙中的产品不同时,请使用此版本。 ::: #### 错误处理 \{#handle-errors\} | 错误代码 | 描述 | 建议操作 | |-----------|--------------------------------------------------------|---------------------------------------------------------------------------| | `AdaptyErrorCode.WrongParam` | 付费墙或产品未配置网页购买 URL,或在浏览器中打开 URL 失败 | 查看错误信息了解详情。在 Adapty 看板中检查付费墙/产品配置,或检查设备设置。 | | `AdaptyErrorCode.DecodingFailed` | 无法正确编码 URL 中的参数 | 验证 URL 参数是否有效且格式正确 | :::note 查看错误的 `Message` 属性,以获取具体的错误详情。`WrongParam` 可能对应多种问题(缺少购买 URL、无法打开浏览器等)。 ::: ## 在应用内浏览器中打开网页付费墙 \{#open-web-paywalls-in-an-in-app-browser\} :::important 从 Adapty SDK v3.15 起,支持在应用内浏览器中打开网页付费墙。 ::: 默认情况下,网页付费墙会在外部浏览器中打开,这会将用户引导至应用之外。 为了提供流畅的用户体验,你可以改为在应用内浏览器中打开网页付费墙。这样一来,网页购买页面会直接在你的应用内展示,用户无需切换应用即可完成交易。 要启用此功能,请将 `AdaptyWebPresentation.InAppBrowser` 传入 `OpenWebPaywall` 方法: ```csharp showLineNumbers title="Unity" Adapty.OpenWebPaywall( product, AdaptyWebPresentation.InAppBrowser, // default — ExternalBrowser (error) => { if (error != null) { Debug.LogError($"Failed to open web paywall: {error.Message}"); } else { Debug.Log("Web paywall opened successfully"); } } ); ``` --- # File: unity-use-fallback-paywalls --- --- title: "Unity - 使用备用付费墙" description: "处理用户离线或 Adapty 服务器不可用的情况" --- :::warning 备用付费墙需要 Unity SDK v2.11 及更高版本支持。 ::: 为了保持流畅的用户体验,请务必为您的流程、[付费墙](paywalls)和[用户引导](onboardings)设置[备用方案](/fallback-paywalls)。这一预防措施可以在网络部分或完全中断时,确保应用仍能正常运行。 * **若应用无法访问 Adapty 服务器:** 应用可以显示备用流程或付费墙,并读取本地的用户引导配置。 * **若应用无法访问互联网:** 应用可以显示备用流程或付费墙。用户引导包含远程内容,需要联网才能正常使用。 :::important 在按照本指南操作之前,请先从 Adapty [下载](/local-fallback-paywalls)备用配置文件。 ::: ## 配置 \{#configuration\} 1. 将备用配置文件添加到项目中的公共目录 `Assets/StreamingAssets`。 2. 在获取目标付费墙或用户引导**之前**调用 `.setFallback` 方法。 ```csharp using UnityEngine; using AdaptySDK; #if UNITY_IOS string fileName = "ios_fallback.json"; #elif UNITY_ANDROID string fileName = "android_fallback.json"; #else // Optional: handle Editor or other platforms string fileName = "fallback.json"; #endif Adapty.SetFallback(fileName, (error) => { if (error != null) { Debug.LogError($"Failed to set fallback: {error}"); return; } // Fallback set successfully }); ``` 参数: | 参数 | 描述 | |:-------------|:-----------------------------------------------------| | **fileName** | 包含备用配置文件名称的字符串。 | --- # File: unity-localizations-and-locale-codes --- --- title: "在 Unity SDK 中使用本地化和语言代码" description: "了解如何使用 Adapty SDK 对 Unity 应用中的付费墙进行本地化。" --- ## 为什么这很重要 \{#why-this-is-important\} 在某些场景下,语言区域代码会发挥关键作用——例如,当你需要根据应用当前的本地化设置获取对应的付费墙时。 由于语言区域代码较为复杂,且在不同平台之间可能存在差异,我们对所有支持的平台统一使用一套内部标准。正因为这些代码比较复杂,了解你实际发送给服务器的内容、以及后续的处理逻辑就显得尤为重要——这样你才能始终获取到预期的本地化结果。 ## Adapty 的语言代码标准 \{#locale-code-standard-at-adapty\} 在语言代码方面,Adapty 采用了经过少量调整的 [BCP 47 标准](https://en.wikipedia.org/wiki/IETF_language_tag):每个代码由小写子标签组成,以连字符分隔。例如:`en`(英语)、`pt-br`(葡萄牙语(巴西))、`zh`(简体中文)、`zh-hant`(繁体中文)。 ## 语言区域代码匹配 \{#locale-code-matching\} 当 Adapty 收到客户端 SDK 传入的语言区域代码并开始查找对应的付费墙本地化版本时,流程如下: 1. 传入的语言区域字符串转换为小写,所有下划线(`_`)替换为连字符(`-`) 2. 查找与完整语言区域代码完全匹配的本地化版本 3. 如果未找到匹配项,则取第一个连字符前的子字符串(例如 `pt-br` 取 `pt`),再次查找匹配的本地化版本 4. 如果仍未找到匹配项,则返回默认的 `en` 本地化版本 这样,发送 `'pt_BR'` 的 iOS 设备、发送 `pt-BR` 的 Android 设备以及发送 `pt-br` 的其他设备,都会得到相同的结果。 ## 实现本地化:推荐方式 \{#implementing-localizations-recommended-way\} 如果你正在考虑本地化问题,很可能项目中已经有了本地化字符串文件。在这种情况下,我们建议在每个本地化文件中加入一个键值对,其值对应 Adapty 的语言区域代码,然后在调用 SDK 时提取该键的值,示例如下: ```csharp showLineNumbers // 1. Modify your localization files (e.g., using Unity's Localization package) /* en.json */ { "adapty_paywalls_locale": "en" } /* es.json */ { "adapty_paywalls_locale": "es" } /* pt-BR.json */ { "adapty_paywalls_locale": "pt-br" } // 2. Extract and use the locale code using UnityEngine; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using AdaptySDK; public class PaywallManager : MonoBehaviour { public async void FetchPaywall() { // Get the current locale from Unity's Localization system var locale = LocalizationSettings.SelectedLocale; var localeCode = GetAdaptyLocaleCode(locale); // Pass locale code to Adapty.GetPaywall or Adapty.GetPaywallForDefaultAudience method Adapty.GetPaywall("placement_id", localeCode, (paywall, error) => { if (error != null) { // handle the error return; } // Use the paywall }); } private string GetAdaptyLocaleCode(Locale locale) { // Convert Unity locale to Adapty format var localeIdentifier = locale.Identifier.Code; return localeIdentifier.ToLower().Replace('_', '-'); } } ``` 这样,您就能完全掌控应用中每位用户所获取的本地化内容。 ## 另一种本地化实现方式 \{#implementing-localizations-the-other-way\} 你也可以不为每个本地化版本明确定义语言区域代码,从而实现类似(但并不完全相同)的效果。这意味着需要从平台提供的其他对象中提取语言区域代码,例如: ```csharp showLineNumbers using UnityEngine; using System.Globalization; using AdaptySDK; public class PaywallManager : MonoBehaviour { public void FetchPaywall() { var localeCode = GetSystemLocaleCode(); // Pass locale code to Adapty.GetPaywall or Adapty.GetPaywallForDefaultAudience method Adapty.GetPaywall("placement_id", localeCode, (paywall, error) => { if (error != null) { // handle the error return; } // Use the paywall }); } private string GetSystemLocaleCode() { // Get the system's current culture var culture = CultureInfo.CurrentCulture; var languageCode = culture.TwoLetterISOLanguageName; var regionCode = culture.Name.Contains('-') ? culture.Name.Split('-')[1] : null; if (!string.IsNullOrEmpty(regionCode)) { return $"{languageCode}-{regionCode.ToLower()}"; } return languageCode; } } ``` 请注意,我们不建议使用此方法,原因如下: 1. 在 iOS 上,首选语言与当前语言区域并不相同。如果想让本地化正确生效,要么依赖 Apple 的内置逻辑(使用推荐的本地化字符串文件方案时开箱即用),要么自行重新实现该逻辑。 2. 很难预测 Adapty 服务器实际会收到什么内容。例如,在 iOS 上,设备可能上报类似 `ar_OM@numbers='latn'` 这样的语言区域标识,并将其发送到我们的服务器。而服务器收到后,返回的不会是你期望的 `ar-om` 本地化内容,而是 `ar`,这往往会让人感到意外。 如果你仍决定采用这种方式,请确保已覆盖所有相关的使用场景。 --- # File: unity-troubleshoot-paywall-builder --- --- title: "排查 Unity SDK 中的付费墙编辑工具问题" description: "排查 Unity SDK 中的付费墙编辑工具问题" --- 本指南帮助您解决在 Unity SDK 中使用 Adapty 付费墙编辑工具设计付费墙时遇到的常见问题。 ## 获取付费墙配置失败 \{#getting-a-paywall-configuration-fails\} **问题**:`CreateView` 方法无法获取付费墙配置。 **原因**:付费墙未在付费墙编辑工具中启用设备显示。 **解决方案**:在付费墙编辑工具中启用 **Show on device** 开关。 ## 付费墙视图数量过大 \{#the-paywall-view-number-is-too-big\} **问题**:付费墙视图计数显示的数量是预期数量的两倍。 **原因**:您可能在代码中调用了 `LogShowPaywall`,如果您正在使用付费墙编辑工具,这会导致视图计数重复。对于使用付费墙编辑工具设计的付费墙,分析数据会自动追踪,因此您无需使用此方法。 **解决方案**:如果您正在使用付费墙编辑工具,请确保代码中未调用 `LogShowPaywall`。 ## 其他问题 \{#other-issues\} **问题**:您遇到了上述未涵盖的其他付费墙编辑工具相关问题。 **解决方案**:如有需要,请参照[迁移指南](unity-sdk-migration-guides)将 SDK 升级至最新版本。许多问题已在较新版本的 SDK 中得到解决。 --- # File: unity-quickstart-manual --- --- title: "在 Unity SDK 的自定义付费墙中启用购买功能" description: "将 Adapty SDK 集成到您的自定义 Unity 付费墙中,以启用应用内购买。" --- 本指南介绍如何将 Adapty 集成到您的自定义付费墙中。您可以完全掌控付费墙的实现,同时由 Adapty SDK 负责获取产品、处理新购买以及恢复历史购买。 :::important **本指南面向正在实现自定义付费墙的开发者。** 如果您希望以最简便的方式启用购买功能,请使用 [付费墙编辑工具](unity-quickstart-paywalls)。使用付费墙编辑工具,您可以在无代码可视化编辑器中创建付费墙,Adapty 会自动处理所有购买逻辑,您无需重新发布应用即可测试不同的设计方案。 ::: ## 开始之前 \{#before-you-start\} ### 设置产品 \{#set-up-products\} 要启用应用内购买,您需要了解三个核心概念: - [**产品**](product) – 用户可以购买的任何内容(订阅、消耗型商品、永久授权) - [**付费墙**](paywalls) – 定义要展示哪些产品的配置。在 Adapty 中,付费墙是获取产品的唯一途径,但这种设计使您无需修改应用代码即可调整产品、价格和优惠。 - [**版位**](placements) – 在应用中展示付费墙的位置和时机(例如 `main`、`onboarding`、`settings`)。您在看板中为版位设置付费墙,然后在代码中通过版位 ID 请求它们。这使得运行 A/B 测试以及向不同用户展示不同付费墙变得轻而易举。 即使您使用自定义付费墙,也请确保理解这些概念。它们本质上只是您管理应用内销售产品的方式。 要实现自定义付费墙,您需要创建一个**付费墙**并将其添加到**版位**中。此设置使您能够获取产品。如需了解在看板中需要执行哪些操作,请参阅[此处](quickstart)的快速入门指南。 ### 管理用户 \{#manage-users\} 您可以选择使用或不使用后端身份验证。 但请注意,Adapty SDK 对匿名用户和已识别用户的处理方式有所不同。请阅读[用户识别快速入门指南](unity-quickstart-identify),了解具体差异并确保您正确地管理用户。 ## 第一步:获取产品 \{#step-1-get-products\} 要获取自定义付费墙的产品,您需要: 1. 通过将[版位](placements) ID 传递给 `getPaywall` 方法来获取 `paywall` 对象。 2. 使用 `getPaywallProducts` 方法获取该付费墙的产品数组。 ```csharp showLineNumbers using AdaptySDK; void LoadPaywall() { Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if (error != null) { // Handle the error return; } Adapty.GetPaywallProducts(paywall, (products, productsError) => { if (productsError != null) { // Handle the error return; } // Use products to build your custom paywall UI }); }); } ``` ## 第二步:接受购买 \{#step-2-accept-purchases\} 当用户在自定义付费墙中点击某个产品时,调用 `makePurchase` 方法并传入所选产品。该方法将处理购买流程并返回更新后的用户画像。 ```csharp showLineNumbers using AdaptySDK; void PurchaseProduct(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { if (error != null) { // Handle the error return; } switch (result.Type) { case AdaptyPurchaseResultType.Success: var profile = result.Profile; // Purchase successful, profile updated break; case AdaptyPurchaseResultType.UserCancelled: // User canceled the purchase break; case AdaptyPurchaseResultType.Pending: // Purchase is pending (e.g., user will pay offline with cash) break; } }); } ``` ## 第三步:恢复购买 \{#step-3-restore-purchases\} 应用商店要求所有包含订阅的应用为用户提供恢复购买的途径。 当用户点击恢复按钮时,调用 `restorePurchases` 方法。该方法将把用户的购买历史与 Adapty 同步,并返回更新后的用户画像。 ```csharp showLineNumbers using AdaptySDK; void RestorePurchases() { Adapty.RestorePurchases((profile, error) => { if (error != null) { // Handle the error return; } // Restore successful, profile updated }); } ``` ## 后续步骤 \{#next-steps\} :::tip 有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助! ::: 您的付费墙已准备好在应用中展示。请在 [App Store 沙盒](test-purchases-in-sandbox)或 [Google Play Store](testing-on-android) 中测试您的购买流程,以确保能够从付费墙完成测试购买。 接下来,[检查用户是否已完成购买](unity-check-subscription-status),以决定是否展示付费墙或授予付费功能的访问权限。 --- # File: fetch-paywalls-and-products-unity --- --- title: "在 Unity SDK 中获取远程配置付费墙的付费墙和产品" description: "在 Adapty Unity SDK 中获取付费墙和产品,以提升用户变现效果。" --- 在展示远程配置和自定义付费墙之前,您需要先获取相关信息。请注意,本主题涉及远程配置和自定义付费墙。如需获取付费墙编辑工具自定义付费墙的指导,请参阅[获取付费墙编辑工具付费墙及其配置](unity-get-pb-paywalls)。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 :::
在移动应用中开始获取付费墙和产品之前(点击展开) 1. 在 Adapty 看板中[创建您的产品](create-product)。 2. 在 Adapty 看板中[创建付费墙并将产品添加到付费墙中](create-paywall)。 3. 在 Adapty 看板中[创建版位并将付费墙添加到版位中](create-placement)。 4. 在您的移动应用中[安装 Adapty SDK](sdk-installation-unity)。
## 获取付费墙信息 \{#fetch-paywall-information\} 在 Adapty 中,[产品](product)是 App Store 和 Google Play 产品的组合。这些跨平台产品被集成到付费墙中,使您能够在特定的移动应用版位中展示它们。 要展示产品,您需要使用 `getPaywall` 方法从某个[版位](placements)中获取[付费墙](paywalls)。 :::important **不要硬编码产品 ID。** 您唯一应该硬编码的是版位 ID。付费墙是远程配置的,因此产品数量和可用优惠随时可能发生变化。您的应用必须动态处理这些变化——如果今天付费墙返回两个产品,明天返回三个,则应显示所有产品而无需修改代码。 ::: ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` | 参数 | 是否必需 | 描述 | |---------|--------|-----------| | **placementId** | 必需 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |

可选

默认值:`en`

|

[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

有关语言环境代码及推荐使用方式的更多信息,请参阅[本地化与语言环境代码](unity-localizations-and-locale-codes)。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 将尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此方式,因为它可确保用户始终获取最新数据。

但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时返回缓存数据。在这种情况下,用户可能无法获取绝对最新的数据,但无论网络状况如何,他们都会获得更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。

请注意,缓存在应用重启后仍然有效,只有在应用卸载重装或手动清理时才会被清除。

Adapty SDK 将付费墙存储在两个层级中:上述定期更新的缓存和[备用付费墙](unity-use-fallback-paywalls)。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可用时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的付费墙,同时即使在网络连接稀缺的情况下也能保证可靠性。

| | **loadTimeout** | 默认值:5 秒 |

该值限制此方法的超时时间。若达到超时时间,将返回缓存数据或本地备用数据。

请注意,在极少数情况下,此方法的超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能包含多个不同的请求。

| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品、产品数量以及特殊优惠(如免费试用)可能随时发生变化。请确保您的代码能够处理这些情况。 例如,如果您最初获取到 2 个产品,您的应用应显示这 2 个产品。但如果您后来获取到 3 个产品,您的应用应显示所有 3 个产品,而无需修改任何代码。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他多个属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,您可以查询与之对应的产品数组: ```csharp showLineNumbers Adapty.GetPaywallProducts(paywall, (products, error) => { if(error != null) { // handle the error return; } // products - the requested products array }); ``` 响应参数: | 参数 | 描述 | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他多个属性。 | 在实现自定义付费墙设计时,您可能需要访问 [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html) 对象中的这些属性。以下列出了最常用的属性,但请参阅链接文档以获取所有可用属性的完整详情。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 要显示产品标题,请使用 `product.LocalizedTitle`。请注意,本地化基于用户在商店所选的国家/地区,而非设备本身的语言环境。 | | **Price** | 要显示本地化的价格,请使用 `product.Price.LocalizedString`。此本地化基于设备的语言环境信息。您也可以通过 `product.Price.Amount` 以数字形式访问价格,该值将以本地货币提供。要获取对应的货币符号,请使用 `product.Price.CurrencySymbol`。 | | **Subscription Period** | 要显示订阅周期(如周、月、年等),请使用 `product.Subscription?.LocalizedPeriod`。此本地化基于设备语言环境。要以编程方式获取订阅周期,请使用 `product.Subscription?.Period`。从中您可以访问 `Unit` 枚举以获取时长(即 `AdaptySubscriptionPeriodUnit.Day`、`AdaptySubscriptionPeriodUnit.Week`、`AdaptySubscriptionPeriodUnit.Month`、`AdaptySubscriptionPeriodUnit.Year` 或 `AdaptySubscriptionPeriodUnit.Unknown`)。`NumberOfUnits` 值将为您提供周期单位的数量。例如,对于季度订阅,Unit 属性中显示 `AdaptySubscriptionPeriodUnit.Month`,NumberOfUnits 属性中显示 `3`。 | | **Introductory Offer** | 要显示徽章或其他指示符以表明订阅包含新用户优惠,请查看 `product.Subscription?.Offer?.Phases` 属性。这是一个最多包含两个折扣阶段的列表:免费试用阶段和新用户优惠价格阶段。每个阶段对象包含以下有用属性:
• `PaymentMode`:枚举值,包括 `AdaptyPaymentMode.FreeTrial`、`AdaptyPaymentMode.PayAsYouGo`、`AdaptyPaymentMode.PayUpFront` 和 `AdaptyPaymentMode.Unknown`。免费试用为 `AdaptyPaymentMode.FreeTrial` 类型。
• `Price`:折扣价格(数字形式)。对于免费试用,此处值为 `0`。
• `LocalizedNumberOfPeriods`:使用设备语言环境本地化的字符串,描述优惠的时长。例如,三天试用优惠在此字段显示为 `"3 days"`。
• `SubscriptionPeriod`:或者,您可以通过此属性获取优惠周期的具体详情,其使用方式与前一节描述的相同。
• `LocalizedSubscriptionPeriod`:针对用户语言环境格式化的折扣订阅周期。 | ## 使用默认目标受众付费墙加速付费墙获取 \{#speed-up-paywall-fetching-with-default-audience-paywall\} 通常情况下,付费墙几乎可以即时获取,因此您无需担心加速此过程。但是,如果您拥有大量目标受众和付费墙,且用户的网络连接较弱,获取付费墙可能需要比预期更长的时间。在这种情况下,您可能希望显示默认付费墙,以确保流畅的用户体验,而不是完全不显示付费墙。 为解决这一问题,您可以使用 `GetPaywallForDefaultAudience` 方法,该方法为**所有用户**目标受众获取指定版位的付费墙。但是,请务必了解,推荐的方式是通过 `getPaywall` 方法获取付费墙,详见上方[获取付费墙](#fetch-paywall)部分。 :::warning 请考虑使用 `GetPaywall` 而非 `GetPaywallForDefaultAudience`,因为后者有以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要向后兼容的设计,或接受旧版本可能显示不正确的情况。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法根据国家/地区、归因或自定义属性进行定向。 如果对于您的使用场景,更快的获取速度超过了这些缺点,请按以下方式使用 `GetPaywallForDefaultAudience`。否则,请按[上述](#fetch-paywall)描述使用 `GetPaywall`。 ::: ```csharp showLineNumbers Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { if(error != null) { // handle the error return; } // paywall - the resulting object }); ``` 参数: | 参数 | 是否必需 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必需 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |

可选

默认值:`en`

|

付费墙本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 将尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它可确保用户始终获取最新数据。

但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时返回缓存数据。在这种情况下,用户可能无法获取绝对最新的数据,但无论网络状况如何,他们都会获得更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。

请注意,缓存在应用重启后仍然有效,只有在应用卸载重装或手动清理时才会被清除。

Adapty SDK 在本地将付费墙存储在两个层级中:上述定期更新的缓存和备用付费墙。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可用时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的付费墙,同时即使在网络连接稀缺的情况下也能保证可靠性。

| --- # File: present-remote-config-paywalls-unity --- --- title: "在 Unity SDK 中渲染通过远程配置设计的付费墙" description: "了解如何在 Adapty Unity SDK 中展示远程配置付费墙,以个性化用户体验。" --- 如果您使用远程配置自定义了付费墙,则需要在移动应用代码中实现渲染逻辑,以便向用户展示它。由于远程配置提供了高度灵活性,您可以完全掌控付费墙视图中包含的内容及其显示方式。我们提供了获取远程配置的方法,让您能够自主展示通过远程配置配置的自定义付费墙。 ## 获取付费墙远程配置并展示 \{#get-paywall-remote-config-and-present-it\} 要获取付费墙的远程配置,请访问 `remoteConfig` 属性并提取所需的值。 ```csharp showLineNumbers Adapty.GetPaywall("YOUR_PLACEMENT_ID", (paywall, error) => { if (error != null) { // handle the error return; } // Access remote config dictionary var dictionary = paywall.RemoteConfig?.Dictionary; var headerText = dictionary?["header_text"] as string; // Or access raw JSON data var jsonData = paywall.RemoteConfig?.Data; }); ``` 此时,一旦您获取到所有必要的值,就可以将它们渲染并组合成一个美观的页面。请确保设计能够适配各种手机屏幕尺寸和方向,为不同设备上的用户提供流畅且友好的体验。 :::warning 请务必按照下文所述[记录付费墙浏览事件](present-remote-config-paywalls-unity#track-paywall-view-events),以便 Adapty 分析系统能够为漏斗和 A/B 测试采集相关数据。 ::: 展示付费墙完成后,请继续设置购买流程。当用户发起购买时,只需使用付费墙中的产品调用 `.MakePurchase()`。有关 `.MakePurchase()` 方法的详细信息,请参阅[发起购买](unity-making-purchases)。 我们建议[创建一个备用付费墙作为备份](unity-use-fallback-paywalls)。当用户没有网络连接或缓存不可用时,将向其展示此备用付费墙,确保在这些情况下也能提供流畅的体验。 ## 追踪付费墙浏览事件 \{#track-paywall-view-events\} Adapty 可帮助您衡量付费墙的表现。虽然我们会自动收集购买数据,但付费墙浏览记录需要您手动上报,因为只有您才知道用户何时看到了付费墙。 要记录付费墙浏览事件,只需调用 `.LogShowPaywall(paywall)`,该事件将反映在漏斗和 A/B 测试的付费墙数据图表中。 :::important 如果您展示的是通过[付费墙编辑工具](adapty-paywall-builder)创建的付费墙,则无需调用 `.LogShowPaywall(paywall)`。 ::: ```csharp showLineNumbers Adapty.LogShowPaywall(paywall, (error) => { // handle the error }); ``` 请求参数: | 参数 | 是否必填 | 描述 | | :---------- | :------- |:------------------------------------------------------------------| | **paywall** | 必填 | 一个 [`AdaptyPaywall`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象。 | --- # File: unity-making-purchases --- --- title: "在 Unity SDK 的移动应用中进行购买" description: "使用 Adapty 处理应用内购买和订阅的指南。" --- 在您的移动应用中展示付费墙是向用户提供高级内容或服务访问权限的重要步骤。但是,仅仅展示付费墙只有在您使用[付费墙编辑工具](adapty-paywall-builder)自定义付费墙时,才足以支持购买。 如果您没有使用付费墙编辑工具,则必须使用一个单独的方法 `.makePurchase()` 来完成购买并解锁所需内容。该方法是用户与付费墙交互并完成所需交易的入口。 如果你的付费墙为用户正在购买的产品设置了有效的促销活动,Adapty 会在购买时自动应用该优惠。 :::warning 请注意,只有使用付费墙编辑工具搭建的付费墙,新用户优惠才会自动应用。 在其他情况下,您需要[验证用户在 iOS 上是否符合新用户优惠的条件](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios)。跳过此步骤可能导致应用在发布审核时被拒绝,还可能向本应享受新用户优惠的用户收取全价。 ::: 请确保您已[完成初始配置](quickstart),且没有跳过任何步骤。否则,我们将无法验证购买。 ## 进行购买 \{#make-purchase\} :::note **使用[付费墙编辑工具](adapty-paywall-builder)?** 购买会自动处理——可以跳过此步骤。 **需要分步指引?** 请查看[快速入门指南](unity-implement-paywalls-manually),其中包含完整的端到端实现说明。 ::: ```csharp showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { switch (result.Type) { case AdaptyPurchaseResultType.Pending: // handle pending purchase break; case AdaptyPurchaseResultType.UserCancelled: // handle purchase cancellation break; case AdaptyPurchaseResultType.Success: var profile = result.Profile; // handle successfull purchase break; default: break; } }); } ``` 请求参数: | 参数 | 是否必填 | 描述 | | :---------- | :------- |:------------------------------------------------------------------------------------------------------| | **Product** | 必填 | 从付费墙中获取的 [`AdaptyPaywallProduct`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall_product.html) 对象。| 响应参数: | 参数 | 描述 | |---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** |

请求成功后,响应中会包含此对象。[AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html) 对象提供了用户在应用内的访问等级、订阅及一次性购买的完整信息。

请检查访问等级状态,以确认用户是否拥有所需的应用访问权限。

| :::warning **注意:** 如果你使用的 Apple StoreKit 版本低于 v2.0,且 Adapty SDK 版本低于 v2.9.0,则需要提供 [Apple App Store 共享密钥](app-store-connection-configuration#step-5-enter-app-store-shared-secret)。此方法目前已被 Apple 弃用。 ::: ## 购买时更改订阅 \{#change-subscription-when-making-a-purchase\} 当用户选择新订阅而非续订当前订阅时,具体行为取决于所使用的应用商店: - 对于 App Store,订阅会在同一订阅组内自动更新。如果用户在已有某个订阅组的订阅的情况下,又购买了另一个订阅组的订阅,则两个订阅将同时处于激活状态。 - 对于 Google Play,订阅不会自动更新。你需要按照下方说明,在移动应用代码中手动处理订阅切换逻辑。 在 Android 上将订阅替换为另一个订阅,请在调用 `.makePurchase()` 方法时传入额外参数: ```csharp showLineNumbers // Create subscription update parameters var subscriptionUpdateParams = new AdaptySubscriptionUpdateParameters( "old_product_id", // Product ID of the current subscription AdaptySubscriptionUpdateReplacementMode.WithTimeProration ); Adapty.MakePurchase(product, subscriptionUpdateParams, (profile, error) => { if(error != null) { // Handle the error return; } // successful cross-grade }); ``` 额外请求参数: | 参数 | 是否必填 | 描述 | | :--------------------------- | :------- |:-------------------------------------------------------------------------------------------------------| | **subscriptionUpdateParams** | 必填 | 一个 [`AdaptySubscriptionUpdateParameters`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_subscription_update_parameters.html) 对象。 | 如需了解更多关于订阅和替换模式的内容,请参阅 Google 开发者文档: - [关于替换模式](https://developer.android.com/google/play/billing/subscriptions#replacement-modes) - [Google 针对替换模式的建议](https://developer.android.com/google/play/billing/subscriptions#replacement-recommendations) - 替换模式 [`CHARGE_PRORATED_PRICE`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#CHARGE_PRORATED_PRICE())。注意:此方法仅适用于订阅升级,不支持降级。 - 替换模式 [`DEFERRED`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.SubscriptionUpdateParams.ReplacementMode#DEFERRED())。注意:实际的订阅变更仅在当前订阅计费周期结束时生效。 ## 在 iOS 中兑换优惠码 \{#redeem-offer-codes-in-ios\}
关于优惠码 优惠码允许您向特定用户提供折扣或免费试用。与自动应用的常规优惠不同,优惠码通过应用外部渠道发放——例如电子邮件营销、社交媒体或印刷材料。用户可以通过在 App Store 中输入代码、访问兑换链接或在应用内对话框中进行兑换。 要设置优惠码,请在 App Store Connect 中打开某个订阅,然后进入其 **Offer Codes** 部分。您可以创建[三种类型](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes)的优惠码: - **Free** — 订阅在设定时长内免费,下次续订恢复原价。 - **Pay as you go** — 用户在设定时长内每个计费周期按折扣价付费,之后订阅恢复原价续订。 - **Pay up front** — 用户一次性按折扣价支付整个优惠期费用,之后订阅恢复原价续订。 您无需将优惠码添加到 Adapty。Apple 会在优惠期内为每笔交易打上优惠码类别标签,包括首次兑换和后续所有折扣续订。Adapty 检测到该标签后,会将每笔交易以 `offer_code` 优惠类别记录。优惠期结束、订阅以原价续订后,该标签将不再存在。您可以在 [Adapty 看板](controls-filters-grouping-compare-proceeds) 中按 **Offer Code** 优惠类型筛选分析数据。 #### 营收差异排查 如果您发现某笔优惠码交易在 Adapty 中以产品原价而非折扣价显示,请在 App Store Connect 中核实以下内容: - 优惠码已为用户可兑换的所有地区正确配置了定价。 - 已为用户所在的特定国家或地区设置了优惠价格。Apple 在交易中发送的是地区价格。如果该优惠未配置地区价格,Apple 可能会发送产品原价。 您可以在 [Adapty 看板](controls-filters-grouping-compare-proceeds) 中通过 **Offer Code** 优惠类型和 **Offer Discount Type** 筛选器来筛选和核实优惠码交易。 #### 旧版促销码(已弃用) :::warning Apple 于 2026 年 3 月弃用了应用内购买的促销码。优惠码以更强大的功能取而代之:可配置资格条件、设置到期日期,每季度最多可生成 100 万个代码。如果您之前使用促销码进行应用内购买,请在 App Store Connect 中迁移至优惠码。 ::: 旧版促销码(每个应用每个版本上限 100 个)可免费授予订阅访问权限。与优惠码不同,Apple 不会在促销码交易中包含折扣信息——它在收据中发送的是产品原价。因此,Adapty 以原价记录这些交易,导致 Adapty 分析数据与 App Store Connect 之间出现营收差异。 如果您看到历史交易以原价显示但本应免费,这些很可能来自旧版促销码。由于这些代码现已被弃用,请迁移至优惠码以确保营收数据的准确性。
在应用中显示兑换码界面: ```csharp showLineNumbers Adapty.PresentCodeRedemptionSheet((error) => { // handle the error }); ``` :::danger 根据我们的观察,某些应用中的优惠码兑换界面可能无法稳定运行。我们建议直接将用户跳转至 App Store。 为此,您需要打开以下格式的 URL: `https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}` ::: ## 管理预付费计划(Android) \{#manage-prepaid-plans-android\} 如果你的应用用户可以购买[预付费计划](https://developer.android.com/google/play/billing/subscriptions#prepaid-plans)(例如,一次性购买数月的非续订订阅),你可以为预付费计划启用[待处理交易](https://developer.android.com/google/play/billing/subscriptions#pending)功能。 ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetGoogleEnablePendingPrepaidPlans(true); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` --- # File: unity-restore-purchase --- --- title: "在 Unity SDK 中恢复移动应用内的购买" description: "了解如何在 Adapty 中恢复购买,以确保无缝的用户体验。" --- 在 iOS 和 Android 中恢复购买是一项功能,允许用户重新获取之前购买的内容(例如订阅或应用内购买),而无需再次付费。此功能对于那些可能已卸载并重新安装应用,或切换到新设备并希望访问之前购买内容而无需再次付款的用户尤为有用。 :::note 在使用[付费墙编辑工具](adapty-paywall-builder)构建的付费墙中,购买会自动恢复,无需您编写额外代码。如果您属于此情况,可以跳过此步骤。 ::: 如果您未使用[付费墙编辑工具](adapty-paywall-builder)来自定义付费墙,请调用 `.restorePurchases()` 方法来恢复购买: ```csharp showLineNumbers Adapty.RestorePurchases((profile, error) => { if (error != null) { // handle the error return; } var accessLevel = profile.AccessLevels["YOUR_ACCESS_LEVEL"]; if (accessLevel != null && accessLevel.IsActive) { // restore access } }); ``` 响应参数: | 参数 | 描述 | |---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Profile** |

一个 [`AdaptyProfile`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html) 对象。该模型包含有关访问等级、订阅和非订阅购买的信息。

请检查**访问等级状态**以确定用户是否有权访问该应用。

| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: implement-observer-mode-unity --- --- title: "在 Unity SDK 中实现观察者模式" description: "在 Adapty 中实现观察者模式,以在 Unity SDK 中追踪用户订阅事件。" --- 如果您已经拥有自己的购买基础设施,并且尚未准备好完全切换到 Adapty,您可以了解[观察者模式](observer-vs-full-mode)。在其基本形式下,观察者模式提供高级分析功能以及与归因和分析系统的无缝集成。 如果这满足您的需求,您只需: 1. 在配置 Adapty SDK 时将 `observerMode` 参数设置为 `true` 来启用它。请参照 [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk) 的设置说明。 2. 将现有购买基础设施中的[交易上报](report-transactions-observer-mode-unity)给 Adapty。 ### 观察者模式设置 \{#observer-mode-setup\} 如果您自行处理购买和订阅状态,并使用 Adapty 发送订阅事件和分析数据,请启用观察者模式。 :::important 在观察者模式下运行时,Adapty SDK 不会关闭任何交易,请确保您自行处理这一事项。 ::: ```csharp showLineNumbers title="C#" using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, AdaptyEventListener { void Start() { DontDestroyOnLoad(this.gameObject); Adapty.SetEventListener(this); var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY") .SetObserverMode(true); // Enable observer mode Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); } public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } } ``` 参数: | 参数 | 描述 | |--------------|-------------------------------------------------------------------------------------------------------------| | observerMode | 用于控制[观察者模式](observer-vs-full-mode)的布尔值。默认值为 `false`。 | ## 在观察者模式下使用 Adapty 付费墙 \{#using-adapty-paywalls-in-observer-mode\} 如果您还想使用 Adapty 的付费墙和 A/B 测试功能,也是可以的——但在观察者模式下需要一些额外的设置。除了上述步骤之外,您还需要: 1. 按照[远程配置付费墙](present-remote-config-paywalls-unity)的常规方式展示付费墙。 3. 将付费墙与购买交易进行[关联](report-transactions-observer-mode-unity)。 --- # File: report-transactions-observer-mode-unity --- --- title: "在 Unity SDK 的观察者模式下上报交易" description: "在 Adapty 观察者模式下上报购买交易,用于用户洞察和收入追踪(Unity SDK)。" --- 在观察者模式下,Adapty SDK 无法自行追踪通过您现有购买系统完成的购买。您需要从应用商店上报交易。务必在发布应用**之前**完成此设置,以避免分析数据出现错误。 使用 `reportTransaction` 显式上报每笔交易,以便 Adapty 识别。 :::warning **不要跳过交易上报!** 如果您不调用 `ReportTransaction`,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会被发送到集成系统。 ::: 如果您使用 Adapty 付费墙,请在上报交易时包含 `variationId`。这会将购买与触发它的付费墙关联起来,从而确保付费墙分析数据的准确性。 ```csharp showLineNumbers Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` 参数: | 参数 | 是否必填 | 描述 | | ------------- | -------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | 必填 |
  • iOS:交易的标识符。
  • Android:购买的字符串标识符 `purchase.getOrderId`,其中 purchase 是计费库 [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) 类的实例。
| | variationId | 可选 | 实验变体的字符串标识符。可通过 [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象的 `variationId` 属性获取。 |
在观察者模式下,Adapty SDK 无法自行追踪通过您现有购买系统完成的购买。您需要从应用商店上报或恢复交易。务必在发布应用**之前**完成此设置,以避免分析数据出现错误。 在两个平台上使用 `reportTransaction` 显式上报每笔交易,并在 Android 上额外使用 `restorePurchases`,以确保 Adapty 识别该交易。 :::warning **不要跳过交易上报和购买恢复!** 如果您不调用这些方法,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会被发送到集成系统。 ::: 如果您使用 Adapty 付费墙,请在上报交易时包含 `PAYWALL_VARIATION_ID`。这会将购买与触发它的付费墙关联起来,从而确保付费墙分析数据的准确性。 ```csharp showLineNumbers // every time when calling transasction.finish() #if UNITY_ANDROID && !UNITY_EDITOR Adapty.RestorePurchases((profile, error) => { // handle the error }); #endif Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` 参数: | 参数 | 是否必填 | 描述 | | ------------- | -------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | 必填 |
  • iOS,StoreKit 1:一个 [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction) 对象。
  • iOS,StoreKit 2:[Transaction](https://developer.apple.com/documentation/storekit/transaction) 对象。
  • Android:购买的字符串标识符(`purchase.getOrderId`),其中 purchase 是计费库 [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) 类的实例。
| | variationId | 可选 | 实验变体的字符串标识符。可通过 [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象的 `variationId` 属性获取。 |
**上报交易** - 3.1.x 及以下版本会自动监听 App Store 中的交易,无需手动上报。 - 3.2 版本不支持观察者模式。 **上报交易** 使用 `restorePurchases` 在观察者模式下向 Adapty 上报交易,详情请参阅[在移动端代码中恢复购买](unity-restore-purchase)页面。 :::warning **不要跳过交易上报!** 如果您不调用 `restorePurchases`,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会被发送到集成系统。 ::: **将付费墙与交易关联** Adapty SDK 无法确定购买的来源,因为购买是由您来处理的。因此,如果您打算在观察者模式下使用付费墙和/或 A/B 测试,则需要在移动应用代码中将来自应用商店的交易与相应的付费墙关联起来。在发布应用之前务必正确完成此操作,否则将导致分析数据出现错误。 ```csharp Adapty.SetVariationForTransaction("", "", (error) => { if(error != null) { // handle the error return; } // successful binding }); ``` | 参数 | 是否必填 | 描述 | | ------------------------------------------------------ | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | 必填 |

iOS,StoreKit 1:一个 [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction) 对象。

iOS,StoreKit 2:[Transaction](https://developer.apple.com/documentation/storekit/transaction) 对象。

Android:购买的字符串标识符(purchase.getOrderId),其中 purchase 是计费库 [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) 类的实例。

| | variationId | 必填 | 实验变体的字符串标识符。可通过 [AdaptyPaywall](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_paywall.html) 对象的 `variationId` 属性获取。 |
--- # File: unity-troubleshoot-purchases --- --- title: "排查 Unity SDK 中的购买问题" description: "排查 Unity SDK 中的购买问题" --- 本指南帮助您解决在 Unity SDK 中手动实现购买时遇到的常见问题。 ## makePurchase 调用成功,但用户画像未更新 \{#makepurchase-is-called-successfully-but-the-profile-is-not-being-updated\} **问题**:`makePurchase` 方法成功执行,但用户的用户画像和订阅状态在 Adapty 中未被更新。 **原因**:这通常表示 Google Play Store 设置不完整或存在配置问题。 **解决方案**:请确保您已完成所有 [Google Play 设置步骤](initial-android)。 ## makePurchase 被调用两次 \{#makepurchase-is-invoked-twice\} **问题**:`makePurchase` 方法针对同一次购买被多次调用。 **原因**:这通常发生在由于 UI 状态管理问题或用户快速操作导致购买流程被多次触发时。 **解决方案**:请确保您已完成所有 [Google Play 设置步骤](initial-android)。 ## 观察者模式下出现 AdaptyError.cantMakePayments \{#adaptyerrorcantmakepayments-in-observer-mode\} **问题**:在观察者模式下使用 `makePurchase` 时收到 `AdaptyError.cantMakePayments` 错误。 **原因**:在观察者模式下,您应在自己的代码中处理购买,而不是使用 Adapty 的 `makePurchase` 方法。 **解决方案**:如果您使用 `makePurchase` 处理购买,请关闭观察者模式。您需要二选一:要么使用 `makePurchase`,要么在观察者模式下自行处理购买。详情请参阅[实现观察者模式](implement-observer-mode-unity)。 ## Adapty 错误:(code: 103, message: Play Market request failed on purchases updated: responseCode=3, debugMessage=Billing Unavailable, detail: null) \{#adapty-error-code-103-message-play-market-request-failed-on-purchases-updated-responsecode3-debugmessagebilling-unavailable-detail-null\} **问题**:您收到来自 Google Play Store 的计费不可用错误。 **原因**:此错误与 Adapty 无关,它是 Google Play 计费库的错误,表示设备上的计费功能不可用。 **解决方案**:此错误与 Adapty 无关。您可以在 Play Store 文档中查看更多信息:[处理 BillingResult 响应码](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) | Play Billing | Android Developers。 ## 未找到 makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\} **问题**:您遇到了找不到 `makePurchasesCompletionHandlers` 的问题。 **原因**:这通常与沙盒测试问题有关。 **解决方案**:创建一个新的沙盒用户并重试。这通常可以解决与沙盒相关的购买完成处理程序问题。 ## 其他问题 \{#other-issues\} **问题**:您遇到了上述未涵盖的其他购买相关问题。 **解决方案**:如有需要,请使用[迁移指南](unity-sdk-migration-guides)将 SDK 升级至最新版本。许多问题已在较新版本的 SDK 中得到修复。 --- # File: unity-identifying-users --- --- title: "在 Unity SDK 中识别用户" description: "了解如何在 Unity 应用中使用 Adapty SDK 识别用户。" --- Adapty 会为每位用户创建一个内部 Profile ID。但如果你有自己的身份验证系统,则应设置自己的 Customer User ID。你可以在[用户画像](profiles-crm)页面通过 Customer User ID 查找用户,也可以在[服务端 API](getting-started-with-server-side-api) 中使用它,该 ID 会同步发送至所有集成。 ### 在配置时设置 Customer User ID \{#setting-customer-user-id-on-configuration\} 如果您在配置时已有用户 ID,只需将其作为 `customerUserId` 参数传递给 `.activate()` 方法: ```csharp showLineNumbers using UnityEngine; using AdaptySDK; var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ### 配置完成后设置用户 ID \{#setting-customer-user-id-after-configuration\} 如果在 SDK 配置时没有用户 ID,可以随时通过 `.identify()` 方法进行设置。最常见的使用场景是用户完成注册或登录后,从匿名用户切换为已认证用户时。 ```csharp showLineNumbers Adapty.Identify("YOUR_USER_ID", (error) => { if(error == null) { // successful identify } }); ``` 请求参数: - **Customer User ID**(必填):字符串类型的用户标识符。 :::warning 重新提交重要用户数据 在某些情况下,例如用户重新登录账号时,Adapty 服务器可能已经存有该用户的信息。此时,Adapty SDK 会自动切换到新用户。如果你之前为匿名用户设置了自定义属性或第三方网络的归因数据,需要为已识别的用户重新提交这些数据。 此外,识别用户后应重新请求所有付费墙和产品,因为新用户的数据可能有所不同。 ::: ### 登出与登录 \{#logging-out-and-logging-in\} 您可以随时调用 `.logout()` 方法使用户登出: ```csharp showLineNumbers Adapty.Logout((error) => { if(error == null) { // successful logout } }); ``` 之后可以使用 `.identify()` 方法使用户重新登录。 ## 分配 `appAccountToken`(iOS)\{#assign-appaccounttoken-ios\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) 是一个 **UUID**,用于将 App Store 交易与你的内部用户身份关联起来。StoreKit 会将该令牌与每笔交易绑定,以便你的后端将 App Store 数据与用户对应匹配。 建议为每个用户生成一个稳定的 UUID,并在同一账号的不同设备上复用该 UUID。这样可以确保购买记录和 App Store 通知始终与正确的用户关联。 您可以通过两种方式设置令牌——在 SDK 初始化时,或在识别用户时。 :::important 您必须始终将 `appAccountToken` 与 `customerUserId` 一起传递。 如果只传递令牌而不传递 `customerUserId`,令牌将不会包含在交易中。 ::: ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; using System; // During configuration: var appAccountToken = new Guid("YOUR_APP_ACCOUNT_TOKEN"); var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID", appAccountToken); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); // Or when identifying users Adapty.Identify("YOUR_USER_ID", appAccountToken, (error) => { if (error == null) { // successful identify } }); ``` ## 设置混淆账户 ID(Android)\{#set-obfuscated-account-ids-android\} Google Play 在某些场景下要求使用混淆账户 ID,以保护用户隐私和安全。这些 ID 帮助 Google Play 在识别购买记录的同时保持用户信息匿名,在防欺诈和数据分析方面尤为重要。 如果你的应用处理敏感用户数据,或需要遵守特定的隐私法规,则可能需要设置这些 ID。混淆 ID 让 Google Play 能够追踪购买行为,同时不暴露真实的用户标识。 ```csharp showLineNumbers title="Unity" using UnityEngine; using AdaptySDK; // 配置期间: var builder = new AdaptyConfiguration.Builder("YOUR_API_KEY") .SetCustomerUserId("YOUR_USER_ID", null, "YOUR_OBFUSCATED_ACCOUNT_ID"); Adapty.Activate(builder.Build(), (error) => { if (error != null) { // 处理错误 return; } }); // 或在识别用户时 Adapty.Identify("YOUR_USER_ID", null, "YOUR_OBFUSCATED_ACCOUNT_ID", (error) => { if (error == null) { // 识别成功 } }); ``` ## 跨设备用户识别 \{#detect-users-across-devices\} 当 SDK 激活时,它会自动从 StoreKit (iOS) 或 Google Play Billing (Android) 读取用户现有的权益,并将其同步到 Adapty 后端。活跃订阅无需应用调用 `restorePurchases`,即可出现在 Adapty 用户画像中。 **不会**自动发生的是:识别新设备上的用户画像与原设备上的用户画像属于同一用户。Adapty 通过 Customer User ID 匹配用户画像,因此身份连续性取决于您使用什么作为 CUID。 **Adapty 跨设备可检测的内容** | 您的配置 | Adapty 检测到的内容 | 您需要做什么 | | --- | --- | --- | | Customer User ID = `device_id`(无应用登录) | 新设备获得不同的 CUID,因此拥有不同的用户画像。订阅通过 **Access level updated** 事件同步到新用户画像,但 `subscription_started` 不会触发——新用户画像被视为原始购买的继承者。基于 `subscription_started` 的分析将少计回归用户。 | 使用稳定的账户 ID 作为 Customer User ID,以便回归用户能跨设备匹配到现有用户画像。 | | Customer User ID = 稳定账户 ID(每台设备均需登录) | SDK 在 `activate()` 时自动同步订阅,`identify()` 通过 CUID 匹配现有用户画像。 | 无需额外配置——身份和订阅均可自动解析。 | | Apple Family Sharing 继承者 | 家庭成员仅通过 **Access level updated** 事件接收订阅——`subscription_started` 不会触发。 | 监听 **Access level updated**。完整的事件矩阵请参见 [Apple Family Sharing](apple-family-sharing)。 | | 同一 Apple/Google 账户,不同应用内用户 | 最先记录购买的用户画像成为父级。后续用户画像通过继承链查看订阅,并触发一次 **Access level updated** 事件。 | 要求用户登录,然后选择适合您业务模型的[共享模式](sharing-paid-access-between-user-accounts)。 | **在新设备上恢复购买** 在付费墙上提供一个用户可主动触发的"恢复购买"按钮。Apple App Review(指南 3.1.1)要求提供此按钮,且当自动同步遗漏边缘情况时,它也可作为备用方案。该按钮应调用 SDK 中的 `restorePurchases`。 正常使用时,首次启动时无需通过代码调用 `restorePurchases`——SDK 已在 `activate()` 时执行了等效操作。仅在需要强制刷新收据检查时才使用代码调用,例如在 `activate()` 完成后调试访问等级缺失问题时。 --- # File: unity-setting-user-attributes --- --- title: "在 Unity SDK 中设置用户属性" description: "了解如何使用 Adapty SDK 在 Unity 应用中更新用户属性和用户画像数据。" --- 您可以为应用用户设置可选属性,例如电子邮件、电话号码等。然后,您可以使用这些属性创建用户[市场细分](segments),或直接在 CRM 中查看。 ### 设置用户属性 \{#setting-user-attributes\} 要设置用户属性,请调用 `.updateProfile()` 方法: ```csharp showLineNumbers var builder = new Adapty.ProfileParameters.Builder() .SetFirstName("John") .SetLastName("Appleseed") .SetBirthday(new DateTime(1970, 1, 3)) .SetGender(ProfileGender.Female) .SetEmail("example@adapty.io"); Adapty.UpdateProfile(builder.Build(), (error) => { if(error != nil) { // handle the error } }); ``` 请注意,之前通过 `updateProfile` 方法设置的属性不会被重置。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ### 允许的键列表 \{#the-allowed-keys-list\} `AdaptyProfileParameters.Builder` 允许的键 `` 及其对应的值 `` 如下所示: | 键 | 值 | |---|-----| |

email

phoneNumber

firstName

lastName

| String | | gender | 枚举,允许的值为:`female`、`male`、`other` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 您可以设置自定义属性,这些属性通常与您的应用使用情况相关。例如,对于健身应用,可以是每周锻炼次数;对于语言学习应用,可以是用户的知识水平等。您可以在市场细分中使用这些属性来创建有针对性的付费墙和优惠,也可以在分析中使用它们来确定哪些产品指标对收入影响最大。 ```csharp showLineNumbers try { builder = builder.SetCustomStringAttribute("string_key", "string_value"); builder = builder.SetCustomDoubleAttribute("double_key", 123.0f); } catch (Exception e) { // handle the exception } ``` 要删除现有键,请使用 `.withRemoved(customAttributeForKey:)` 方法: ```csharp showLineNumbers try { builder = builder.RemoveCustomAttribute("key_to_remove"); } catch (Exception e) { // handle the exception } ``` 有时您需要查看已设置的自定义属性。为此,请使用 `AdaptyProfile` 对象的 `customAttributes` 字段。 :::warning 请注意,`customAttributes` 的值可能并非最新,因为用户属性可以随时从不同设备发送,因此服务器上的属性可能在上次同步后已发生变化。 ::: ### 限制 \{#limits\} - 每位用户最多 30 个自定义属性 - 键名最多 30 个字符,可包含字母数字字符及以下任意字符:`_` `-` `.` - 值可以是字符串或浮点数,最多 50 个字符。 --- # File: unity-listen-subscription-changes --- --- title: "在 Unity SDK 中检查订阅状态" description: "在 Adapty 中追踪和管理用户订阅状态,提升 Unity 应用的用户留存率。" --- 借助 Adapty,追踪订阅状态变得轻而易举。您无需在代码中手动插入产品 ID,只需检查用户是否拥有有效的[访问等级](access-level),即可确认其订阅状态。
开始检查订阅状态之前(点击展开) - 对于 iOS,请配置 [App Store 服务器通知](enable-app-store-server-notifications) - 对于 Android,请配置[实时开发者通知 (RTDN)](enable-real-time-developer-notifications-rtdn)
## 访问等级与 AdaptyProfile 对象 \{#access-level-and-the-adaptyprofile-object\} 访问等级是 [AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html) 对象的属性。我们建议在应用启动时(例如[识别用户](unity-identifying-users#setting-customer-user-id-on-configuration)时)获取用户画像,并在发生变更时及时更新。这样,您就可以直接使用已获取的用户画像对象,而无需反复请求。 如需接收用户画像更新通知,请按照下方[监听订阅状态更新](#listening-for-subscription-status-updates)章节的说明监听用户画像变更事件。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 从服务器获取访问等级 \{#retrieving-the-access-level-from-the-server\} 使用 `.GetProfile()` 方法从服务器获取访问等级: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // check the access }); ``` 响应参数: | 参数 | 描述 | | --------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile |

[AdaptyProfile](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_profile.html) 对象。通常,您只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级权限。

`.getProfile` 方法始终会尝试请求 API,因此可提供最新的结果。如果由于某些原因(如无网络连接)Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。值得注意的是,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持信息的最新状态。

| `.getProfile()` 方法会返回用户画像,您可以从中获取访问等级状态。每个应用可以设置多个访问等级。例如,如果您有一个新闻应用,并针对不同主题独立销售订阅,可以创建"sports"和"science"等访问等级。但大多数情况下,您只需要一个访问等级,此时直接使用默认的"premium"访问等级即可。 以下是检查默认"premium"访问等级的示例: ```csharp showLineNumbers Adapty.GetProfile((profile, error) => { if (error != null) { // handle the error return; } // "premium" is an identifier of default access level var accessLevel = profile.AccessLevels["premium"]; if (accessLevel != null && accessLevel.IsActive) { // grant access to premium features } }); ``` ### 监听订阅状态更新 \{#listening-for-subscription-status-updates\} 每当用户的订阅发生变化时,Adapty 都会触发相应事件。 要接收来自 Adapty 的消息,您需要进行一些额外配置: ```csharp showLineNumbers // Extend `AdaptyEventListener ` with `OnLoadLatestProfile ` method: public class AdaptyListener : MonoBehaviour, AdaptyEventListener { public void OnLoadLatestProfile(AdaptyProfile profile) { // handle any changes to subscription state } } ``` Adapty 也会在应用启动时触发事件,此时将传递缓存的订阅状态。 ### 订阅状态缓存 \{#subscription-status-cache\} Adapty SDK 中实现的缓存会存储用户画像的订阅状态。这意味着即使服务器不可用,也可以访问缓存数据以获取用户画像的订阅状态信息。 但需要注意的是,无法直接从缓存中请求数据。SDK 会每分钟定期向服务器查询,以检查用户画像是否有任何更新或变更。如果存在任何修改(如新的交易记录或其他更新),这些变更将同步至缓存数据,以确保其与服务器保持一致。 --- # File: unity-deal-with-att --- --- title: "在 Unity SDK 中处理 ATT" description: "在 Unity 上开始使用 Adapty,简化订阅设置与管理。" --- 如果您的应用使用了 AppTrackingTransparency 框架,并向用户展示应用跟踪授权请求,则应将[授权状态](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/)发送给 Adapty。 ```csharp showLineNumbers var builder = new Adapty.ProfileParameters.Builder() .SetAppTrackingTransparencyStatus(IOSAppTrackingTransparencyStatus.Authorized); Adapty.UpdateProfile(builder.Build(), (error) => { if(error != null) { // handle the error } }); ``` :::warning 我们强烈建议您在该值发生变化时尽早发送,只有这样,数据才能及时传送到您已配置的集成渠道。 ::: --- # File: kids-mode-unity --- --- title: "Unity SDK 中的儿童模式" description: "轻松启用儿童模式以符合 Apple 和 Google 的政策。Unity SDK 中不收集 IDFA、GAID 或广告数据。" --- 如果您的 Unity 应用面向儿童,则必须遵守 [Apple](https://developer.apple.com/kids/) 和 [Google](https://support.google.com/googleplay/android-developer/answer/9893335) 的政策。如果您使用的是 Adapty SDK,只需几个简单的步骤即可将其配置为符合这些政策,并通过应用商店审核。 ## 需要做什么? \{#whats-required\} 您需要配置 Adapty SDK 以禁止收集以下信息: - [IDFA(广告标识符)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers)(iOS) - [Android 广告 ID(AAID/GAID)](https://support.google.com/googleplay/android-developer/answer/6048248)(Android) - [IP 地址](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf) 此外,我们建议谨慎使用客户用户 ID。`` 格式的用户 ID 肯定会被视为收集个人数据,使用电子邮件亦然。对于儿童模式,最佳实践是使用随机化或匿名化的标识符(例如哈希 ID 或设备生成的 UUID),以确保合规。 ## 启用儿童模式 \{#enabling-kids-mode\} ### 在 Adapty 看板中进行更新 \{#updates-in-the-adapty-dashboard\} 在 Adapty 看板中,您需要禁用 IP 地址收集。为此,请前往 [App settings](https://app.adapty.io/settings/general),然后在 **Collect users' IP address** 下点击 **Disable IP address collection**。 ### 在您的移动应用代码中进行更新 \{#updates-in-your-mobile-app-code\} Unity 中对儿童模式的支持即将推出! 目前,您可以参阅原生平台指南: - [iOS SDK 中的儿童模式](kids-mode),用于 iOS 配置 - [Android SDK 中的儿童模式](kids-mode-android),用于 Android 配置 --- # File: unity-get-onboardings --- --- title: "在 Unity SDK 中获取用户引导" description: "了解如何在 Adapty for Unity 中获取用户引导。" --- 在 Adapty 看板中[使用编辑工具设计完用户引导的视觉部分](design-onboarding)后,您可以在 Unity 应用中展示它。此过程的第一步是获取与版位关联的用户引导及其视图配置,具体如下所述。 开始之前,请确保: 1. 您已安装 [Adapty Unity SDK](sdk-installation-unity) 3.14.0 或更高版本。 2. 您已[创建用户引导](create-onboarding)。 3. 您已将用户引导添加到[版位](placements)中。 ## 获取用户引导并创建视图 \{#fetch-onboarding-and-create-view\} 当您使用我们的无代码编辑工具创建[用户引导](onboardings)时,它会以容器的形式存储,包含应用需要获取并展示的配置。该容器管理整个体验——显示哪些内容、如何呈现,以及如何处理用户交互(例如测验答案或表单输入)。容器还会自动跟踪分析事件,因此您无需单独实现视图跟踪。 为了获得最佳性能,请尽早获取用户引导配置,以便在向用户展示之前有足够时间下载图片。 要获取用户引导,请使用 `GetOnboarding` 方法: ```csharp showLineNumbers Adapty.GetOnboarding("YOUR_PLACEMENT_ID", (onboarding, error) => { if (error != null) { // handle the error return; } // the requested onboarding }); ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |

可选

默认值:`en`

|

用户引导本地化的标识符。该参数应为由减号(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个子标签表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

有关区域设置代码及推荐使用方式的更多信息,请参阅[本地化与区域设置代码](flutter-localizations-and-locale-codes)。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。

但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取绝对最新的数据,但无论网络连接多么不稳定,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。

请注意,缓存在应用重启后保持不变,仅在重新安装应用或手动清理时才会清除。

Adapty SDK 在本地以两个层次存储用户引导:上述定期更新的缓存以及备用用户引导。我们还使用 CDN 更快地获取用户引导,并在 CDN 无法访问时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不佳的情况下也能保证可靠性。

| | **loadTimeout** | 默认值:5 秒 |

此值限制该方法的超时时间。如果达到超时时间,将返回缓存数据或本地备用内容。

请注意,在极少数情况下,此方法可能比 `loadTimeout` 中指定的时间稍晚超时,因为该操作在内部可能由多个不同请求组成。

| 响应参数: | 参数 | 描述 | |:----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://unity.adapty.io/class_adapty_s_d_k_1_1_adapty_onboarding.html) 对象,包含:用户引导标识符和配置、远程配置以及其他几个属性。 | 获取用户引导后,调用 `CreateOnboardingView` 方法。 :::warning `CreateOnboardingView` 方法的结果只能使用一次。如果需要再次使用,请重新调用 `CreateOnboardingView` 方法。在不重新创建的情况下调用两次可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```csharp showLineNumbers AdaptyUI.CreateOnboardingView(onboarding, (view, error) => { // handle the result }); ``` 参数: | 参数 | 是否必填 | 描述 | |:---------------| :------------- |:-----------------------------------------------------------------------------| | **onboarding** | 必填 | 用于获取所需用户引导视图的 `AdaptyOnboarding` 对象。 | | **externalUrlsPresentation** |

可选

默认值:`InAppBrowser`

|

控制用户引导中链接的打开方式。可用选项:

- `AdaptyWebPresentation.InAppBrowser` - 在应用内浏览器中打开链接(默认)

- `AdaptyWebPresentation.ExternalBrowser` - 在设备外部浏览器中打开链接

使用示例请参阅[自定义用户引导中链接的打开方式](unity-present-onboardings#customize-how-links-open-in-onboardings)。

| 成功加载用户引导及其视图配置后,您可以[在移动应用中展示它](unity-present-onboardings)。 ## 使用默认目标受众用户引导加速获取 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常情况下,用户引导几乎可以即时获取,因此您无需担心加速此过程。但是,如果您拥有大量目标受众和用户引导,且用户的网络连接较弱,获取用户引导可能需要比预期更长的时间。在这种情况下,您可能希望显示默认用户引导,以确保流畅的用户体验,而不是完全不显示用户引导。 为此,您可以使用 `GetOnboardingForDefaultAudience` 方法,该方法为**所有用户**目标受众获取指定版位的用户引导。但是,务必理解,推荐的方式是通过 `getOnboarding` 方法获取用户引导,详见上方[获取用户引导](#fetch-onboarding)部分。 :::warning 建议使用 `GetOnboarding` 而非 `GetOnboardingForDefaultAudience`,因为后者有以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要向后兼容的设计,否则较旧版本可能显示不正确。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法根据国家、归因或自定义属性进行定向。 如果对您的使用场景而言,更快的获取速度超过了这些缺点,请按如下所示使用 `GetOnboardingForDefaultAudience`。否则,请按[上方](#fetch-onboarding)所述使用 `GetOnboarding`。 ::: ```csharp showLineNumbers Adapty.GetOnboardingForDefaultAudience("YOUR_PLACEMENT_ID", (onboarding, error) => { if (error != null) { // handle the error return; } // the requested onboarding }); ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |

可选

默认值:`en`

|

用户引导本地化的标识符。该参数应为由减号(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个子标签表示地区。

示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。

| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |

默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。

但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取绝对最新的数据,但无论网络连接多么不稳定,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。

请注意,缓存在应用重启后保持不变,仅在重新安装应用或手动清理时才会清除。

Adapty SDK 在本地以两个层次存储用户引导:上述定期更新的缓存以及备用用户引导。我们还使用 CDN 更快地获取用户引导,并在 CDN 无法访问时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不佳的情况下也能保证可靠性。

| --- # File: unity-present-onboardings --- --- title: "在 Unity SDK 中展示用户引导" description: "了解如何有效地展示用户引导以提升转化率。" --- 如果你已经在编辑工具中自定义了用户引导,就不需要在 Unity 应用代码中另行处理渲染逻辑——该用户引导已经包含了展示内容和展示方式的完整配置。 开始之前,请确保: 1. 已安装 [Adapty Unity SDK](sdk-installation-unity) 3.14.0 或更高版本。 2. 已[创建用户引导](create-onboarding)。 3. 已将用户引导添加到[版位](placements)。 要展示用户引导,请对 `CreateOnboardingView` 方法创建的 `view` 调用 `view.Present()` 方法。每个 `view` 只能使用一次。如需再次展示付费墙,请重新调用 `CreateOnboardingView` 创建新的 `view` 实例。 :::warning 在未重新创建 `view` 的情况下复用同一个 `view`,可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```csharp showLineNumbers title="Unity" view.Present((presentError) => { if (presentError != null) { // handle the error } }; ``` ## 配置 iOS 展示样式 \{#configure-ios-presentation-style\} 通过将 `iosPresentationStyle` 参数传递给 `Present()` 方法,可配置用户引导在 iOS 上的展示方式。该参数接受 `AdaptyUIIOSPresentationStyle.FullScreen`(默认值)或 `AdaptyUIIOSPresentationStyle.PageSheet` 值。 ```csharp showLineNumbers title="Unity" view.Present(AdaptyUIIOSPresentationStyle.PageSheet, (error) => { // handle the error }); ``` ## 自定义用户引导中链接的打开方式 \{#customize-how-links-open-in-onboardings\} :::important 自定义用户引导中链接打开方式的功能从 Adapty SDK v3.15 开始支持。 ::: 默认情况下,用户引导中的链接会在应用内浏览器中打开,让用户无需切换应用即可直接查看网页内容,体验更加流畅。 如需改为在外部浏览器中打开链接,请将 `AdaptyWebPresentation.ExternalBrowser` 传入 `CreateOnboardingView` 方法: ```csharp showLineNumbers title="Unity" AdaptyUI.CreateOnboardingView( onboarding, AdaptyWebPresentation.ExternalBrowser, // default — InAppBrowser (view, error) => { if (error != null) { // handle the error return; } // present the onboarding view view.Present((presentError) => { if (presentError != null) { // handle the error } }); } ); ``` 可用选项: - `AdaptyWebPresentation.InAppBrowser` - 在应用内浏览器中打开链接(默认) - `AdaptyWebPresentation.ExternalBrowser` - 在设备的外部浏览器中打开链接 --- # File: unity-handling-onboarding-events --- --- title: "在 Unity SDK 中处理用户引导事件" description: "使用 Adapty 在 Unity 中处理用户引导相关事件。" --- 在开始之前,请确保: 1. 您已安装 [Adapty Unity SDK](sdk-installation-unity) 3.14.0 或更高版本。 2. 您已[创建用户引导](create-onboarding)。 3. 您已将用户引导添加到[版位](placements)。 使用编辑工具配置的用户引导会生成应用可以响应的事件。请参阅以下内容了解如何响应这些事件。 要在 Unity 应用中控制或监控用户引导界面上发生的流程,请实现 `AdaptyOnboardingsEventsListener` 接口。 ## 自定义动作 \{#custom-actions\} 在编辑工具中,您可以为按钮添加**自定义**动作并为其分配一个 ID。 然后,您可以在代码中使用此 ID 并将其作为自定义动作处理。例如,当用户点击自定义按钮(如**登录**或**允许通知**)时,将触发 `OnboardingViewOnCustomAction` 方法,其中 `actionId` 参数为编辑工具中的 **Action ID**。您可以创建自己的 ID,例如 "allowNotifications"。 要处理用户引导事件,请实现 `AdaptyOnboardingsEventsListener` 接口: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { void Start() { Adapty.SetOnboardingsEventsListener(this); } public void OnboardingViewOnCustomAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { if (actionId == "allowNotifications") { // request notification permissions } } public void OnboardingViewDidFailWithError( AdaptyUIOnboardingView view, AdaptyError error ) { // handle errors } // Implement other required interface methods (see examples below) } ```
事件示例(点击展开) ```json { "actionId": "allowNotifications", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 } } ```
## 关闭用户引导 \{#closing-onboarding\} 当用户点击分配了**关闭**动作的按钮时,用户引导即被视为已关闭。 :::important 请注意,您需要自行管理用户关闭用户引导后发生的事情。例如,您需要停止显示用户引导本身。 ::: 在您的类中实现 `OnboardingViewOnCloseAction` 方法: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnCloseAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { view.Dismiss((error) => { if (error != null) { // handle the error } }); } // ... other interface methods } ```
事件示例(点击展开) ```json { "action_id": "close_button", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
## 打开付费墙 \{#opening-a-paywall\} :::tip 如果你想在用户引导内部打开付费墙,请处理此事件。如果你想在付费墙关闭后再打开另一个付费墙,有一种更直接的方式——处理 [`OnboardingViewOnCloseAction`](#closing-onboarding) 并直接打开付费墙,无需依赖事件数据。 ::: 在用户引导中使用付费墙最流畅的方式,是将 action ID 设置为与付费墙版位 ID 相同。这样,在收到 `OnboardingViewOnPaywallAction` 事件后,你可以直接用版位 ID 获取并打开对应的付费墙。 请注意,在 iOS 上,屏幕上一次只能显示一个视图(付费墙或用户引导)。如果您在用户引导上叠加显示付费墙,则无法以编程方式控制后台的用户引导。尝试关闭用户引导将会关闭付费墙,导致用户引导仍然可见。为避免这种情况,请始终在显示付费墙之前先关闭用户引导视图。 ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnPaywallAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string actionId ) { // Dismiss onboarding before presenting paywall view.Dismiss((dismissError) => { if (dismissError != null) { // handle the error return; } Adapty.GetPaywall(actionId, (paywall, error) => { if (error != null) { // handle the error return; } AdaptyUI.CreatePaywallView(paywall, (paywallView, createError) => { if (createError != null) { // handle the error return; } paywallView.Present((presentError) => { if (presentError != null) { // handle the error } }); }); }); }); } // ... other interface methods } ```
事件示例(点击展开) ```json { "action_id": "premium_offer_1", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "pricing_screen", "screen_index": 2, "total_screens": 4 } } ```
## 完成用户引导加载 \{#finishing-loading-onboarding\} 当用户引导加载完成时,实现 `OnboardingViewDidFinishLoading` 方法: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewDidFinishLoading( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta ) { // handle loading completion } // ... other interface methods } ```
事件示例(点击展开) ```json { "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } ```
## 追踪导航 \{#tracking-navigation\} `OnboardingViewOnAnalyticsEvent` 方法在用户引导流程中发生各种分析事件时被调用。 `analyticsEvent` 对象可以是以下类型之一: | 类型 | 描述 | |------------|-------------| | `AdaptyOnboardingsAnalyticsEventOnboardingStarted` | 用户引导已加载时 | | `AdaptyOnboardingsAnalyticsEventScreenPresented` | 任何屏幕显示时 | | `AdaptyOnboardingsAnalyticsEventScreenCompleted` | 屏幕完成时。包含可选的 `ElementId`(已完成元素的标识符)和可选的 `Reply`(用户的响应)。当用户执行任何操作退出屏幕时触发。| | `AdaptyOnboardingsAnalyticsEventSecondScreenPresented` | 第二个屏幕显示时 | | `AdaptyOnboardingsAnalyticsEventUserEmailCollected` | 通过输入字段收集用户邮箱时触发 | | `AdaptyOnboardingsAnalyticsEventOnboardingCompleted` | 当用户到达具有 `final` ID 的屏幕时触发。如果您需要此事件,请[将 `final` ID 分配给最后一个屏幕](design-onboarding)。| | `AdaptyOnboardingsAnalyticsEventUnknown` | 用于任何无法识别的事件类型。包含 `Name`(未知事件的名称)和 `meta`(附加元数据)| 每个事件都包含 `meta` 信息: | 字段 | 描述 | |------------|-------------| | `OnboardingId` | 用户引导流程的唯一标识符 | | `ScreenClientId` | 当前屏幕的标识符 | | `ScreenIndex` | 当前屏幕在流程中的位置 | | `ScreensTotal` | 流程中的屏幕总数 | 以下是如何将分析事件用于追踪的示例: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnAnalyticsEvent( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, AdaptyOnboardingsAnalyticsEvent analyticsEvent ) { switch (analyticsEvent) { case AdaptyOnboardingsAnalyticsEventOnboardingStarted: // track onboarding start TrackEvent("onboarding_started", meta); break; case AdaptyOnboardingsAnalyticsEventScreenPresented: // track screen presentation TrackEvent("screen_presented", meta); break; case AdaptyOnboardingsAnalyticsEventScreenCompleted screenCompleted: // track screen completion with user response TrackEvent("screen_completed", meta, screenCompleted.ElementId, screenCompleted.Reply); break; case AdaptyOnboardingsAnalyticsEventOnboardingCompleted: // track successful onboarding completion TrackEvent("onboarding_completed", meta); break; case AdaptyOnboardingsAnalyticsEventUnknown unknownEvent: // handle unknown events TrackEvent(unknownEvent.Name, meta); break; // handle other cases as needed } } // ... other interface methods } ``` :::note `TrackEvent` 方法是一个占位符,您需要自行实现它以将分析数据发送到您首选的分析服务。 :::
事件示例(点击展开) ```javascript // onboardingStarted { "name": "onboarding_started", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "welcome_screen", "screen_index": 0, "total_screens": 4 } } // screenPresented { "name": "screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "interests_screen", "screen_index": 2, "total_screens": 4 } } // screenCompleted { "name": "screen_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 }, "params": { "element_id": "profile_form", "reply": "success" } } // secondScreenPresented { "name": "second_screen_presented", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // userEmailCollected { "name": "user_email_collected", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "profile_screen", "screen_index": 1, "total_screens": 4 } } // onboardingCompleted { "name": "onboarding_completed", "meta": { "onboarding_id": "onboarding_123", "screen_cid": "final_screen", "screen_index": 3, "total_screens": 4 } } ```
--- # File: unity-onboarding-input --- --- title: "在 Unity SDK 中处理用户引导数据" description: "使用 Adapty SDK 在 Unity 应用中保存和使用用户引导数据。" --- 当用户回答测验问题或在输入框中输入数据时,`OnboardingViewOnStateUpdatedAction` 方法将被调用。您可以在代码中保存或处理字段类型。 在您的类中实现 `OnboardingViewOnStateUpdatedAction` 方法: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { switch (@params) { case AdaptyOnboardingsSelectParams selectParams: // handle single selection break; case AdaptyOnboardingsMultiSelectParams multiSelectParams: // handle multiple selections break; case AdaptyOnboardingsInputParams inputParams: // handle text input break; case AdaptyOnboardingsDatePickerParams datePickerParams: // handle date selection break; } } // ... other interface methods } ``` 参数说明: | 参数 | 描述 | |----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `elementId` | 输入元素的唯一标识符。您可以用它在保存时将问题与答案关联起来。 | | `@params` | 用户输入数据对象。可以是以下类型之一。 | | `AdaptyOnboardingsSelectParams` | 从选项中单选。包含 `Id`、`Value`、`Label` | | `AdaptyOnboardingsMultiSelectParams` | 从选项中多选。包含 `Params` 列表(每个包含 `Id`、`Value`、`Label`)
• `input`:包含 `type`、`value` 的对象
• `datePicker`:包含 `day`、`month`、`year` 的对象 | | `AdaptyOnboardingsInputParams` | 文本输入框。包含 `Input`,可以是 `AdaptyOnboardingsTextInput`、`AdaptyOnboardingsEmailInput` 或 `AdaptyOnboardingsNumberInput` | | `AdaptyOnboardingsDatePickerParams` | 日期选择。包含可为空的 `Day`、`Month`、`Year` |
已保存数据示例(您的实现可能有所不同) ```javascript // Example of a saved select action { "elementId": "preference_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "preferences_screen", "screenIndex": 1, "screensTotal": 3 }, "params": { "type": "select", "value": { "id": "option_1", "value": "premium", "label": "Premium Plan" } } } // Example of a saved multi-select action { "elementId": "interests_selector", "meta": { "onboardingId": "onboarding_123", "screenClientId": "interests_screen", "screenIndex": 2, "screensTotal": 3 }, "params": { "type": "multiSelect", "value": [ { "id": "interest_1", "value": "sports", "label": "Sports" }, { "id": "interest_2", "value": "music", "label": "Music" } ] } } // Example of a saved input action { "elementId": "name_input", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "input", "value": { "type": "text", "value": "John Doe" } } } // Example of a saved date picker action { "elementId": "birthday_picker", "meta": { "onboardingId": "onboarding_123", "screenClientId": "profile_screen", "screenIndex": 0, "screensTotal": 3 }, "params": { "type": "datePicker", "value": { "day": 15, "month": 6, "year": 1990 } } } ```
## 使用场景 \{#use-cases\} ### 用数据丰富用户画像 \{#enrich-user-profiles-with-data\} 如果您希望立即将输入数据与用户画像关联,避免重复询问相同信息,则需要在处理操作时使用输入数据[更新用户画像](unity-setting-user-attributes)。 例如,您要求用户在 ID 为 `name` 的文本框中输入姓名,并希望将该字段的值设为用户的名字;同时要求用户在 `email` 字段中输入电子邮件。在您的应用代码中,可以如下实现: ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { if (@params is AdaptyOnboardingsInputParams inputParams) { var builder = new AdaptyProfileParameters.Builder(); switch (elementId) { case "name": if (inputParams.Input is AdaptyOnboardingsTextInput textInput) { builder.SetFirstName(textInput.Value); } break; case "email": if (inputParams.Input is AdaptyOnboardingsEmailInput emailInput) { builder.SetEmail(emailInput.Value); } break; } Adapty.UpdateProfile(builder.Build(), (error) => { if (error != null) { // handle the error } }); } } // ... other interface methods } ``` ### 根据答案自定义付费墙 \{#customize-paywalls-based-on-answers\} 通过在用户引导中使用测验,您还可以根据用户完成用户引导后的情况自定义向其展示的付费墙。 例如,您可以询问用户的运动经验,并向不同用户群体展示不同的 CTA 和产品。 1. 在用户引导编辑工具中[添加测验](onboarding-quizzes),并为其选项分配有意义的 ID。 2. 根据 ID 处理测验响应,并为用户[设置自定义属性](unity-setting-user-attributes)。 ```csharp showLineNumbers title="Unity" public class OnboardingManager : MonoBehaviour, AdaptyOnboardingsEventsListener { public void OnboardingViewOnStateUpdatedAction( AdaptyUIOnboardingView view, AdaptyUIOnboardingMeta meta, string elementId, AdaptyOnboardingsStateUpdatedParams @params ) { if (@params is AdaptyOnboardingsSelectParams selectParams) { var builder = new AdaptyProfileParameters.Builder(); switch (elementId) { case "experience": // set custom attribute 'experience' with the selected value (beginner, amateur, pro) builder.SetCustomStringAttribute("experience", selectParams.Value); break; } Adapty.UpdateProfile(builder.Build(), (error) => { if (error != null) { // handle the error } }); } } // ... other interface methods } ``` 3. 为每个自定义属性值[创建市场细分](segments)。 4. 创建一个[版位](placements),并为您创建的每个市场细分添加[目标受众](audience)。 5. 在您的应用代码中为该版位[展示付费墙](unity-paywalls)。如果您的用户引导中有一个按钮用于打开付费墙,请将付费墙代码作为[该按钮操作的响应](unity-handling-onboarding-events#opening-a-paywall)来实现。 --- # File: unity-sdk-call-order --- --- title: "Unity SDK 中的调用顺序" description: "通过按照正确顺序调用 Adapty SDK 方法,避免付费权限丢失、归因缺失以及偶发的 #2002 错误。" --- `Adapty.Activate()` 必须在调用任何其他 Adapty SDK 方法之前完成。在其完成回调触发之前,SDK 没有任何状态。在 `Activate()` 之前或与其并行发起的任何调用都会失败,并返回 [`#2002 notActivated`](unity-handle-errors#custom-network-codes) 错误。 如果你的应用需要对用户进行身份验证,并且在启动后才能获取到 customer user ID,请在获取到该 ID 时调用 `Adapty.Identify()`。在 `Identify` 回调触发之前,不要调用任何用户操作相关的方法。与该调用产生竞争的请求,要么会以 [`#3006 profileWasChanged`](unity-handle-errors#custom-network-codes) 错误失败,要么会落到激活时创建的匿名用户画像上。一旦发生这种情况,归因数据、`appsflyer_id` 等 MMP ID 以及安装归属并不总能转移到已识别的用户画像上。如果你的应用不需要用户身份验证,则跳过 `Identify`,继续使用匿名用户画像即可。 MMP 和分析类 SDK(AppsFlyer、Adjust、Branch、PostHog)遵循同样的规则。请先初始化它们,等待其 UID 回调后再调用 `Adapty.Activate`。否则 MMP ID 会绑定到一个短暂的匿名用户画像上,不一定能迁移到已识别的用户画像。关于 AppsFlyer 的具体说明,请参阅 [AppsFlyer](appsflyer)。 ## 正确的操作顺序 \{#the-correct-order\} 你的操作路径取决于两件事:何时获取到 customer user ID,以及是否使用了 MMP 或数据分析 SDK。 - **步骤 2 和 5**:所有应用必须完成。初始化 SDK,然后调用 SDK 方法。 - **步骤 1 和 3**:仅在集成 MMP 或数据分析 SDK(AppsFlyer、Adjust、Branch、PostHog)时需要。 - **步骤 4**:仅在应用需要用户登录、且在启动后才能获取 customer user ID 时需要。 如果在应用启动时已知客户用户 ID,可直接在 `Activate()` 中传入(步骤 2a)。这条路径不会创建匿名用户画像,因此步骤 4 无需执行。 | 步骤 | 调用 | 时机 | 说明 | |------|---------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | 1 | 初始化 MMP 或分析 SDK(AppsFlyer、Adjust、PostHog、Branch) | 应用启动,最先执行 | 等待 MMP 的 UID 回调,例如 `getAppsFlyerId`。 | | 2a | `Adapty.Activate(builder.Build(), ...)` 并在 builder 上设置 `SetCustomerUserId` | 应用启动,步骤 1 之后,如果已有 customer user ID | 推荐方式,不会创建匿名用户画像。 | | 2b | `Adapty.Activate(builder.Build(), ...)` 不设置 `SetCustomerUserId` | 应用启动,步骤 1 之后,如果没有 customer user ID(或从不收集) | Adapty 会创建一个匿名用户画像。 | | 3 | 为每个 MMP 调用 `Adapty.SetIntegrationIdentifier(key, value, callback)` | 步骤 2 之后,任何用户操作调用之前 | 必须执行,确保 MMP ID 关联到正确的用户画像。 | | 4 | `Adapty.Identify("YOUR_USER_ID", callback)` | 步骤 3 之后(若无 MMP 则在步骤 2 之后),步骤 5 之前——仅适用于路径 2b 且需要身份验证时 | 等待完成回调。在 `Identify` 执行期间并发调用会产生 `#3006 profileWasChanged` 错误。 | | 5 | `GetPaywall`、`GetPaywallProducts`、`RestorePurchases`、`MakePurchase`、`UpdateAttribution`、`UpdateProfile` | 如果调用了 `Identify`,则在步骤 4 之后;否则在步骤 3 之后(若无 MMP 则在步骤 2 之后) | 这些调用需要一个稳定的用户画像。 | :::important 跳过这些步骤会导致回访用户失去高级访问权限、用户画像缺少 `appsflyer_id`,以及付费墙按错误的目标受众返回。 ::: ## Web2app 与网页漏斗安装 \{#web2app-and-web-funnel-installs\} 如果用户在网页端(Stripe、Paddle)完成购买后再安装原生应用,设备首次调用 `Activate()` 时会创建一个新的匿名用户画像,该画像不会与网页端的用户画像关联。如果能在应用启动前(通过登录流程或安装来源追踪)拿到客户用户 ID,请直接传入 `Activate()`;否则,网页端的购买记录在设备上将不可见,直到你调用 `Identify("YOUR_USER_ID")` 再调用 `RestorePurchases` 才能同步。 关于每次网页端结账时需要传入的元数据,请参阅: - [Stripe](stripe) - [Paddle](paddle) --- # File: unity-optimize-paywall-fetching --- --- title: "在 Unity SDK 中优化付费墙获取" description: "可靠地获取 Adapty 付费墙:适用于 Unity 的时机、缓存与备用方案。" --- 在 Unity 中可靠地获取付费墙需要做到三点:快速渲染、返回面向目标受众的付费墙,以及在网络较慢时优雅降级。以下规则涵盖了实现这一目标所需的时机、缓存与备用方案。 :::tip 以下规则假设 `Adapty.Activate()` 和 `Adapty.Identify()` 已经执行完成。详见 [Unity SDK 调用顺序](unity-sdk-call-order)。 ::: ## 规则与注意事项 \{#rules-and-pitfalls\} | 应该这样做 | 不应该这样做 | 原因 | |---|---|---| | 仅在即将展示付费墙时才获取对应版位。 | 在启动时并发预取所有版位。 | 批量预取会阻塞主线程,导致启动期间出现黑屏。 | | 在归因数据有机会解析后再调用 `GetPaywall`——例如在 `Activate` 之后等待 1–2 秒,或等 `OnLoadLatestProfile` 触发后再调用。 | 在 `Awake()` 中调用 `GetPaywall`。 | 此时归因数据尚未到达。付费墙会按默认目标受众解析,悄悄绕过市场细分和 ASA 个性化逻辑。 | | 设置 `loadTimeout`,并为每个版位配置[备用付费墙](fallback-paywalls)。 | 无限等待 `GetPaywall` 返回。 | 没有超时限制时,网络较差的用户会看到空白屏幕,直到网络恢复——或者直接关掉应用。 | 有关 `fetchPolicy` 和 `loadTimeout` 参数的说明,请参阅[获取付费墙和产品](fetch-paywalls-and-products-unity);有关如何选择合适版位的信息,请参阅[版位](placements)。 ## 针对弱网环境进行优化 \{#tune-for-poor-connectivity\} 对于网络连接持续较差的市场(农村地区、交通途中、受路由影响的地区): - 除首次请求外,每次获取时将 `fetchPolicy` 设置为 `AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad`。 - 在 Adapty 看板中为每个版位配置[备用付费墙](fallback-paywalls)。 - 将 `loadTimeout` 设置为 3–5 秒,超时后接受备用付费墙。 - 不要将付费墙的展示逻辑阻塞在 `GetProfile` 上。独立调用 `GetPaywall`,避免因用户画像加载缓慢而影响界面显示。 --- # File: unity-test --- --- title: "在 Unity SDK 中测试与发布" description: "了解如何使用 Adapty SDK 测试和发布您的 Unity 应用。" --- 如果您已经在 Unity 应用中集成了 Adapty SDK,您需要测试所有内容是否正确配置,以及在 iOS 和 Android 平台上购买流程是否按预期运行。这包括使用 Apple 沙盒环境和 Google Play 测试环境测试 SDK 集成和实际购买流程。 ## 测试您的应用 \{#test-your-app\} 如需全面测试应用内购买,请参阅我们针对各平台的测试指南:[iOS 测试指南](test-purchases-in-sandbox) 和 [Android 测试指南](testing-on-android)。 ## 准备发布 \{#prepare-for-release\} 在将应用提交到商店之前,请遵循[发布检查清单](release-checklist)以确认: - 商店连接和服务器通知已配置 - 购买完成并已上报至 Adapty - 访问等级解锁和恢复功能正常 - 隐私和审核要求已满足 --- # File: InvalidProductIdentifiers-unity --- --- title: "修复 Unity SDK 中的 Code-1000 noProductIDsFound 错误" description: "解决在 Adapty 中管理订阅时出现的无效产品标识符错误。" --- 1000 代码错误 `noProductIDsFound` 表示你在付费墙中请求的产品虽然已在 App Store 中列出,但目前无法购买。该错误有时会附带 `InvalidProductIdentifiers` 警告。如果只出现警告而没有错误,可以忽略。 如果你遇到了 `noProductIDsFound` 错误,请按以下步骤排查: ## 步骤 1. 检查 Bundle ID \{#step-2-check-bundle-id\} 1. 打开 [App Store Connect](https://appstoreconnect.apple.com/apps)。选择你的应用,进入 **General** → **App Information** 页面。 2. 在 **General Information** 子区块中复制 **Bundle ID**。 3. 在 Adapty 顶部菜单中打开 [**App settings** -> **iOS SDK** 标签页](https://app.adapty.io/settings/ios-sdk),将复制的值粘贴到 **Bundle ID** 字段中。 4. 返回 App Store Connect 中的 **App information** 页面,复制其中的 **Apple ID**。 5. 在 Adapty 看板的 [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) 页面中,将该 ID 粘贴到 **Apple app ID** 字段。 ## 步骤 2:检查产品 \{#step-3-check-products\} 1. 前往 **App Store Connect**,在左侧菜单中导航至 [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions)。 2. 点击订阅组名称,你将在 **Subscriptions** 区域看到所有产品。 3. 确认你要测试的产品已标记为 **Ready to Submit**。 4. 将表格中的产品 ID 与 Adapty 看板中 [**Products**](https://app.adapty.io/products) 标签页里的产品 ID 进行比对。如果 ID 不匹配,请从表格中复制该产品 ID,然后在 Adapty 看板中[创建产品](create-product)。 ## 第 3 步:检查产品可用性 \{#step-4-check-product-availability\} 1. 返回 **App Store Connect**,打开同一个 **Subscriptions** 页面。 2. 点击订阅组名称,查看你的产品。 3. 选择您要测试的产品。 4. 滚动至 **Availability** 部分,确认所有所需国家和地区均已列出。 ## 第 4 步:检查产品价格 \{#step-5-check-product-prices\} 1. 再次前往 **App Store Connect** 中的 **Monetization** → **Subscriptions** 部分。 2. 点击订阅组名称。 3. 选择您要测试的产品。 4. 向下滚动至 **Subscription Pricing**,展开 **Current Pricing for New Subscribers** 部分。 5. 确保所有必填价格均已填写。 ## 第 5 步:确认应用付费状态、银行账户及税务表格均已生效 \{#step-5-check-app-paid-status-bank-account-and-tax-forms-are-active\} 1. 在 [**App Store Connect**](https://appstoreconnect.apple.com/) 主页,点击 **Business**。 2. 选择你的公司名称。 3. 向下滚动,确认你的 **Paid Apps Agreement**、**Bank Account** 和 **Tax forms** 均显示为 **Active**。 按照以上步骤,您应该能够解决 `InvalidProductIdentifiers` 警告,并让您的产品在商店中上线。 ## 第 6 步:若产品卡住,请重新创建 \{#step-6-recreate-the-product-if-its-stuck\} 即使第 1–5 步全部通过——状态为 `Approved`、Bundle ID 匹配、API 密钥有效——SDK 仍然可能返回 `1000 noProductIDsFound`。这种情况下,产品可能卡在了 Apple 的注册表中。Apple 的产品注册表偶尔会进入一种状态:产品在 App Store Connect 界面中存在,但无法通过 StoreKit 的查找路径被识别。 在 App Store Connect 中删除该产品,然后用相同的产品 ID 重新创建。重新创建后,最长需要等待 24 小时才能完成同步。 --- # File: cantMakePayments-unity --- --- title: "修复 Unity SDK 中 Code-1003 cantMakePayment 错误" description: "解决在 Adapty 中管理订阅时出现的支付错误。" --- 1003 错误 `cantMakePayments` 表示该设备无法进行应用内购买。 如果你遇到了 `cantMakePayments` 错误,通常是由以下原因之一导致的: - 设备限制:该错误与 Adapty 无关。请参阅下方的修复方法。 - 观察者模式配置:`makePurchase` 方法与观察者模式不能同时使用。请参阅下方相关章节。 ## 问题:设备限制 \{#issue-device-restrictions\} | 问题 | 解决方案 | |---------------------------|---------------------------------------------------------| | 屏幕使用时间限制 | 在 [Screen Time](https://support.apple.com/en-us/102470) 中关闭应用内购买限制 | | 账户被暂停 | 联系 Apple 支持以解决账户问题 | | 地区限制 | 使用受支持地区的 App Store 账户 | ## 问题:同时使用观察者模式和 makePurchase \{#issue-using-both-observer-mode-and-makepurchase\} 如果你使用 `makePurchases` 来处理购买,则无需启用观察者模式。[观察者模式](observer-vs-full-mode) 仅在你自行实现购买逻辑时才需要使用。 因此,如果你正在使用 `makePurchase`,可以安全地从 SDK 激活代码中移除对观察者模式的启用。 --- # File: migration-to-unity-sdk-314 --- --- title: "迁移 Adapty Unity SDK 至 v3.14" description: "迁移至 Adapty Unity SDK v3.14,获得更好的性能和新的变现功能。" --- Adapty SDK 3.14.0 是一个主要版本,带来了一些改进,但可能需要你执行一些迁移步骤: 1. 为付费墙事件添加独立的事件监听器。 2. 将 `AdaptyUI.CreateView` 重命名为 `AdaptyUI.CreatePaywallView` 及相关方法。 3. 更新 `MakePurchase` 方法,改用 `AdaptyPurchaseParameters` 替代单独参数。 4. 将 `SetFallbackPaywalls` 替换为 `SetFallback` 方法。 5. 更新付费墙属性访问方式,改用 `AdaptyPlacement`。 6. 更新远程配置访问方式,改用 `AdaptyRemoteConfig` 对象。 7. 将 `AdaptyPaywall` 模型中的 `VendorProductIds` 替换为 `ProductIdentifiers`。 8. 更新 `GetPaywall` 的获取策略,改用 `AdaptyFetchPolicy`。 ## 付费墙事件的独立事件监听器 \{#separate-event-listener-for-paywall-events\} 如果你展示的付费墙是通过[付费墙编辑工具](adapty-paywall-builder)设计的,付费墙视图事件现在使用专用的 `AdaptyPaywallsEventsListener` 接口和 `SetPaywallsEventsListener` 方法。核心 `AdaptyEventListener` 接口仍用于用户画像更新和安装详情。 ```diff showLineNumbers using UnityEngine; using AdaptySDK; public class AdaptyListener : MonoBehaviour, - AdaptyEventListener { + AdaptyEventListener, + AdaptyPaywallsEventsListener { void Start() { Adapty.SetEventListener(this); + Adapty.SetPaywallsEventsListener(this); } // AdaptyEventListener methods public void OnLoadLatestProfile(AdaptyProfile profile) { } public void OnInstallationDetailsSuccess(AdaptyInstallationDetails details) { } public void OnInstallationDetailsFail(AdaptyError error) { } + // AdaptyPaywallsEventsListener methods + // Implement paywall event handlers here } ``` [了解有关处理付费墙事件的更多信息](unity-handling-events)。 ## 重命名视图创建与展示方法 \{#rename-view-creation-and-presentation-methods\} 视图创建和展示方法已重命名: ```diff showLineNumbers using AdaptySDK; - AdaptyUI.CreateView(paywall, parameters, (view, error) => { + AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => { if (error != null) { // handle the error return; } - AdaptyUI.PresentView(view, (error) => { + AdaptyUI.PresentPaywallView(view, (error) => { // handle the error }); }); } ``` 同样,关闭方法也已重命名: ```diff showLineNumbers - AdaptyUI.DismissView(view, (error) => { + AdaptyUI.DismissPaywallView(view, (error) => { // handle the error }); ``` ## 更新 MakePurchase 方法 \{#update-makepurchase-method\} `MakePurchase` 方法现在使用 `AdaptyPurchaseParameters`,替代了原来独立的 `subscriptionUpdateParams` 和 `isOfferPersonalized` 参数。这样做可以提升类型安全性,并为未来扩展购买参数预留空间。 ```diff showLineNumbers using AdaptySDK; void MakePurchase( AdaptyPaywallProduct product, AdaptySubscriptionUpdateParameters subscriptionUpdate, bool? isOfferPersonalized ) { - Adapty.MakePurchase(product, subscriptionUpdate, isOfferPersonalized, (result, error) => { + var parameters = new AdaptyPurchaseParametersBuilder() + .SetSubscriptionUpdateParams(subscriptionUpdate) + .SetIsOfferPersonalized(isOfferPersonalized) + .Build(); + + Adapty.MakePurchase(product, parameters, (result, error) => { switch (result.Type) { case AdaptyPurchaseResultType.Pending: // handle pending purchase break; case AdaptyPurchaseResultType.UserCancelled: // handle purchase cancellation break; case AdaptyPurchaseResultType.Success: var profile = result.Profile; // handle successful purchase break; default: break; } }); } ``` 如果不需要额外参数,可以直接使用: ```csharp showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { Adapty.MakePurchase(product, (result, error) => { // handle purchase result }); } ``` ## 更新备用付费墙方法 \{#update-fallback-method\} :::important 升级到 Unity SDK 3.14 时,你需要从 Adapty 看板下载新的备用文件,并替换项目中的现有文件。 ::: 设置备用付费墙的方法已更新。`SetFallbackPaywalls` 方法已重命名为 `SetFallback`: ```diff showLineNumbers using AdaptySDK; void SetFallBackPaywalls() { #if UNITY_IOS var assetId = "adapty_fallback_ios.json"; #elif UNITY_ANDROID var assetId = "adapty_fallback_android.json"; #else var assetId = ""; #endif - Adapty.SetFallbackPaywalls(assetId, (error) => { + Adapty.SetFallback(assetId, (error) => { // handle the error }); } ``` 请查看 [在 Unity 中使用备用付费墙](unity-use-fallback-paywalls) 页面中的完整代码示例。 ## 更新付费墙属性访问方式 \{#update-paywall-property-access\} 以下属性已从 `AdaptyPaywall` 移至 `AdaptyPlacement`: ```diff showLineNumbers using AdaptySDK; void ProcessPaywall(AdaptyPaywall paywall) { - var abTestName = paywall.ABTestName; - var audienceName = paywall.AudienceName; - var revision = paywall.Revision; - var placementId = paywall.PlacementId; + var abTestName = paywall.Placement.ABTestName; + var audienceName = paywall.Placement.AudienceName; + var revision = paywall.Placement.Revision; + var placementId = paywall.Placement.Id; } ``` ## 更新远程配置访问方式 \{#update-remote-config-access\} 远程配置属性已被重构为 `AdaptyRemoteConfig` 对象,以提供更好的组织结构: ```diff showLineNumbers using AdaptySDK; void ProcessRemoteConfig(AdaptyPaywall paywall) { - var remoteConfigString = paywall.RemoteConfigString; - var locale = paywall.Locale; - var remoteConfigDict = paywall.RemoteConfig; + var remoteConfigString = paywall.RemoteConfig.Data; + var locale = paywall.RemoteConfig.Locale; + var remoteConfigDict = paywall.RemoteConfig.Dictionary; } ``` ## 更新 AdaptyPaywall 模型用法 \{#update-adaptypaywall-model-usage\} `VendorProductIds` 属性已被弃用,请改用 `ProductIdentifiers`。新属性返回 `AdaptyProductIdentifier` 对象,而非简单的字符串,能提供更结构化的产品信息。 ```diff showLineNumbers using AdaptySDK; void ProcessPaywallProducts(AdaptyPaywall paywall) { - var productIds = paywall.VendorProductIds; - foreach (var vendorId in productIds) { - // use vendorId - } + var productIdentifiers = paywall.ProductIdentifiers; + foreach (var productId in productIdentifiers) { + var vendorId = productId.VendorProductId; + // use vendorId + } } ``` `AdaptyProductIdentifier` 对象通过 `VendorProductId` 属性提供对厂商产品 ID 的访问,在保持原有功能的同时,为未来的功能扩展提供了更清晰的结构。 ## 更新 GetPaywall 获取策略 \{#update-getpaywall-fetch-policy\} `GetPaywall` 方法中的 `fetchPolicy` 参数类型已从 `AdaptyPaywallFetchPolicy` 更改为 `AdaptyPlacementFetchPolicy`。此更改统一了 SDK 中获取策略的使用方式。 ```diff showLineNumbers using AdaptySDK; void GetPaywall(string placementId) { - Adapty.GetPaywall(placementId, AdaptyPaywallFetchPolicy.ReloadRevalidatingCacheData, null, (paywall, error) => { + Adapty.GetPaywall(placementId, AdaptyPlacementFetchPolicy.ReloadRevalidatingCacheData, null, (paywall, error) => { // handle the result }); } ``` --- # File: migration-to-unity-sdk-34 --- --- title: "迁移 Adapty Unity SDK 至 v3.4" description: "迁移至 Adapty Unity SDK v3.4,获得更好的性能与全新的变现功能。" --- Adapty SDK 3.4.0 是一个重要版本,引入了需要你进行迁移操作的改进内容。 ## 更新备用付费墙文件 \{#update-fallback-paywall-files\} 更新您的备用付费墙文件,以确保与新 SDK 版本的兼容性: 1. 从 Adapty 看板[下载更新后的备用付费墙文件](fallback-paywalls)。 2. 将移动应用中的现有备用付费墙[替换为新文件](unity-use-fallback-paywalls)。 ## 更新 Observer Mode 的实现方式 \{#update-implementation-of-observer-mode\} 如果你正在使用 Observer Mode,请确保更新其实现方式。 之前,向 Adapty 上报交易时使用的是不同的方法。在新版本中,Android 和 iOS 应均统一使用 `reportTransaction` 方法。该方法会明确地将每笔交易上报给 Adapty,确保其被正确识别。如果使用了付费墙,请传入 variation ID 以将交易与付费墙关联。 :::warning **不要跳过交易上报!** 如果不调用 `reportTransaction`,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会发送到集成渠道。 ::: ```diff showLineNumbers - #if UNITY_ANDROID && !UNITY_EDITOR - Adapty.RestorePurchases((profile, error) => { - // handle the error - }); - #endif Adapty.ReportTransaction( "YOUR_TRANSACTION_ID", "PAYWALL_VARIATION_ID", // optional (error) => { // handle the error }); ``` --- # File: migration-to-unity330 --- --- title: "迁移 Adapty Unity SDK 至 v3.3" description: "迁移至 Adapty Unity SDK v3.3,获得更好的性能和全新的变现功能。" --- Adapty SDK 3.3.0 是一个重要版本,带来了一些改进,但可能需要你执行一些迁移步骤。 1. 升级至 Adapty SDK v3.3.x。 2. 重命名了 Adapty SDK 中 Adapty 和 AdaptyUI 模块的多个类、属性和方法。 3. 从现在起,`SetLogLevel` 方法接受一个回调作为参数。 4. 从现在起,`PresentCodeRedemptionSheet` 方法接受一个回调作为参数。 5. 更改付费墙视图的创建方式。 6. 移除 `GetProductsIntroductoryOfferEligibility` 方法。 7. 将备用付费墙保存为独立文件(每个平台一个),放在 `Assets/StreamingAssets/` 目录下,并将文件名传递给 `SetFallbackPaywalls` 方法。 8. 更新购买流程。 9. 更新付费墙编辑工具事件的处理方式。 10. 更新付费墙编辑工具付费墙错误的处理方式。 11. 更新 Adjust、Amplitude、AppMetrica、Appsflyer、Branch、Firebase 和 Google Analytics、Mixpanel、OneSignal、Pushwoosh 的集成配置。 13. 更新观察者模式的实现方式。 14. 使用显式 `Activate` 调用更新 Unity 插件初始化。 ## 将 Adapty Unity SDK 升级到 3.3.x \{#upgrade-adapty-unity-sdk-to-33x\} 在此版本之前,Adapty SDK 是确保 Adapty 在应用中正常运行所必需的核心 SDK,而 AdaptyUI SDK 则是可选 SDK,仅在使用 Adapty 付费墙编辑工具时才需要安装。 从 3.3.0 版本开始,AdaptyUI SDK 已被弃用,AdaptyUI 已作为模块合并到 Adapty SDK 中。由于此变更,您需要移除 AdaptyUI SDK 并重新安装 Adapty SDK。 1. 从项目中移除 **AdaptySDK** 和 **AdaptyUISDK** 的包依赖项。 2. 删除 **AdaptySDK** 和 **AdaptyUISDK** 文件夹。 3. 按照 [Unity 的 Adapty SDK 安装与配置](sdk-installation-unity) 页面的说明,重新导入 AdaptySDK 包。 ## 重命名 \{#renamings\} 1. 在 Adapty 模块中重命名: | 旧版本 | 新版本 | | ------------------------- | ------------------------ | | Adapty.sdkVersion | Adapty.SDKVersion | | Adapty.LogLevel | AdaptyLogLevel | | Adapty.Paywall | AdaptyPaywall | | Adapty.PaywallFetchPolicy | AdaptyPaywallFetchPolicy | | PaywallProduct | AdaptyPaywallProduct | | Adapty.Profile | AdaptyProfile | | Adapty.ProfileParameters | AdaptyProfileParameters | | ProfileGender | AdaptyProfileGender | | Error | AdaptyError | 2. 在 AdaptyUI 模块中重命名: | 旧版本 | 新版本 | | ------------------ | ------------------ | | CreatePaywallView | CreateView | | PresentPaywallView | PresentView | | DismissPaywallView | DismissView | | AdaptyUI.View | AdaptyUIView | | AdaptyUI.Action | AdaptyUIUserAction | ## 更改 SetLogLevel 方法 \{#change-the-setloglevel-method\} 从现在起,`SetLogLevel` 方法接受回调作为参数。 ```diff showLineNumbers - Adapty.SetLogLevel(Adapty.LogLevel.Verbose); + Adapty.SetLogLevel(Adapty.LogLevel.Verbose, null); // or you can pass the callback to handle the possible error ``` ## 更改 PresentCodeRedemptionSheet 方法 \{#change-the-presentcoderedemptionsheet-method\} 从现在起,`PresentCodeRedemptionSheet` 方法接受回调作为参数。 ```diff showLineNumbers - Adapty.PresentCodeRedemptionSheet(); + Adapty.PresentCodeRedemptionSheet(null); // or you can pass the callback to handle the possible error ``` ## 更改付费墙视图的创建方式 \{#change-how-the-paywall-view-is-created\} 完整代码示例请参阅[获取使用付费墙编辑工具设计的付费墙视图配置](unity-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder)。 ```diff showLineNumbers + var parameters = new AdaptyUICreateViewParameters() + .SetPreloadProducts(true); - AdaptyUI.CreatePaywallView( + AdaptyUI.CreateView( paywall, - preloadProducts: true, + parameters, (view, error) => { // use the view }); ``` ## 移除 GetProductsIntroductoryOfferEligibility 方法 \{#remove-the-getproductsintroductoryoffereligibility-method\} 在 Adapty iOS SDK 3.3.0 之前,无论用户是否符合资格,产品对象始终包含优惠信息。您必须在使用优惠之前手动检查资格。 现在,产品对象仅在用户符合资格时才包含优惠信息。这意味着您不再需要检查资格——如果存在优惠,则用户符合资格。 ## 备用付费墙的传入方式更新 \{#update-method-for-providing-fallback-paywalls\} 在此版本之前,备用付费墙以序列化 JSON 的形式传入。从 v 3.3.0 开始,机制发生了变化: 1. 将备用付费墙保存到 `/Assets/StreamingAssets/` 目录下的文件中,Android 和 iOS 各一个文件。 2. 将文件名传入 `SetFallbackPaywalls` 方法。 你的代码需要做如下修改: ```diff showLineNumbers using AdaptySDK; void SetFallBackPaywalls() { + #if UNITY_IOS + var assetId = "adapty_fallback_ios.json"; + #elif UNITY_ANDROID + var assetId = "adapty_fallback_android.json"; + #else + var assetId = ""; + #endif - Adapty.SetFallbackPaywalls("FALLBACK_PAYWALLS_JSON_STRING", (error) => { + Adapty.SetFallbackPaywalls(assetId, (error) => { // handle the error }); } ``` 完整代码示例请参阅 [在 Unity 中使用备用付费墙](unity-use-fallback-paywalls) 页面。 ## 更新购买功能 \{#update-making-purchase\} 之前,取消的购买和待处理的购买被视为错误,分别返回 `PaymentCancelled` 和 `PendingPurchase` 错误码。 现在引入了新的 `AdaptyPurchaseResultType` 类,用于处理已取消、成功和待处理的购买。请按以下方式更新购买相关代码: ```diff showLineNumbers using AdaptySDK; void MakePurchase(AdaptyPaywallProduct product) { - Adapty.MakePurchase(product, (profile, error) => { - // handle successfull purchase + Adapty.MakePurchase(product, (result, error) => { + switch (result.Type) { + case AdaptyPurchaseResultType.Pending: + // handle pending purchase + break; + case AdaptyPurchaseResultType.UserCancelled: + // handle purchase cancellation + break; + case AdaptyPurchaseResultType.Success: + var profile = result.Profile; + // handle successful purchase + break; + default: + break; } }); } ``` 查看[在移动应用中进行购买](unity-making-purchases)页面中的最终代码示例。 ## 更新付费墙编辑工具事件处理方式 \{#update-handling-of-paywall-builder-events\} 取消和待处理的购买不再被视为错误,所有这些情况现在通过 `PaywallViewDidFinishPurchase` 方法处理。 1. 删除对取消购买事件的处理。 2. 按以下方式更新成功购买事件的处理: ```diff showLineNumbers - public void OnFinishPurchase( - AdaptyUI.View view, - Adapty.PaywallProduct product, - Adapty.Profile profile - ) { } + public void PaywallViewDidFinishPurchase( + AdaptyUIView view, + AdaptyPaywallProduct product, + AdaptyPurchaseResult purchasedResult + ) { } ``` 3. 更新操作处理方式: ```diff showLineNumbers - public void OnPerformAction( - AdaptyUI.View view, - AdaptyUI.Action action - ) { + public void PaywallViewDidPerformAction( + AdaptyUIView view, + AdaptyUIUserAction action + ) { switch (action.Type) { - case AdaptyUI.ActionType.Close: + case AdaptyUIUserActionType.Close: view.Dismiss(null); break; - case AdaptyUI.ActionType.OpenUrl: + case AdaptyUIUserActionType.OpenUrl: var urlString = action.Value; if (urlString != null { Application.OpenURL(urlString); } default: // handle other events break; } } ``` 4. 更新已开始购买的处理方式: ```diff showLineNumbers - public void OnSelectProduct( - AdaptyUI.View view, - Adapty.PaywallProduct product - ) { } + public void PaywallViewDidSelectProduct( + AdaptyUIView view, + string productId + ) { } ``` 5. 更新购买失败的处理方式: ```diff showLineNumbers - public void OnFailPurchase( - AdaptyUI.View view, - Adapty.PaywallProduct product, - Adapty.Error error - ) { } + public void PaywallViewDidFailPurchase( + AdaptyUIView view, + AdaptyPaywallProduct product, + AdaptyError error + ) { } ``` 6. 更新成功恢复购买事件的处理方式: 查看 [处理付费墙事件](unity-handling-events) 页面中的完整代码示例。 ## 更新付费墙编辑工具付费墙错误的处理方式 \{#update-handling-of-paywall-builder-paywall-errors\} 错误处理方式也有所变更,请根据以下指引更新你的代码。 1. 更新产品加载错误的处理方式: ```diff showLineNumbers - public void OnFailLoadingProducts( - AdaptyUI.View view, - Adapty.Error error - ) { } + public void PaywallViewDidFailLoadingProducts( + AdaptyUIView view, + AdaptyError error + ) { } ``` 2. 更新渲染错误的处理方式: ```diff showLineNumbers - public void OnFailRendering( - AdaptyUI.View view, - Adapty.Error error - ) { } + public void PaywallViewDidFailRendering( + AdaptyUIView view, + AdaptyError error + ) { } ``` ## 更新第三方集成 SDK 配置 \{#update-third-party-integration-sdk-configuration\} 从 Adapty Unity SDK 3.3.0 开始,我们更新了 `updateAttribution` 方法的公共 API。之前,它接受 `[AnyHashable: Any]` 字典,允许您直接从各种服务传递归因对象。现在,它需要 `[String: any Sendable]`,因此您需要在传递之前转换归因对象。 为确保集成在 Adapty Unity SDK 3.3.0 及更高版本中正常运行,请按以下各节所述更新以下集成的 SDK 配置。 ### Adjust 按照以下方式更新您的移动应用代码。完整代码示例请参阅 [Adjust 集成的 SDK 配置](adjust#connect-your-app-to-adjust)。 ```diff showLineNumbers - using static AdaptySDK.Adapty; using AdaptySDK; Adjust.GetAdid((adid) => { - Adjust.GetAttribution((attribution) => { - Dictionary data = new Dictionary(); - - data["network"] = attribution.Network; - data["campaign"] = attribution.Campaign; - data["adgroup"] = attribution.Adgroup; - data["creative"] = attribution.Creative; - - String attributionString = JsonUtility.ToJson(data); - Adapty.UpdateAttribution(attributionString, AttributionSource.Adjust, adid, (error) => { - // handle the error - }); + if (adid != null) { + Adapty.SetIntegrationIdentifier( + "adjust_device_id", + adid, + (error) => { + // handle the error + }); } }); Adjust.GetAttribution((attribution) => { Dictionary data = new Dictionary(); data["network"] = attribution.Network; data["campaign"] = attribution.Campaign; data["adgroup"] = attribution.Adgroup; data["creative"] = attribution.Creative; String attributionString = JsonUtility.ToJson(data); - Adapty.UpdateAttribution(attributionString, AttributionSource.Adjust, adid, (error) => { + Adapty.UpdateAttribution(attributionString, "adjust", (error) => { // handle the error }); }); ``` ### Amplitude 按如下方式更新你的移动应用代码。完整代码示例请参阅 [Amplitude 集成的 SDK 配置](amplitude#sdk-configuration)。 ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetAmplitudeUserId("YOUR_AMPLITUDE_USER_ID"); - builder.SetAmplitudeDeviceId(amplitude.getDeviceId()); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + Adapty.SetIntegrationIdentifier( + "amplitude_user_id", + "YOUR_AMPLITUDE_USER_ID", + (error) => { + // handle the error + }); + Adapty.SetIntegrationIdentifier( + "amplitude_device_id", + amplitude.getDeviceId(), + (error) => { + // handle the error + }); ``` ### AppMetrica 按照下方示例更新您的移动应用代码。完整代码示例请参阅 [AppMetrica 集成的 SDK 配置](appmetrica#sdk-configuration)。 ```diff showLineNumbers using AdaptySDK; - var deviceId = AppMetrica.GetDeviceId(); - if (deviceId != null { - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetAppmetricaProfileId("YOUR_ADAPTY_CUSTOMER_USER_ID"); - builder.SetAppmetricaDeviceId(deviceId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); - } + var deviceId = AppMetrica.GetDeviceId(); + if (deviceId != null { + Adapty.SetIntegrationIdentifier( + "appmetrica_device_id", + deviceId, + (error) => { + // handle the error + }); + + Adapty.SetIntegrationIdentifier( + "appmetrica_profile_id", + "YOUR_ADAPTY_CUSTOMER_USER_ID", + (error) => { + // handle the error + }); + } ``` ### AppsFlyer 按照以下方式更新你的移动应用代码。完整代码示例请参阅 [AppsFlyer 集成的 SDK 配置](appsflyer#connect-your-app-to-appsflyer)。 ```diff showLineNumbers using AppsFlyerSDK; using AdaptySDK; // before SDK initialization AppsFlyer.getConversionData(this.name); // in your IAppsFlyerConversionData void onConversionDataSuccess(string conversionData) { // It's important to include the network user ID - string appsFlyerId = AppsFlyer.getAppsFlyerId(); - Adapty.UpdateAttribution(conversionData, AttributionSource.Appsflyer, appsFlyerId, (error) => { + string appsFlyerId = AppsFlyer.getAppsFlyerId(); + + Adapty.SetIntegrationIdentifier( + "appsflyer_id", + appsFlyerId, + (error) => { // handle the error }); + + Adapty.UpdateAttribution( + conversionData, + "appsflyer", + (error) => { + // handle the error + }); } ``` ### Branch 按如下方式更新您的移动应用代码。完整代码示例请参阅 [Branch 集成的 SDK 配置](branch#connect-your-app-to-branch)。 ```diff showLineNumbers using AdaptySDK; - class YourBranchImplementation { - func initializeBranch() { - Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in - if let data { - Adapty.updateAttribution(data, source: .branch) - } - } - } - } + Branch.initSession(delegate(Dictionary parameters, string error) { + string attributionString = JsonUtility.ToJson(parameters); + + Adapty.UpdateAttribution( + attributionString, + "branch", + (error) => { + // handle the error + }); + }); ``` ### Firebase 和 Google Analytics \{#firebase-and-google-analytics\} 按照以下方式更新移动应用代码。完整代码示例请参阅 [Firebase 和 Google Analytics 集成的 SDK 配置](firebase-and-google-analytics)。 ```diff showLineNumbers // We suppose FirebaseAnalytics Unity Plugin is already installed using AdaptySDK; Firebase.Analytics .FirebaseAnalytics .GetAnalyticsInstanceIdAsync() .ContinueWithOnMainThread((task) => { if (!task.IsCompletedSuccessfully) { // handle error return; } var firebaseId = task.Result var builder = new Adapty.ProfileParameters.Builder(); - builder.SetFirebaseAppInstanceId(firebaseId); - - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error + Adapty.SetIntegrationIdentifier( + "firebase_app_instance_id", + firebaseId, + (error) => { + // handle the error }); }); ``` ### Mixpanel 按照以下步骤更新您的移动应用代码。完整代码示例请参阅 [Mixpanel 集成的 SDK 配置](mixpanel#sdk-configuration)。 ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetMixpanelUserId(Mixpanel.DistinctId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + var distinctId = Mixpanel.DistinctId; + if (distinctId != null) { + Adapty.SetIntegrationIdentifier( + "mixpanel_user_id", + distinctId, + (error) => { + // handle the error + }); + } ``` ### OneSignal 按以下方式更新您的移动应用代码。完整代码示例请参阅 [OneSignal 集成的 SDK 配置](onesignal#sdk-configuration)。 ```diff showLineNumbers using AdaptySDK; - using OneSignalSDK; - var pushUserId = OneSignal.Default.PushSubscriptionState.userId; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetOneSignalPlayerId(pushUserId); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + var distinctId = Mixpanel.DistinctId; + if (distinctId != null) { + Adapty.SetIntegrationIdentifier( + "mixpanel_user_id", + distinctId, + (error) => { + // handle the error + }); + } ``` ### Pushwoosh 按如下所示更新您的移动应用代码。完整代码示例请参阅 [Pushwoosh 集成的 SDK 配置](pushwoosh#sdk-configuration)。 ```diff showLineNumbers using AdaptySDK; - var builder = new Adapty.ProfileParameters.Builder(); - builder.SetPushwooshHWID(Pushwoosh.Instance.HWID); - Adapty.UpdateProfile(builder.Build(), (error) => { - // handle error - }); + Adapty.SetIntegrationIdentifier( + "pushwoosh_hwid", + Pushwoosh.Instance.HWID, + (error) => { + // handle the error + }); ``` ## 更新 Observer 模式实现 \{#update-observer-mode-implementation\} 更新付费墙与交易的关联方式。之前,您需要使用 `setVariationId` 方法来分配 `variationId`。现在,您可以在使用新的 `reportTransaction` 方法记录交易时直接传入 `variationId`。请参阅[在 Observer 模式下将付费墙与购买交易关联](report-transactions-observer-mode-unity)中的完整代码示例。 ```diff showLineNumbers // every time when calling transaction.finish() - Adapty.SetVariationForTransaction("", "", (error) => { - if(error != null) { - // handle the error - return; - } - - // successful binding - }); + Adapty.ReportTransaction( + "YOUR_TRANSACTION_ID", + "PAYWALL_VARIATION_ID", // optional + (error) => { + // handle the error + }); ``` ## 更新 Unity 插件初始化 \{#update-the-unity-plugin-initialization\} 从 Adapty Unity SDK 3.3.0 开始,在插件初始化期间需要显式调用 `Activate` 方法: ```csharp showLineNumbers Adapty.Activate(builder.Build(), (error) => { if (error != null) { // handle the error return; } }); ``` --- # File: migration-to-unity-sdk-v3 --- --- title: "将 Adapty Unity SDK 迁移至 v3.0" description: "迁移至 Adapty Unity SDK v3.0,获得更好的性能与新的变现功能。" --- Adapty SDK v3.0 带来了全新的 [Adapty 付费墙编辑工具](adapty-paywall-builder)支持——这是一款全新的无代码、易上手的付费墙创建工具。凭借极高的灵活性和丰富的设计能力,你的付费墙将变得更加高效、更具盈利潜力。 ## 升级流程 \{#upgrade-process\} Unity 的升级流程与其他平台相同: 1. 升级至 Adapty SDK v3.x 2. 将现有付费墙迁移至新版付费墙编辑工具 有关 Unity 专属的详细迁移说明,请参阅 [Unity SDK 安装指南](sdk-installation-unity),并遵循主迁移指南中概述的通用迁移步骤。 --- # File: unity-migration-guide --- --- title: "SDK 迁移指南" description: "Unity Adapty SDK 的迁移指南。" --- ## 迁移指南 \{#migration-guides\} ### [迁移至 Unity Adapty SDK 3.x 的指南](unity-sdk-migration-guides) 了解如何从旧版本迁移到 Unity Adapty SDK 3.x。 ## 新特性 \{#whats-new\} ### 版本 3.x \{#version-3x\} - 增强的付费墙展示 - 改进的错误处理 - 更好的 C# 支持 - 性能优化 ### 版本 2.x \{#version-2x\} - 新增用户引导功能 - 增强的分析能力 - 改进的购买流程 - 缺陷修复与稳定性提升 ## 重大变更 \{#breaking-changes\} ### 版本 3.x \{#version-3x-breaking\} - 更新了观察者 API - 更改了付费墙展示方法 - 修改了错误处理结构 ### 版本 2.x \{#version-2x-breaking\} - 更新了用户引导 API - 更改了用户画像结构 - 修改了购买流程 ## 迁移检查清单 \{#migration-checklist\} 迁移到新版本时: - [ ] 审查重大变更 - [ ] 更新 API 调用 - [ ] 测试所有功能 - [ ] 更新错误处理 - [ ] 验证分析跟踪 - [ ] 在所有平台上进行测试 --- # End of Documentation _Generated on: 2026-07-24T13:01:53.556Z_ _Successfully processed: 41/41 files_