### 在登录/注册时 \{#during-loginsignup\}
如果您在应用启动后识别用户(例如,在他们登录或注册后),请使用 `identify` 方法设置他们的 customer user ID。
- 如果您**之前从未使用过此 customer user ID**,Adapty 将自动将其与当前用户画像关联。
- 如果您**之前已使用此 customer user ID 识别过用户**,Adapty 将切换到与该 customer user ID 关联的用户画像。
:::important
每位用户的 customer user ID 必须唯一。如果将该参数值硬编码,所有用户将被视为同一人。
:::
等待 `identify` 的回调触发后,再调用其他 SDK 方法。并发调用可能会落到匿名用户画像上,而非已识别的用户画像。详见 [Android SDK 的调用顺序](android-sdk-call-order)。
默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方案,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。这种方式下用户可能无法获取最新数据,但无论网络状况如何,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制在本地存储流程和付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加速获取,并配备了独立的备用服务器以应对 CDN 不可用的情况。整套系统旨在确保你始终获取最新版本,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |此值限制该方法的超时时间。达到超时时间后,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能由多个请求组成。
对于 Android:你可以使用扩展函数创建 `TimeInterval`(例如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或者使用 `TimeInterval.seconds(5)`。如需不设限制,请使用 `TimeInterval.INFINITE`。
| | 参数 | 描述 | | :-------- | :---------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位信息、标识符(`id`、`variationId`)、名称、远程配置,以及 `hasViewConfiguration` 标志(用于指示该流程是否包含视图配置)。如需为预加载、自定义 UI 或程序化检查获取实际产品,请调用 `getPaywallProducts(flow)`。 | ## 获取视图配置 \{#fetch-the-view-configuration\} 获取流程或付费墙之后,通过 `flow.hasViewConfiguration` 检查其是否包含视图配置。该标志用于区分版位在 Adapty 看板中的设计方式: - **`true`** — 该版位是在 **Flow Builder**(流程)或 **付费墙编辑工具**(付费墙)中设计的,Adapty 会为您渲染 UI。请继续执行以下步骤,获取视图配置并[展示流程或付费墙](android-present-paywalls)。 - **`false`** — 该版位是没有编辑工具 UI 的自定义付费墙。[将其作为远程配置付费墙处理](present-remote-config-paywalls-android)。 :::important 请确保在 Flow Builder 中启用 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 :::可选
默认:设备语言
| [本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符,格式为语言代码,可包含一至两个以 `-` 分隔的子标签(例如 `en`、`pt-br`)。详见[本地化与语言代码](android-localizations-and-locale-codes)。 | | **loadTimeout** | 默认:5 秒 | 该参数限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。注意,在极少数情况下,由于该方法底层可能包含多个请求,实际超时时间可能略晚于 `loadTimeout` 指定的时间。 |可选
默认值:设备语言
| [本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符,格式为由 `-` 分隔的一个或两个子标签的语言代码(例如 `en`、`pt-br`)。详见[本地化与语言代码](android-localizations-and-locale-codes)。 | | **loadTimeout** | 默认值:5 秒 | 该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。请注意,在极少数情况下,由于该操作在底层可能包含多个请求,实际超时时间可能略晚于 `loadTimeout` 中指定的值。 |默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存存在时直接返回缓存数据。这种方式下用户获取的数据可能不是最新的,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全可靠的。
请注意,缓存在应用重启后仍会保留,仅在应用重新安装或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要自定义流程或付费墙中的图片和视频,请实现自定义资源。 主图和视频具有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,您可以通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,您需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,您可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在视频播放前显示预览图。 以下是通过简单字典提供自定义资源的示例: ```kotlin showLineNumbers val customAssets = AdaptyCustomAssets.of( "hero_image" to AdaptyCustomImageAsset.remote( url = "https://example.com/image.jpg", preview = AdaptyCustomImageAsset.file( FileLocation.fromAsset("images/hero_image_preview.png"), ) ), "hero_video" to AdaptyCustomVideoAsset.file( FileLocation.fromResId(requireContext(), R.raw.custom_video), preview = AdaptyCustomImageAsset.file( FileLocation.fromResId(requireContext(), R.drawable.video_preview), ), ), ) val flowView = AdaptyUI.getFlowView( activity, flowConfiguration, products, eventListener, insets, customAssets, ) ``` :::note 如果找不到资源,流程将回退到其默认外观。 ::: 对于视频,您可以选择传入 `resolution`,在视频加载前预留布局空间并设置宽高比(`width / height`): ```kotlin showLineNumbers AdaptyCustomVideoAsset.file( FileLocation.fromResId(requireContext(), R.raw.custom_video), preview = AdaptyCustomImageAsset.file( FileLocation.fromResId(requireContext(), R.drawable.video_preview), ), resolution = AdaptyCustomVideoAsset.Resolution(width = 1080, height = 1920), ) ```可选
默认值:`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` 中指定的时间,因为该操作底层可能由多个请求组成。
对于 Android:你可以使用扩展函数创建 `TimeInterval`(例如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或使用 `TimeInterval.seconds(5)`。若不设置限制,请使用 `TimeInterval.INFINITE`。
| 响应参数: | 参数 | 描述 | | :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | 一个 [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中开启 **Show on device** 开关。如果未开启该选项,将无法获取视图配置。 ::: 获取付费墙后,检查其是否包含 `ViewConfiguration`——这表明该付费墙是使用付费墙编辑工具创建的,并将指导你如何展示该付费墙。如果存在 `ViewConfiguration`,则将其视为付费墙编辑工具付费墙;否则,请[将其作为远程配置付费墙处理](present-remote-config-paywalls)。可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间用连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果请求失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——如果缓存数据存在,则直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要自定义付费墙中的图片和视频,需要实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你可以通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 为部分用户展示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在播放视频前显示预览图。 :::important 要使用此功能,请将 Adapty Android SDK 更新至 3.7.0 或更高版本。 ::: 以下是如何通过简单字典提供自定义资源的示例: ```kotlin showLineNumbers val customAssets = AdaptyCustomAssets.of( "hero_image" to AdaptyCustomImageAsset.remote( url = "https://example.com/image.jpg", preview = AdaptyCustomImageAsset.file( FileLocation.fromAsset("images/hero_image_preview.png"), ) ), "hero_video" to AdaptyCustomVideoAsset.file( FileLocation.fromResId(requireContext(), R.raw.custom_video), preview = AdaptyCustomImageAsset.file( FileLocation.fromResId(requireContext(), R.drawable.video_preview), ), ), ) val paywallView = AdaptyUI.getPaywallView( activity, viewConfiguration, products, eventListener, insets, customAssets, ) ``` :::note 如果找不到某个资源,付费墙将回退到其默认外观。 :::Insets 是流程周围的间距,用于防止可点击元素被系统状态栏遮挡。
默认值:`Unspecified`,即 Adapty 会自动调整 insets,这对边到边的流程效果很好。
如果你的流程不是边到边的,可能需要设置自定义 insets。具体方法请参阅下方的[修改流程 insets](android-present-paywalls#change-flow-insets) 部分。
| | **customAssets** | 可选 | 传入一个 `AdaptyCustomAssets` 对象,在运行时替换流程或付费墙中的图片和视频。详情请参阅[自定义资源](android-get-pb-paywalls#customize-assets)。 | | **tagResolver** | 可选 | 使用 `AdaptyUiTagResolver` 解析流程文本中的自定义标签。该解析器接受标签参数并将其解析为对应的字符串。详情请参阅付费墙编辑工具中的自定义标签相关内容。 | | **timerResolver** | 可选 | 如果你需要使用自定义计时器功能,请在此传入对应的解析器。 | :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 修改流程边距 \{#change-flow-insets\} 边距是指流程周围的空白区域,用于防止可点击元素被系统栏遮挡。默认情况下,Adapty 会自动调整边距,非常适合全面屏流程。 如果你的流程不是全面屏,可以自定义边距: - 如果状态栏和导航栏都不与 `AdaptyFlowView` 重叠,请使用 `AdaptyFlowInsets.None`。 - 对于更复杂的场景,例如流程与顶部状态栏重叠但不与底部重叠,可以仅将 `bottomInset` 设置为 `0`,如下例所示:Insets 是付费墙周围的间距,用于防止可点击元素被系统栏遮挡。
默认值为 `UNSPECIFIED`,即 Adapty 将自动调整 insets,非常适合全屏边到边付费墙。
如果你的付费墙不是边到边布局,可能需要设置自定义 insets。具体方法请参阅下方的[更改付费墙 insets](android-present-paywalls#change-paywall-insets) 部分。
| | **personalizedOfferResolver** | 可选 | 如需标记个性化定价([了解更多](https://developer.android.com/google/play/billing/integrate#personalized-price)),请实现 `AdaptyUiPersonalizedOfferResolver` 并传入你自己的逻辑,将 `AdaptyPaywallProduct` 映射为 true(表示该产品价格已个性化)或 false。 | | **tagResolver** | 可选 | 使用 `AdaptyUiTagResolver` 解析付费墙文本中的自定义标签。该解析器接收一个标签参数,并将其解析为对应的字符串。详情请参阅付费墙编辑工具中的自定义标签主题。 | | **timerResolver** | 可选 | 如果你要使用自定义计时器功能,请在此处传入对应的解析器。 | :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 更改付费墙边距 \{#change-paywall-insets\} 边距是付费墙周围的空白区域,用于防止可点击元素被系统栏遮挡。默认情况下,Adapty 会自动调整边距,这对全屏付费墙效果很好。 如果你的付费墙不是全屏布局,可能需要自定义边距: - 如果状态栏和导航栏都不与 `AdaptyPaywallView` 重叠,请使用 `AdaptyPaywallInsets.NONE`。 - 对于更复杂的自定义场景,例如付费墙与顶部状态栏重叠但不与底部重叠,可以仅将 `bottomInset` 设置为 `0`,如下例所示:
## 付费墙展示次数过多 \{#the-paywall-view-number-is-too-big\}
**问题**:付费墙展示次数显示为预期值的两倍。
**原因**:你可能在代码中调用了 `logShowFlow`(Android SDK v4+)/ `logShowPaywall`,如果你使用的是付费墙编辑工具或 Flow Builder,这会导致展示次数重复计算。对于使用这些工具构建的流程和付费墙,分析数据会自动追踪,无需手动调用此方法。
**解决方案**:如果你使用的是付费墙编辑工具或 Flow Builder,请确保代码中没有调用 `logShowFlow`(Android SDK v4+)/ `logShowPaywall`。
## 其他问题 \{#other-issues\}
**问题**:您遇到了上述未涵盖的其他付费墙编辑工具相关问题。
**解决方案**:如有需要,请参考[迁移指南](android-sdk-migration-guides)将 SDK 升级至最新版本。许多问题已在较新的 SDK 版本中得到修复。
---
# File: android-quickstart-manual
---
---
title: "在 Android SDK 的自定义付费墙中启用购买功能"
description: "将 Adapty SDK 集成到自定义 Android 付费墙中,以启用应用内购买功能。"
---
本指南介绍如何将 Adapty 集成到自定义付费墙中。你可以完全掌控付费墙的实现方式,同时由 Adapty SDK 负责获取产品、处理新购买以及恢复历史购买记录。
:::important
**本指南适用于需要自行实现自定义付费墙的开发者。** 如果你希望以最简便的方式开启购买功能,请使用 [Adapty Flow Builder](android-quickstart-paywalls)。使用 Flow Builder,你可以在无代码可视化编辑器中创建流程,Adapty 自动处理所有购买逻辑,并且无需重新发布应用即可测试不同的设计方案。
:::
## 开始之前 \{#before-you-start\}
### 设置产品 \{#set-up-products\}
要启用应用内购买,你需要了解三个关键概念:
- [**产品**](product) – 用户可以购买的任何内容(订阅、消耗型商品、永久授权)
- [**付费墙**](paywalls) – 定义向用户展示哪些产品的配置。在 Adapty 中,付费墙是获取产品的唯一方式,但这种设计让你无需修改应用代码就能调整产品、价格和优惠。
- [**版位**](placements) – 应用中展示付费墙的位置和时机(如 `main`、`onboarding`、`settings`)。你在看板中为版位配置付费墙,然后在代码里通过版位 ID 请求对应的付费墙。这样就能轻松运行 A/B 测试,并向不同用户展示不同的付费墙。
即使你使用自定义付费墙,也需要了解这些概念。简单来说,它们就是你在应用中管理所售产品的方式。
要实现自定义付费墙,你需要创建一个**付费墙**并将其添加到一个**版位**。这样才能获取你的产品。如果想了解需要在看板中完成哪些操作,请参考[这里](quickstart)的快速入门指南。
### 管理用户 \{#manage-users\}
您可以选择在您的后端使用或不使用身份验证。
但是,Adapty SDK 对匿名用户和已识别用户的处理方式不同。请阅读[用户识别快速入门指南](android-quickstart-identify)以了解具体细节,确保您正确处理用户信息。
## 第一步:获取产品 \{#step-1-get-products\}
要为自定义付费墙获取产品,你需要:
1. 通过将[版位](placements) ID 传递给 `getFlow` 方法来获取 `flow` 对象。
2. 使用 `getPaywallProducts` 方法获取该流程的产品数组。
默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存存在时直接返回缓存数据。这种情况下用户获取的数据可能不是最新的,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清理时才会清除。
Adapty SDK 通过两个层级存储流程和付费墙:上述定期更新的缓存,以及[备用付费墙](android-use-fallback-paywalls)。我们还使用 CDN 加速流程和付费墙的获取,并在 CDN 不可达时提供独立的备用服务器。
| | **loadTimeout** | 默认值:5 秒 |此值限制该方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能包含多个请求。
| 不要硬编码产品 ID!由于流程是远程配置的,可用产品、产品数量以及特殊优惠(如免费试用)都可能随时变化。请确保你的代码能够处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果之后获取到 3 个产品,应用无需修改任何代码即可显示全部 3 个产品。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`id`、`variationId`)、名称、`remoteConfigs` 数组(每个已配置语言区域对应一条记录)以及 `hasViewConfiguration` 标志。如需获取该 flow 的产品,请调用 `getPaywallProducts(flow)`。 | :::note 在 v4 中,`locale` 参数已从 `getFlow` 移至 `getFlowConfiguration`(仅在使用 AdaptyUI 渲染时使用)。对于自定义付费墙,所有可用的语言区域将一并在 `flow.remoteConfigs` 中返回——请选择与用户设备或应用设置相匹配的语言区域。 ::: ## 获取产品 \{#fetch-products\} 获取流程后,你可以查询与其对应的产品数组:默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存存在时直接返回缓存数据。这样一来,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全可靠的。
请注意,重启应用不会清除缓存,只有卸载重装或手动清理才会清空缓存。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐用法,请参阅[本地化与语言区域代码](android-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,都能享受更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储付费墙:上述定期更新的缓存,以及[备用付费墙](android-use-fallback-paywalls)。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可用时提供独立的备用服务器。这套机制旨在确保您始终能获取最新版本的付费墙,同时在网络条件较差时也能保持可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制了此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能包含多个不同的请求。
| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品的数量以及特殊优惠(如免费试用)都可能随时变化。请确保你的代码能够处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果后来获取到 3 个产品,应用无需修改代码即可显示全部 3 个。唯一需要硬编码的是版位 ID。 返回参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://android.adapty.io/adapty/com.adapty.models/-adapty-paywall/) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,你可以查询与其对应的产品数组:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](android-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此选项,因为它能确保用户始终获取最新数据。
不过,如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时优先返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免额外的网络请求是安全可靠的。
请注意,缓存在应用重启后依然保留,仅在重新安装应用或手动清除时才会被清空。
|如果请求成功,响应将包含此对象。[AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/) 对象提供了关于用户访问等级、订阅及应用内非订阅购买的全面信息。
请检查访问等级状态,以确认用户是否拥有访问应用所需的权限。
| :::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\} 当用户选择新订阅而不是续订当前订阅时,具体行为取决于应用商店。对于 Google Play,订阅不会自动更新。您需要按照以下说明在移动应用代码中管理切换操作。 要在 Android 中将订阅替换为另一个订阅,请使用附加参数调用 `.makePurchase()` 方法:一个 [`AdaptyProfile`](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/) 对象。该模型包含访问等级、订阅及非订阅购买的相关信息。
请检查**访问等级状态**以确定用户是否有权访问该应用。
| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: implement-observer-mode-android --- --- title: "在 Android SDK 中实现观察者模式" description: "在 Adapty 中实现观察者模式,以在 Android SDK 中追踪用户订阅事件。" --- 如果您已经拥有自己的购买基础设施,并且还未准备好完全切换到 Adapty,您可以探索[观察者模式](observer-vs-full-mode)。在其基本形式下,观察者模式提供高级分析功能,并可与归因和分析系统无缝集成。 如果这满足您的需求,您只需要: 1. 在配置 Adapty SDK 时通过将 `observerMode` 参数设置为 `true` 来开启该模式。请按照 [Android](sdk-installation-android#activate-adapty-module-of-adapty-sdk) 的设置说明进行操作。 2. 将您现有购买基础设施中的[交易上报](report-transactions-observer-mode-android)给 Adapty。 ## 观察者模式设置 \{#observer-mode-setup\} 如果您自行处理购买和订阅状态,并使用 Adapty 发送订阅事件和分析数据,请开启观察者模式。 :::important 在观察者模式下运行时,Adapty SDK 不会关闭任何交易,请确保您自行处理这些交易。 :::1. 实现 `AdaptyUiObserverModeHandler`。 当用户发起购买时,`onPurchaseInitiated` 事件会通知你。你可以在此回调中触发自定义的购买流程:
1. 实现 `AdaptyUiObserverModeHandler`。 `onPurchaseInitiated` 事件将通知您用户已发起购买。您可以在此回调中触发自定义的购买流程:
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 是 billing library [Purchase](https://developer.android.com/reference/com/android/billingclient/api/Purchase) 类的实例。
|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) 类的实例。
| 对于全屏模式下系统状态栏遮挡部分界面的情况,请通过以下方式获取插入值:phoneNumber
firstName
lastName
| String | | gender | 枚举,允许的值为:`female`、`male`、`other` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 您可以设置自定义属性,这些属性通常与您的应用使用情况相关。例如,对于健身应用,可以是每周锻炼次数;对于语言学习应用,可以是用户的知识水平等。您可以在市场细分中使用这些属性来创建针对性的付费墙和优惠,也可以在数据分析中用于找出哪些产品指标对营收影响最大。[AdaptyProfile](https://android.adapty.io/adapty/com.adapty.models/-adapty-profile/) 对象。通常情况下,您只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级访问权限。
`.getProfile` 方法始终尝试查询 API,因此能提供最新的结果。如果由于某些原因(例如无网络连接)导致 Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。此外,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持信息的实时性。
| `.getProfile()` 方法会返回用户画像,您可以从中获取访问等级状态。每个应用可以拥有多个访问等级。例如,如果您有一个新闻应用并独立销售不同主题的订阅,可以创建"sports"和"science"等访问等级。但在大多数情况下,您只需要一个访问等级,此时直接使用默认的"premium"访问等级即可。 以下是检查默认"premium"访问等级的示例:可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由连字符(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获得最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存和备用用户引导。我们还使用 CDN 来更快地获取用户引导,并在 CDN 不可访问时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不佳的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |此值限制该方法的超时时间。如果超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作底层可能包含多个不同请求。
对于 Android:您可以使用扩展函数创建 `TimeInterval`(如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或使用 `TimeInterval.seconds(5)`。若不设置限制,请使用 `TimeInterval.INFINITE`。
| 响应参数: | 参数 | 描述 | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://android.adapty.io/adapty/com.adapty.models/-adapty-onboarding/) 对象,包含:用户引导标识符和配置、远程配置以及其他若干属性。 | ## 使用默认目标受众用户引导加速获取 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常情况下,用户引导的获取几乎是即时的,因此您无需担心加速此过程。但如果您有大量目标受众和用户引导,且用户的网络连接较弱,则获取用户引导可能需要比预期更长的时间。在这种情况下,您可能希望展示默认用户引导,以确保流畅的用户体验,而不是不显示任何用户引导。 为解决此问题,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法为**所有用户**目标受众获取指定版位的用户引导。但请务必理解,推荐的方式是通过 `getOnboarding` 方法获取用户引导,详见上方[获取用户引导](#fetch-onboarding)部分。 :::warning 请考虑使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者存在以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要兼容向后设计,否则旧版本可能显示异常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法基于国家、归因或自定义属性进行定向。 如果对您的使用场景而言,更快的获取速度优先于这些缺点,请按如下所示使用 `getOnboardingForDefaultAudience`。否则,请按[上方](#fetch-onboarding)所述使用 `getOnboarding`。 ::: ```kotlin Adapty.getOnboardingForDefaultAudience("YOUR_PLACEMENT_ID") { result -> when (result) { is AdaptyResult.Success -> { val onboarding = result.value // Handle successful onboarding retrieval } is AdaptyResult.Error -> { val error = result.error // Handle error case } } } ``` 参数说明: | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由连字符(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获得最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存和备用用户引导。我们还使用 CDN 来更快地获取用户引导,并在 CDN 不可访问时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不佳的情况下也能保证可靠性。
| --- # File: android-present-onboardings --- --- title: "在 Android SDK 中展示用户引导" description: "了解如何在 Android 上展示用户引导,以有效提升用户参与度。" --- :::tip **从 SDK v4 开始**,你可以构建[流程](android-get-pb-paywalls),作为用户引导更强大的替代方案。与在 WebView 中运行的用户引导不同,流程直接在设备上原生渲染——带来更流畅的动画、一致的 Android 视觉体验、更快的加载速度,以及无需 WebView 运行时依赖。请参阅[获取流程与付费墙](android-get-pb-paywalls)和[展示流程与付费墙](android-present-paywalls)以开始使用。 ::: 在开始之前,请确保: 1. 您已安装 [Adapty Android SDK](sdk-installation-android) 3.8.0 或更高版本。 2. 您已[创建用户引导](create-onboarding)。 3. 您已将用户引导添加到[版位](placements)。 如果您已使用 Onboarding Builder 自定义了用户引导,则无需在移动应用代码中手动处理其渲染逻辑来向用户展示它。此类用户引导已包含展示内容和展示方式的完整配置。 要在设备屏幕上显示可视化的用户引导,首先需要进行配置。调用 `AdaptyUI.getOnboardingView()` 方法,或直接创建 `OnboardingView`:
例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,委托方法 `onCustomAction` 将被触发,并携带来自编辑工具的操作 ID。你可以自定义 ID,例如 "allowNotifications"。
```kotlin showLineNumbers
class YourActivity : AppCompatActivity() {
private val eventListener = object : AdaptyOnboardingEventListener {
override fun onCustomAction(action: AdaptyOnboardingCustomAction, context: Context) {
when (action.actionId) {
"allowNotifications" -> {
// Request notification permissions
}
}
}
override fun onError(error: AdaptyOnboardingError, context: Context) {
// Handle errors
}
// ... other required delegate methods
}
}
```
本地备用付费墙 JSON 格式无效。
请先修复默认的英文付费墙,再替换无效的本地付费墙。如何修复付费墙,请参阅[使用远程配置自定义付费墙](customize-paywall-with-remote-config);如何替换本地付费墙,请参阅[定义本地备用付费墙](fallback-paywalls)。
| |CURRENT_SUBSCRIPTION_TO_UPDATE
\_NOT_FOUND_IN_HISTORY
| 需要替换的原始订阅在活跃订阅中未找到。 | | [BILLING_SERVICE_TIMEOUT](https://developer.android.com/google/play/billing/errors#service_timeout_error_code_-3) | 请求在 Google Play 响应之前已达到最大超时时间。例如,Play Billing Library 调用所请求的操作执行延迟可能导致此错误。 | | [FEATURE_NOT_SUPPORTED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#FEATURE_NOT_SUPPORTED()) | 当前设备的 Play Store 不支持所请求的功能。 | | [BILLING_SERVICE_DISCONNECTED](https://developer.android.com/google/play/billing/errors#service_disconnected_error_code_-1) | 客户端应用通过 `BillingClient` 与 Google Play Store 服务的连接已断开。 | | [BILLING_SERVICE_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#service_unavailable_error_code_2) | Google Play 计费服务当前不可用。大多数情况下,这意味着客户端设备与 Google Play 计费服务之间存在网络连接问题。 | | [BILLING_UNAVAILABLE](https://developer.android.com/google/play/billing/errors#billing_unavailable_error_code_3) |购买过程中发生了计费问题,可能原因如下:
1. 用户设备上的 Play Store 应用缺失或版本过旧。
2. 用户所在国家/地区不受支持。
3. 用户属于企业账号,管理员已禁用购买功能。
4. Google Play 无法向用户的支付方式扣款(例如信用卡已过期)。
5. 用户未登录 Play Store 应用。
| | [DEVELOPER_ERROR](https://developer.android.com/google/play/billing/errors#developer_error) | API 使用方式不正确。 | | [BILLING_ERROR](https://developer.android.com/google/play/billing/errors#error_error_code_6) | Google Play 内部出现问题。 | | [ITEM_ALREADY_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_ALREADY_OWNED()) | 该产品已购买。 | | [ITEM_NOT_OWNED](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#ITEM_NOT_OWNED()) | 该商品不属于当前用户,无法执行所请求的操作。 | | [BILLING_NETWORK_ERROR](https://developer.android.com/google/play/billing/errors#network_error_error_code_12) | 设备与 Play 系统之间的网络连接出现问题。 | | NO_PRODUCT_IDS_FOUND |付费墙中没有任何产品在商店中可用。
如果遇到此错误,请按以下步骤排查:
如果验证码在您授权之前过期,或者您点击了 **Deny**,请再次运行以下命令以重新开始流程:
```bash
adapty auth login
```
## 管理身份验证 \{#manage-authentication\}
### 检查身份验证状态 \{#check-authentication-status\}
要查看当前身份验证状态,请运行:
```bash
adapty auth status
```
已通过身份验证时,输出会显示您的电子邮件、经过掩码处理的令牌前缀以及本地配置文件的路径:
```
Email: you@example.com
Token: abcd1234****
Config: ~/.config/adapty/config.json
```
未通过身份验证时:
```
Not authenticated. Run `adapty auth login`.
```
### 验证您的令牌 \{#verify-your-token\}
要确认令牌有效并查看您的账户详情,请运行:
```bash
adapty auth whoami
```
与 `adapty auth status` 不同,此命令会向服务器发起实时请求以验证令牌。
### 退出登录 \{#log-out\}
要在本地清除已存储的凭据,请运行:
```bash
adapty auth logout
```
这将清除 `~/.config/adapty/config.json`。令牌在服务器端仍然有效,直到其过期为止——如果您需要立即使其失效,请改用 `adapty auth revoke`。
### 撤销您的令牌 \{#revoke-your-token\}
要在服务器上使令牌失效并在本地将其清除,请运行:
```bash
adapty auth revoke
```
当您希望完全使令牌失效时(例如您的凭据可能已泄露),请使用此命令。撤销后,请运行 `adapty auth login` 重新进行身份验证。
## 令牌错误 \{#token-errors\}
如果令牌被撤销或变为无效,CLI 命令将返回 401 错误。要重新进行身份验证,请运行:
```bash
adapty auth login
```
---
# File: developer-cli-reference
---
---
title: "Adapty 开发者 CLI 完整参考"
description: "所有 Adapty 开发者 CLI 命令的完整参考文档。"
---
:::link
正在使用 AI 助手?可以使用 [Adapty CLI skill](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli) 帮助大语言模型操作 CLI。
:::
本文列出了所有 Adapty CLI 命令及其参数、标志和可接受的值。
:::link
有关身份验证设置和令牌管理,请参阅[身份验证](developer-cli-authentication)。
:::
## 全局标志 \{#global-flags\}
这些标志适用于所有命令。
| 标志 | 描述 |
|---|---|
| `--json` | 以 JSON 格式输出,而非格式化文本 |
| `--help` | 显示命令帮助 |
所有 `list` 命令还接受分页标志:
| 标志 | 默认值 | 描述 |
|---|---|---|
| `--page` | `1` | 页码 |
| `--page-size` | `20` | 每页条目数(最大:100) |
## 应用 \{#apps\}
管理 Adapty 账户中的应用。有关基于看板的配置,请参阅 [App settings](general)。
### adapty apps list \{#adapty-apps-list\}
列出 Adapty 账户中的所有应用。
```bash
adapty apps list
```
接受[分页标志](#global-flags)。
### adapty apps get \{#adapty-apps-get\}
获取特定应用的详细信息。
```bash
adapty apps get
:::note 如需追踪订阅事件,请在 Adapty 中使用 [Webhook](webhook) 集成,或直接与您现有的服务进行集成。 ::: ## 案例一:同步网页端与移动端的订阅用户 \{#case-1-sync-subscribers-between-web-and-mobile\} 如果你使用 Stripe、ChargeBee 或其他网页支付服务商,可以轻松同步订阅用户。操作步骤如下: 1.
用户的 Adapty 用户画像 ID。可在 [Adapty 看板 -> **Profiles**](https://app.adapty.io/profiles/users) -> 具体用户画像页面的 **Adapty ID** 字段中查看。
与 **adapty-customer-user-id** 可互换使用,任选其一即可。
| | **adapty-customer-user-id** |用户在您系统中的 ID。可在 [Adapty 看板 -> **Profiles**](https://app.adapty.io/profiles/users) -> 具体用户画像页面的 **Customer user ID** 字段中查看。
与 **adapty-profile-id** 可互换使用,任选其一即可。
⚠️ 仅当您在应用代码中通过 Adapty SDK
### 登录/注册时 \{#during-loginsignup\}
如果你在应用启动后才识别用户身份(例如,用户登录或注册之后),请使用 `identify` 方法设置其 customer user ID。
- 如果你**之前从未使用过这个 customer user ID**,Adapty 会自动将其关联到当前用户画像。
- 如果你**之前已经用这个 customer user ID 识别过用户**,Adapty 会切换到与该 customer user ID 关联的用户画像。
:::tip
创建 customer user ID 时,请将其与用户数据一起保存,这样当用户在新设备上登录或重新安装应用时,你可以发送相同的 ID。
:::
在调用其他 SDK 方法之前,请始终 `await` `identify`。并发调用会产生 `#3006 profileWasChanged` 错误,或者落到匿名用户画像上。详见 [Capacitor SDK 的调用顺序](capacitor-sdk-call-order)。
```typescript showLineNumbers
try {
await adapty.identify({ customerUserId: "YOUR_USER_ID" });
// successfully identified
} catch (error) {
// handle the error
}
```
### 在 SDK 激活期间 \{#during-the-sdk-activation\}
如果在激活 SDK 时你已经知道用户 ID,可以直接在 `activate` 方法中传入,无需单独调用 `identify`。
如果你知道用户 ID,但在激活之后才设置,那么 SDK 激活时 Adapty 会先创建一个新的空用户画像,等到你调用 `identify` 后才会切换到已有的用户画像。
您可以传入现有的 customer user ID(即您之前使用过的 ID),也可以传入一个新的。如果传入新 ID,激活时创建的新用户画像将自动与该 customer user ID 关联。
:::tip
如需将创建的空用户画像排除在看板分析之外,请前往 **App settings**,配置 [**Installs definition for analytics**](general#4-installs-definition-for-analytics)。
:::
```typescript showLineNumbers
await adapty.activate({
apiKey: "YOUR_PUBLIC_SDK_KEY",
params: {
customerUserId: "YOUR_USER_ID"
}
});
```
### 退出登录用户 \{#log-users-out\}
如果您有供用户退出登录的按钮,请使用 `logout` 方法。这将为用户创建一个新的匿名用户画像 ID。
```typescript showLineNumbers
try {
await adapty.logout();
// successful logout
} catch (error) {
// handle the error
}
```
:::info
要让用户重新登录应用,请使用 `identify` 方法。
:::
### 允许未登录状态下购买 \{#allow-purchases-without-login\}
如果你的用户在登录前后均可进行购买,则无需额外配置:
工作原理如下:
1. 当未登录用户完成购买时,Adapty 会将其绑定到该用户的匿名用户画像 ID。
2. 当用户登录账户后,Adapty 会切换到使用其已识别的用户画像。
- 如果是已有的 customer user ID(该 customer user ID 已关联到某个用户画像),Adapty 会自动同步其交易记录。
- 如果是新的 customer user ID(例如,购买发生在注册之前),Adapty 会将该 customer user ID 分配给当前用户画像,从而保留完整的购买历史。
---
# File: adapty-sdk-integration-skill-capacitor
---
---
title: "通过 SDK 集成技能将 Adapty 集成到 Capacitor 应用中"
description: "使用 adapty-sdk-integration 技能,借助 AI 编程工具将 Adapty SDK 端到端集成到 Capacitor 应用中。"
---
[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-capacitor)——它会引导你的 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-capacitor
---
---
title: "借助 AI 将 Adapty 集成到 Capacitor 应用"
description: "使用 Cursor、Context7、ChatGPT、Claude 或其他 AI 工具将 Adapty 集成到 Capacitor 应用的分步指南。"
---
本指南将逐步带你用 AI 编程工具将 Adapty 集成到 Capacitor 应用中——你只需按正确顺序把合适的 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\}
在编写任何 SDK 代码之前,Adapty 需要先完成一些看板配置。您可以通过交互式 LLM 技能,或手动通过看板来完成配置。
### 技能方式(推荐)\{#skill-approach-recommended\}
Adapty CLI 技能允许您的 LLM 直接设置应用、产品、访问等级、付费墙和版位——无需为每个步骤打开看板。您只需在看板中[连接您的应用商店](integrate-payments)即可。
```
npx skills add adaptyteam/adapty-cli --skill adapty-cli
```
添加技能后,在您的 agent 中运行 `/adapty-cli`。它将引导您完成每个步骤——包括何时打开看板来连接您的应用商店。
### 看板配置方式 \{#dashboard-approach\}
如果你更倾向于手动配置,以下是编写代码前需要准备的内容。LLM 无法自动从看板中获取这些值,需要你手动提供。
1. **连接应用商店**:在 Adapty 看板中,进入 **App settings → General**,将 App Store 和 Google Play 都连接上(如果你的 Capacitor 应用需要同时支持两个平台)。这是购买功能正常运行的必要前提。
[连接应用商店](integrate-payments)
2. **复制你的 Public SDK key**:在 Adapty 看板中,前往 **App settings → General**,找到 **API keys** 部分。在代码中,这就是你传给 `adapty.activate()` 的字符串。
3. **至少创建一个产品**:在 Adapty 看板中,前往 **Products** 页面。你不需要在代码中直接引用产品——Adapty 会通过付费墙来分发它们。
[添加产品](quickstart-products)
4. **创建付费墙和版位**:在 Adapty 看板中,在 **Paywalls** 页面创建付费墙,然后在 **Placements** 页面将其分配到版位。在代码中,版位 ID 就是传给 `adapty.getFlow()` 的字符串。
[创建付费墙](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)。
## 向您的 LLM 提供 Adapty 文档 \{#feed-adapty-docs-to-your-llm\}
### 使用 Context7(推荐)\{#use-context7-recommended\}
[Context7](https://context7.com) 是一个 MCP 服务器,可让你的 LLM 直接访问最新的 Adapty 文档。它会根据你的提问自动获取相关文档,无需手动粘贴 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 Capacitor SDK
```
:::warning
尽管 Context7 无需手动粘贴文档链接,实施顺序仍然重要。请按照下方的[实施流程](#implementation-walkthrough)逐步操作,确保一切正常运行。
:::
### 使用纯文本文档 \{#use-plain-text-docs\}
您可以以纯文本 Markdown 格式访问任何 Adapty 文档。在其 URL 末尾添加 `.md`,或点击文章标题下方的 **Copy for LLM**。例如:[adapty-cursor-capacitor.md](https://adapty.io/docs/zh/adapty-cursor-capacitor.md)。
下方[实现演练](#implementation-walkthrough)中的每个阶段都包含一个"发送给您的 LLM"代码块,其中有可粘贴的 `.md` 链接。
如需一次获取更多文档,请参阅下方的[索引文件和平台特定子集](#plain-text-doc-index-files)。
## 实现演练 \{#implementation-walkthrough\}
本指南的其余部分按实现顺序介绍 Adapty 集成。每个阶段包含要发送给 LLM 的文档、完成后应看到的效果,以及常见问题。
### 规划集成方案 \{#plan-your-integration\}
在开始写代码之前,先让你的 LLM 分析项目结构并制定实施计划。如果你使用的 AI 工具支持规划模式(例如 Cursor 或 Claude Code 的 plan 模式),建议先启用该模式,让 LLM 在生成代码前同时读取你的项目结构和 Adapty 文档。
告诉你的 LLM 你使用哪种购买方式——这会影响它需要参考的指南:
- [**Adapty Flow Builder**](adapty-flow-builder):在 Adapty 的无代码编辑工具中创建流程,SDK 自动完成渲染。
- [**手动创建付费墙**](capacitor-making-purchases):用代码构建自己的付费墙界面,但仍使用 Adapty 获取产品并处理购买。
- [**观察者模式**](observer-vs-full-mode):保留现有的购买基础设施,仅将 Adapty 用于数据分析和集成。
不确定该选哪个?请查看[快速入门中的对比表格](capacitor-quickstart-paywalls)。
### 安装并配置 SDK \{#install-and-configure-the-sdk\}
通过 npm 添加 Adapty SDK 依赖,并使用您的 Public SDK key 激活它。这是一切功能的基础——没有它,其他任何功能都无法正常运行。
**指南:** [安装并配置 Adapty SDK](sdk-installation-capacitor)
:::info
本演示面向 Adapty Capacitor SDK v4(测试版)——即[快速入门](capacitor-quickstart-paywalls)所介绍的 API。v4 尚处于预发布阶段,请确保你的 LLM 固定使用精确版本(`npm install @adapty/capacitor@4.0.0-beta.2`),而非安装最新的稳定版 3.x。请参阅 [SDK 4.0 安装说明](sdk-installation-capacitor#adapty-sdk-40-beta)及[迁移指南](migration-to-capacitor-sdk-v4)。
:::
将以下内容发送给你的 LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/zh/sdk-installation-capacitor.md
```
:::tip[Checkpoint]
- **预期结果:** 应用在 iOS 和 Android 上均能构建并运行,控制台显示 Adapty 激活日志。
- **常见问题:** 提示"Public API key is missing" → 检查是否已将占位符替换为 **App settings** 中的真实密钥。
:::
### 展示付费墙并处理购买 \{#show-paywalls-and-handle-purchases\}
通过版位 ID 获取付费墙、展示付费墙并处理购买事件。具体需要参考哪些指南,取决于你的购买处理方式。
每完成一个购买功能就在沙盒中测试一次,不要等到最后再统一测试。沙盒环境的配置说明请参见[在沙盒中测试购买](test-purchases-in-sandbox)。
通过可选的 `params` 对象传入。默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时直接返回缓存数据。这样一来,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清除时才会被清空。
Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 来加快付费墙的获取速度,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终获取最新版本的付费墙,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |通过可选的 `params` 对象传入。该值限制此方法的超时时间。如果超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeoutMs` 中指定的值,因为该操作在底层可能包含多个不同的请求。
| **不要硬编码产品 ID。** 你唯一需要硬编码的 ID 是版位 ID。流程和付费墙均在远程配置,因此产品数量和可用优惠随时可能发生变化。你的应用必须动态处理这些变化——如果付费墙今天返回两个产品,明天返回三个,应无需修改代码即可全部展示。 响应参数: | 参数 | 描述 | | :-------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Flow | 一个 `AdaptyFlow` 对象,包含流程的标识符(`id`、`variationId`)、名称、版位、付费墙变体(`paywalls`)以及远程配置(`remoteConfigs`)。 | ## 获取视图配置 \{#fetch-the-view-configuration\} :::important 请确保在编辑工具中启用了 **Show on device** 开关。如果未开启该选项,将无法获取视图配置。 ::: 如果版位是在 **Flow Builder** 或 **Paywall Builder** 中设计的,Adapty 会自动为您渲染 UI。使用 `createFlowView` 创建视图,然后[展示流程或付费墙](capacitor-present-paywalls)。如果版位是没有编辑工具 UI 的自定义付费墙,请改为[将其作为远程配置付费墙处理](present-remote-config-paywalls-capacitor)。 在 Capacitor SDK 中,直接调用 `createFlowView` 即可——无需提前获取视图配置。 :::warning `createFlowView` 方法的返回结果只能使用一次。如需再次使用,请重新调用 `createFlowView` 方法。不重新创建而重复调用可能会导致错误。 ::: ```typescript showLineNumbers try { const view = await createFlowView(flow); } catch (error) { // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | | :------------------- | :------- | :----------------------------------------------------------- | | **flow** | 必填 | 一个 `AdaptyFlow` 对象,用于获取所需流程/付费墙的控制器。 | | **customTags** | 可选 | 定义自定义标签及其解析值的字典。自定义标签在内容中作为占位符使用,会动态替换为特定字符串,从而在流程/付费墙中实现个性化内容。详情请参阅付费墙编辑工具中的自定义标签主题。 | | **prefetchProducts** | 可选 | 启用后可优化产品在屏幕上的显示时机。设为 `true` 时,AdaptyUI 将自动预加载所需产品。默认值:`true`。 | | **android.enableSafeArea** | 可选 | 仅适用于 Android(在 iOS 上会被忽略)。嵌套在 `android` 键下。设为 `true` 时,流程视图会应用安全区域内边距。默认值:`true`。该默认值适用于大多数场景。 | :::note 如果您使用多种语言,请了解如何添加[流程本地化](add-paywall-locale-in-adapty-paywall-builder),以及如何在[此处](capacitor-localizations-and-locale-codes)正确使用语言区域代码。 ::: 获取视图后,请[展示流程/付费墙](capacitor-present-paywalls)。 ## 为默认目标受众获取流程或付费墙以加快加载速度 \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,流程和付费墙几乎可以瞬间完成加载,无需担心速度问题。但如果你配置了大量目标受众和版位,且用户的网络连接较差,加载流程或付费墙的时间可能会比预期更长。在这种情况下,你可能希望直接展示默认流程或付费墙,以确保良好的用户体验,而不是让用户看到空白页面。 为了解决这个问题,您可以使用 `getFlowForDefaultAudience` 方法,该方法会获取指定版位中 **All Users** 目标受众的流程或付费墙。但请务必理解,推荐的方式是通过 `getFlow` 方法来获取流程或付费墙,详情请参阅上方的[获取流程/付费墙](#fetch-flowpaywall)部分。 :::warning 为什么我们推荐使用 `getFlow` `getFlowForDefaultAudience` 方法存在以下几个明显缺陷: - **潜在的向后兼容性问题**:如果你需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会面临挑战。你要么设计兼容当前(旧版)版本的付费墙,要么接受使用当前(旧版)版本的用户可能遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向能力(包括基于国家、营销归因或自定义属性的定向)。 如果你愿意接受这些缺点以换取更快的 flow 或付费墙加载速度,可以按如下方式使用 `getFlowForDefaultAudience` 方法。否则,请继续使用[上文](#fetch-flowpaywall)介绍的 `getFlow`。 ::: ```typescript showLineNumbers try { const flow = await adapty.getFlowForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', }); // the requested flow/paywall } catch (error) { // handle the error } ``` | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **fetchPolicy** | 默认值:`'reload_revalidating_cache_data'` |通过可选的 `params` 对象传入。默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `'return_cache_data_else_load'`——若缓存数据存在则直接返回。这种情况下用户获取的数据可能不是最新的,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全可靠的。
请注意,缓存在应用重启后仍会保留,只有在卸载重装应用或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要自定义流程/付费墙中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,通过这些 ID 定位相应元素并自定义其行为。 对于其他图片和视频,您需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,您可以: - 为部分用户展示不同的图片或视频。 - 在远程主图加载时显示本地预览图。 - 在视频播放前先显示预览图。 以下是通过简单字典提供自定义资源的示例: ```typescript showLineNumbers const customAssets: Record可选
默认值:`en`
|[付费墙本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及使用建议,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **params** | 可选 | 获取付费墙的附加参数。 | **不要硬编码产品 ID。** 唯一需要硬编码的是版位 ID。付费墙是远程配置的,因此产品数量和可用优惠随时可能变化。你的应用必须动态处理这些变化——如果今天某个付费墙返回两个产品,明天返回三个,则无需修改代码即可全部展示。 返回参数: | 参数 | 描述 | | :-------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | 一个 [`AdaptyPaywall`](https://capacitor.adapty.io/interfaces/adaptypaywall) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中开启 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 获取付费墙后,检查它是否包含 `ViewConfiguration`,这表明它是使用付费墙编辑工具创建的。这将指导你如何展示付费墙。如果存在 `ViewConfiguration`,则将其视为付费墙编辑工具付费墙;否则,[将其作为远程配置付费墙处理](present-remote-config-paywalls-capacitor)。 在 Capacitor SDK 中,直接调用 `createPaywallView` 方法,无需手动先获取视图配置。 :::warning `createPaywallView` 方法的返回结果只能使用一次。如需再次使用,请重新调用 `createPaywallView` 方法。 ::: ```typescript showLineNumbers if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { // use your custom logic } ``` 参数: | 参数 | 是否必填 | 描述 | | :------------------- | :------- | :----------------------------------------------------------- | | **paywall** | 必填 | 一个 `AdaptyPaywall` 对象,用于获取目标付费墙的控制器。 | | **customTags** | 选填 | 定义自定义标签及其对应值的字典。自定义标签作为付费墙内容中的占位符,在运行时动态替换为指定字符串,从而实现付费墙的个性化内容展示。详情请参阅付费墙编辑工具中的自定义标签相关文档。 | | **prefetchProducts** | 选填 | 启用后可优化产品在屏幕上的展示时机。设为 `true` 时,AdaptyUI 将自动预加载所需产品。默认值:`false`。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder),以及如何正确使用语言区域代码([点击了解](capacitor-localizations-and-locale-codes))。 ::: 获取视图后,[展示付费墙](capacitor-present-paywalls)。 ## 为默认目标受众获取付费墙以加快加载速度 \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,付费墙的获取速度非常快,无需额外优化。但如果你配置了大量目标受众和付费墙,且用户的网络状况较差,获取付费墙可能会花费较长时间。在这种情况下,你可能希望先展示一个默认付费墙,以确保用户体验流畅,而不是什么都不显示。 为解决此问题,你可以使用 `getPaywallForDefaultAudience` 方法,该方法会获取指定版位中 **All Users** 目标受众对应的付费墙。但请务必注意,推荐的做法是通过 `getPaywall` 方法获取付费墙,详见上文[获取付费墙信息](#fetch-paywall-designed-with-paywall-builder)章节。 :::warning 为什么我们推荐使用 `getPaywall` `getPaywallForDefaultAudience` 方法存在以下几个明显缺点: - **潜在的向后兼容性问题**:如果需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,你要么需要设计同时兼容当前(旧版)的付费墙,要么接受当前(旧版)用户可能遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向能力(包括基于国家、营销归因或自定义属性的定向)。 如果你愿意接受这些缺点以换取更快的付费墙获取速度,请按如下方式使用 `getPaywallForDefaultAudience` 方法。否则,请继续使用[上文](#fetch-paywall-designed-with-paywall-builder)介绍的 `getPaywall`。 ::: ```typescript showLineNumbers try { const paywall = await adapty.getPaywallForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', }); // the requested paywall } catch (error) { // handle the error } ``` | 参数 | 是否必填 | 说明 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](capacitor-localizations-and-locale-codes)。
| | **params** | 可选 | 获取付费墙时的附加参数。 | ## 自定义资源 \{#customize-assets\} 要自定义付费墙中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你可以通过这些 ID 定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 为部分用户展示不同的图片或视频。 - 在远程主图加载时显示本地预览图。 - 在播放视频前先展示预览图。 以下是通过简单字典提供自定义资源的示例: ```typescript showLineNumbers const customAssets: Record可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络状况不稳定,可以考虑使用 `'return_cache_data_else_load'`——在缓存存在时优先返回缓存数据。这种方式下用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储流程和付费墙:上述会定期更新的缓存,以及[备用付费墙](capacitor-use-fallback-paywalls)。我们还使用 CDN 来加快流程和付费墙的加载速度,并在 CDN 不可用时启用独立的备用服务器。该系统旨在确保您始终能获取最新版本的流程,同时在网络条件受限时也能保证可靠性。
| | **params.loadTimeoutMs** |可选
默认值:5000 ms
|该值限制此方法的超时时间(毫秒)。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeoutMs` 中指定的值,因为该操作在底层可能包含多个不同的请求。
| :::note 在 v4 中,`getFlow` 不再接受 `locale` 参数。对于自定义付费墙,所有可用的语言设置都会通过流程的远程配置(`flow.remoteConfigs`)返回——请从中选取与用户设备或应用设置相匹配的语言。 ::: 不要在代码中硬编码产品 ID!由于流程是远程配置的,可用产品的数量以及特殊优惠(例如免费试用)都可能随时间变化。请确保你的代码能够处理这些情况。 例如,如果最初获取到 2 个产品,你的应用应显示这 2 个产品;但如果后来获取到 3 个产品,你的应用无需修改代码即可显示全部 3 个。唯一需要硬编码的是版位 ID。 返回参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`id`、`variationId`)、名称、付费墙变体列表(`paywalls`)以及 `remoteConfigs` 数组(每个已配置的语言环境对应一条记录)。如需获取该流程的产品,请调用 `getPaywallProducts({ flow })`。 | ## 获取产品 \{#fetch-products\} 获取到流程后,你可以查询与其对应的产品数组: ```typescript showLineNumbers try { const products = await adapty.getPaywallProducts({ flow }); // the requested products list } catch (error) { console.error('Failed to fetch products:', error); } ``` 响应参数: | 参数 | 说明 | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他若干属性。 | 在实现自定义付费墙设计时,您可能需要访问 [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) 对象中的这些属性。以下列出了最常用的属性,完整属性详情请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **标题** | 要显示产品标题,请使用 `product.localizedTitle`。请注意,本地化基于用户所选的应用商店国家/地区,而非设备本身的语言环境。 | | **价格** | 要显示本地化价格,请使用 `product.price?.localizedString`。该本地化基于设备的语言环境信息。你也可以通过 `product.price?.amount` 以数字形式获取价格,值以当地货币表示。要获取对应的货币符号,请使用 `product.price?.currencySymbol`。 | | **订阅周期** | 要显示订阅周期(如周、月、年等),请使用 `product.subscription?.localizedSubscriptionPeriod`,该本地化基于设备的语言环境。要以编程方式获取订阅周期,请使用 `product.subscription?.subscriptionPeriod`,从中可访问 `unit` 属性获取周期单位(即 `'day'`、`'week'`、`'month'`、`'year'` 或 `'unknown'`)。`numberOfUnits` 值表示周期单位的数量。例如,对于季度订阅,`unit` 属性值为 `'month'`,`numberOfUnits` 值为 `3`。 | | **新用户优惠** | 要显示表示订阅包含新用户优惠的标签或其他指示器,请查看 `product.subscription?.offer?.phases` 属性。这是一个列表,最多可包含两个折扣阶段:免费试用阶段和优惠价格阶段。每个阶段对象包含以下实用属性:可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐这种方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络连接不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时直接返回缓存数据。这种情况下,用户获取的数据可能不是最新的,但加载速度更快,不受网络状况影响。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清除时才会被清空。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。此参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及我们建议使用方式的更多信息,请参阅[本地化与语言区域代码](capacitor-localizations-and-locale-codes)。
| | **params.fetchPolicy** |可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时直接返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络连接质量如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
| | **params.loadTimeoutMs** |可选
默认值:5000 ms
|此值限制该方法的超时时间(毫秒)。如果达到超时时间,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的超时时间可能略晚于 `loadTimeoutMs` 中指定的值,因为该操作在底层可能包含多个请求。
| **不要硬编码产品 ID。** 您唯一应该硬编码的 ID 是版位 ID。付费墙是远程配置的,因此产品数量和可用优惠随时可能发生变化。您的应用必须动态处理这些变化——如果付费墙今天返回两个产品,明天返回三个,则应在不修改代码的情况下全部显示。 响应参数: | 参数 | 说明 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://capacitor.adapty.io/interfaces/adaptypaywall) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取产品 \{#fetch-products\} 获得付费墙后,您可以查询与其对应的产品数组: ```typescript showLineNumbers try { const products = await adapty.getPaywallProducts({ paywall }); // the requested products list } catch (error) { console.error('Failed to fetch products:', error); } ``` 响应参数: | 参数 | 描述 | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他若干属性。 | 在实现自定义付费墙设计时,你可能需要访问 [`AdaptyPaywallProduct`](https://capacitor.adapty.io/interfaces/adaptypaywallproduct) 对象中的这些属性。以下列出了最常用的属性,完整属性列表请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 要显示产品标题,请使用 `product.localizedTitle`。请注意,本地化基于用户所选的商店国家/地区,而非设备本身的语言区域设置。 | | **Price** | 要显示本地化的价格,请使用 `product.price?.localizedString`。该本地化基于设备的语言区域信息。您也可以通过 `product.price?.amount` 以数字形式获取价格,该值以本地货币为单位。要获取对应的货币符号,请使用 `product.price?.currencySymbol`。 | | **Subscription Period** | 要显示订阅周期(例如周、月、年等),请使用 `product.subscription?.localizedSubscriptionPeriod`。该本地化基于设备的语言区域设置。要以编程方式获取订阅周期,请使用 `product.subscription?.subscriptionPeriod`。通过该属性可访问 `unit` 属性以获取时间单位(即 `'day'`、`'week'`、`'month'`、`'year'` 或 `'unknown'`)。`numberOfUnits` 值表示周期单位的数量。例如,对于季度订阅,`unit` 属性值为 `'month'`,`numberOfUnits` 属性值为 `3`。 | | **Introductory Offer** | 要显示订阅包含新用户优惠的标识或其他指示器,请查看 `product.subscription?.offer?.phases` 属性。这是一个最多包含两个折扣阶段的列表:免费试用阶段和优惠价格阶段。每个阶段对象包含以下实用属性:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。此参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及我们建议使用方式的更多信息,请参阅[本地化与语言区域代码](capacitor-localizations-and-locale-codes)。
| | **params.fetchPolicy** |可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时直接返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络连接质量如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
|可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及推荐用法的更多信息,请参阅[本地化与语言代码](localizations-and-locale-codes)。
| | **params.fetchPolicy** |可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐此选项,因为它可确保用户始终获取最新数据。
但如果您认为用户网络不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时优先返回缓存数据。这样用户获得的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清除时才会被清空。
| | **params.loadTimeoutMs** |可选
默认值:5000 毫秒
|该值限制此方法的超时时间(以毫秒为单位)。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略长于 `loadTimeoutMs` 中指定的值,因为该操作在底层可能包含多个请求。
| 响应参数: | 参数 | 描述 | |:----------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onboarding** | 一个 [`AdaptyOnboarding`](https://capacitor.adapty.io/interfaces/adaptyonboarding) 对象,包含:用户引导标识符和配置、远程配置以及其他若干属性。 | ## 通过默认目标受众用户引导加速获取 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常情况下,用户引导几乎可以即时获取,因此您无需担心加速此过程。但是,如果您有大量目标受众和用户引导,且用户网络较差,获取用户引导可能需要比预期更长的时间。在这种情况下,您可能希望显示一个默认用户引导,以确保流畅的用户体验,而不是不显示任何内容。 为解决此问题,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法会获取指定版位中**所有用户**目标受众的用户引导。但请务必了解,推荐的做法是使用 `getOnboarding` 方法获取用户引导,详见上方[获取用户引导](#fetch-onboarding)章节。 :::warning 建议使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者有以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要设计向后兼容的界面,否则旧版本可能显示不正常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法基于国家、归因或自定义属性进行定向投放。 如果更快的获取速度对您的使用场景而言优于上述缺点,请按下方示例使用 `getOnboardingForDefaultAudience`。否则,请按[上方](#fetch-onboarding)描述使用 `getOnboarding`。 ::: ```typescript showLineNumbers try { const onboarding = await adapty.getOnboardingForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en', params: { fetchPolicy: 'reload_revalidating_cache_data' // Load from server, fallback to cache } }); console.log('Default audience onboarding fetched successfully'); } catch (error) { console.error('Failed to fetch default audience onboarding:', error); } ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | 目标[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及推荐用法的更多信息,请参阅[本地化与语言代码](localizations-and-locale-codes)。
| | **params.fetchPolicy** |可选
默认值:`'reload_revalidating_cache_data'`
|默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐此选项,因为它可确保用户始终获取最新数据。
但如果您认为用户网络不稳定,可以考虑使用 `'return_cache_data_else_load'`,在缓存存在时优先返回缓存数据。这样用户获得的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清除时才会被清空。
| --- # File: capacitor-present-onboardings --- --- title: "在 Capacitor SDK 中展示用户引导" description: "了解如何在 Capacitor 上展示用户引导,以提升转化率和收入。" --- :::warning **用户引导功能已在 SDK v4 中废弃,并将在未来版本中移除。** 该功能不再接受修复或改进。请改用 [流程](capacitor-get-pb-paywalls):与在 WebView 中运行的用户引导不同,流程直接在设备上进行原生渲染,带来更流畅的动画效果、一致的原生外观体验、更快的加载速度,以及无需 WebView 运行时依赖。请参阅 [获取流程与付费墙](capacitor-get-pb-paywalls) 和 [展示流程与付费墙](capacitor-present-paywalls) 以开始使用。 ::: 如果你已通过编辑工具自定义了用户引导,则无需在移动应用代码中手动处理其渲染逻辑来向用户展示。这类用户引导已经同时包含了展示内容和展示方式。 在开始之前,请确认: 1. 你已[创建用户引导](create-onboarding)。 2. 你已将用户引导添加到[版位](placements)。 ## 展示用户引导 \{#present-onboarding\} 要展示用户引导,请在 `createOnboardingView` 方法创建的 `view` 上调用 `view.present()` 方法。每个 `view` 只能使用一次。如果需要再次展示用户引导,请重新调用 `createOnboardingView` 来创建新的 `view` 实例。 :::warning 重复使用同一个 `view` 而不重新创建,可能会导致报错。 ::: ```typescript showLineNumbers try { const view = await createOnboardingView(onboarding); view.setEventHandlers({ onClose: (actionId, meta) => { console.log('Onboarding closed:', actionId); return true; // Allow the onboarding to close }, onCustom: (actionId, meta) => { console.log('Custom action:', actionId); return false; // Don't close the onboarding } }); await view.present(); console.log('Onboarding presented successfully'); } catch (error) { console.error('Failed to present onboarding:', error); } ``` ## 配置 iOS 展示样式 \{#configure-ios-presentation-style\} 通过向 `present()` 方法传递 `iosPresentationStyle` 参数,可以配置用户引导在 iOS 上的展示方式。该参数接受 `'full_screen'`(默认值)或 `'page_sheet'` 值。 ```typescript showLineNumbers await view.present({ iosPresentationStyle: 'page_sheet' }); ``` ## 自定义用户引导中链接的打开方式 \{#customize-how-links-open-in-onboardings\} :::important 自定义用户引导中链接的打开方式需要 Adapty SDK v3.15 及以上版本。 ::: 默认情况下,用户引导中的链接会在应用内浏览器中打开。这种方式可以让用户无需切换应用即可浏览网页,从而提供流畅的使用体验。 如果你希望改用外部浏览器打开链接,可以将 `openIn` 参数设置为 `browser_out_app` 来自定义此行为: ```typescript showLineNumbers await view.present({ openIn: 'browser_out_app' }); // default — browser_in_app ``` ## 后续步骤 \{#next-steps\} 展示用户引导后,您需要[处理用户交互和事件](capacitor-handling-onboarding-events)。了解如何处理用户引导事件,以响应用户操作并跟踪分析数据。 --- # File: capacitor-handling-onboarding-events --- --- title: "在 Capacitor SDK 中处理用户引导事件" description: "使用 Adapty 处理 Capacitor 中与用户引导相关的事件。" --- :::warning **用户引导功能已在 SDK v4 中弃用,并将在未来版本中移除。** 该功能不再接受修复或改进。请改用 [流程](capacitor-get-pb-paywalls):与在 WebView 中运行的用户引导不同,流程直接在设备上原生渲染,带来更流畅的动画、一致的原生外观与体验、更快的加载速度,且无需依赖 WebView 运行时。请参阅 [获取流程和付费墙](capacitor-get-pb-paywalls) 和 [展示流程和付费墙](capacitor-present-paywalls) 开始使用。 ::: 使用构建工具配置的用户引导会生成相应事件,供应用程序响应。使用 `setEventHandlers` 方法来处理独立屏幕展示的这些事件。 开始之前,请确认: 1. 您已[创建用户引导](create-onboarding)。 2. 您已将用户引导添加到[版位](placements)。 ## 设置事件处理器 \{#set-up-event-handlers\} 要处理用户引导的事件,请使用 `view.setEventHandlers` 方法: ```typescript showLineNumbers try { const view = await createOnboardingView(onboarding); view.setEventHandlers({ onAnalytics(event, meta) { console.log('Analytics event:', event); }, onClose(actionId, meta) { console.log('Onboarding closed:', actionId); return true; // Allow the onboarding to close }, onCustom(actionId, meta) { console.log('Custom action:', actionId); return false; // Don't close the onboarding }, onPaywall(actionId, meta) { console.log('Paywall action:', actionId); view.dismiss().then(() => { openPaywall(actionId); }); }, onStateUpdated(action, meta) { console.log('State updated:', action); }, onFinishedLoading(meta) { console.log('Onboarding finished loading'); }, onError(error) { console.error('Onboarding error:', error); }, }); await view.present(); } catch (error) { console.error('Failed to present onboarding:', error); } ``` ## 事件类型 \{#event-types\} 以下部分描述了您可以处理的不同类型的事件。 ### 处理自定义操作 \{#handle-custom-actions\} 在编辑工具中,你可以为按钮添加**自定义**操作并为其指定一个 ID。
之后,您可以在代码中使用这个 ID,并将其作为自定义动作来处理。例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,事件处理程序将被触发,并附带与编辑工具中 **Action ID** 对应的 `actionId` 参数。您可以自定义 ID,例如 "allowNotifications"。
```typescript showLineNumbers
view.setEventHandlers({
onCustom(actionId, meta) {
switch (actionId) {
case 'login':
console.log('Login action triggered');
break;
case 'allow_notifications':
console.log('Allow notifications action triggered');
break;
}
return false; // Don't close the onboarding
},
});
```
:::important
请注意,当用户关闭用户引导时,你需要自行处理后续逻辑。例如,你需要停止显示用户引导界面本身。
:::
```typescript showLineNumbers
view.setEventHandlers({
onClose(actionId, meta) {
console.log('Onboarding closed:', actionId);
return true; // Allow the onboarding to close
},
});
```
用户取消了支付请求。
无需采取额外操作,但从业务逻辑角度,你可以向用户提供折扣或稍后再次提醒。
| | paymentInvalid | 3 | 支付参数中有一项未被商店识别。 | | paymentNotAllowed | 4 |用户不被允许进行支付授权。可能的原因:
- 该用户所在国家/地区不支持支付。
- 用户未成年。
| | storeProductNotAvailable | 5 | 请求的产品在 App Store 中不存在。请确认该产品在对应国家/地区可用。 | | cloudServicePermissionDenied | 6 | 用户未授权访问云服务信息。 | | cloudServiceNetworkConnectionFailed | 7 | 设备无法连接到网络。 | | cloudServiceRevoked | 8 | 用户已撤销对该云服务的使用权限。 | | privacyAcknowledgementRequired | 9 | 用户尚未确认商店隐私政策。 | | unauthorizedRequestData | 10 | 请求构建有误。 | | invalidOfferIdentifier | 11 |优惠标识符无效。可能的原因:
- 你未在 App Store 中设置该标识符对应的优惠。
- 该优惠已被撤销。
- 优惠 ID 填写有误。
| | invalidSignature | 12 | 支付折扣中的签名无效。请确认你已填写 **In-app purchase Key ID** 字段并上传了 **In-App Purchase Private Key** 文件。详情请参阅 [配置 App Store 集成](app-store-connection-configuration)。 | | missingOfferParams | 13 |Adapty 集成或优惠配置存在问题。
详情请参阅 [配置 App Store 集成](app-store-connection-configuration) 和 [优惠](offers)。
| | invalidOfferPrice | 14 | 你在商店中指定的价格已失效。优惠价格必须低于原价。 | ## 自定义 Android 错误码 \{#custom-android-codes\} | 错误 | 错误码 | 描述 | |-----|----|-----------| | adaptyNotInitialized | 20 | 你需要通过 `Adapty.activate` 方法正确配置 Adapty SDK。了解如何 [在 React Native 中配置](sdk-installation-reactnative)。 | | productNotFound | 22 | 请求购买的产品在商店中不可用。 | | invalidJson | 23 | 付费墙 JSON 格式无效。请在 Adapty 看板中修复它。详情请参阅 [使用远程配置自定义付费墙](customize-paywall-with-remote-config)。 | | currentSubscriptionToUpdateNotFoundInHistory | 24 | 未找到需要续订的原始订阅记录。 | | pendingPurchase | 25 | 购买状态为待处理,而非已完成。详情请参阅 Android 开发者文档中的 [处理待处理交易](https://developer.android.com/google/play/billing/integrate#pending) 页面。 | | billingServiceTimeout | 97 | 请求在 Google Play 响应前已达到最大超时时间。例如,Play Billing Library 调用请求的操作执行出现延迟时可能触发此错误。 | | featureNotSupported | 98 | 当前设备上的 Play Store 不支持该功能。 | | billingServiceDisconnected | 99 | 这是一个致命错误,表示客户端应用与 Google Play Store 服务之间通过 `BillingClient` 建立的连接已断开。 | | billingServiceUnavailable | 102 | 这是一个暂时性错误,表示 Google Play 结算服务当前不可用。大多数情况下,这意味着客户端设备与 Google Play 结算服务之间的网络连接存在问题。 | | billingUnavailable | 103 |购买过程中发生了用户结算错误。常见原因包括:
1\. 用户设备上的 Play Store 应用版本过旧。
2. 用户所在国家/地区不受支持。
3. 用户为企业用户,且其企业管理员已禁止用户进行购买。
4. Google Play 无法向用户的支付方式扣款,例如用户的信用卡已过期。
5. 用户未登录 Play Store 应用。
| | developerError | 105 | 这是一个致命错误,表示你正在不正确地使用某个 API。 | | billingError | 106 | 这是一个致命错误,表示 Google Play 内部出现了问题。 | | itemAlreadyOwned | 107 | 该消耗型商品已被购买。 | | itemNotOwned | 108 | 对该商品执行的请求操作失败。 | ## 自定义 StoreKit 错误码 \{#custom-storekit-codes\} | 错误 | 错误码 | 描述 | |-----|----|-----------| | noProductIDsFound | 1000 |付费墙中的所有产品均无法在商店中找到。
如果遇到此错误,请按以下步骤排查:
1. 检查所有产品是否已添加到 Adapty 看板。
2. 确认应用的 Bundle ID 与 Apple Connect 中的一致。
3. 核实应用商店中的产品标识符与看板中添加的标识符一致。请注意,标识符中不应包含 Bundle ID,除非商店本身已包含它。
4. 确认你的 Apple 税务设置中应用的付费状态为有效,税务信息为最新,且证书有效。
5. 检查应用是否已绑定银行账户,以便具备变现资格。
6. 检查产品是否在所有地区可用,并确保产品状态为 **"Ready to Submit"**。
| | productRequestFailed | 1002 |当前无法获取可用产品。可能的原因:
- 尚未创建缓存,同时也没有网络连接。
| | cantMakePayments | 1003 | 此设备不允许进行应用内购买。 | | noPurchasesToRestore | 1004 | Google Play 未找到可恢复的购买记录。 | | cantReadReceipt | 1005 |设备上没有有效的收据。这在沙盒测试期间可能出现。
无需采取额外操作,但从业务逻辑角度,你可以向用户提供折扣或稍后再次提醒。
| | productPurchaseFailed | 1006 | 产品购买失败。此错误封装了底层 StoreKit 错误——请读取被封装的错误(或开启详细日志以在控制台查看)以获取实际原因。被封装的错误通常是上表中错误码 0–14 之一,最常见的是 `paymentCancelled`、`paymentInvalid`、`paymentNotAllowed` 或 `invalidOfferPrice`。如果无法确定具体原因,请尝试新建一个[沙盒用户画像](test-purchases-in-sandbox);若问题依然存在,请联系 Apple 支持。 | | refreshReceiptFailed | 1010 | 未收到收据。仅适用于 StoreKit 1。 | | receiveRestoredTransactionsFailed | 1011 | 购买恢复失败。 | ## 自定义网络错误码 \{#custom-network-codes\} | 错误 | 错误码 | 描述 | | :------------------- | :--- | :----------------------------------------------------------- | | notActivated | 2002 | 你需要通过 `Adapty.activate` 方法正确配置 Adapty SDK。了解如何 [在 React Native 中配置](sdk-installation-reactnative)。 | | badRequest | 2003 | 请求无效。 | | serverError | 2004 | 服务器错误。 | | networkFailed | 2005 | 网络请求失败。 | | decodingFailed | 2006 | 响应解码失败。 | | encodingFailed | 2009 | 请求编码失败。 | | analyticsDisabled | 3000 | 由于你已选择退出,我们无法处理分析事件。详情请参阅 [分析集成](analytics-integration)。 | | wrongParam | 3001 | 部分参数不正确:不能为空时传入了空值,或类型有误等。 | | activateOnceError | 3005 | `.activate` 方法只能调用一次。 | | profileWasChanged | 3006 | 操作执行期间用户画像发生了变更。 | | fetchTimeoutError | 3101 | 付费墙未能在规定时间内获取完成。为避免此问题,请 [设置本地备用方案](fetch-paywalls-and-products)。 | | operationInterrupted | 9000 | 该操作被系统中断。 | --- # File: capacitor-sdk-migration-guides --- --- title: "Capacitor SDK 迁移指南" description: "Adapty Capacitor SDK 各版本的迁移指南。" --- 本页包含 Adapty Capacitor SDK 的所有迁移指南。请选择您要迁移的目标版本以查看详细说明: - **[迁移至 v4.0 (beta)](migration-to-capacitor-sdk-v4)** - [**迁移至 v3.16**](migration-to-capacitor-316) --- # File: migration-to-capacitor-sdk-v4 --- --- title: "将 Adapty Capacitor SDK 迁移至 v. 4.0" description: "将 SDK 迁移至 Adapty Capacitor SDK v4.0(测试版),使用流程 API 替换付费墙 API,兼容 Flow Builder 和 Paywall Builder。" --- Adapty Capacitor SDK 4.0(测试版)引入了流程功能,并对付费墙 API 进行了相应重命名。新 API 同时兼容全新的 Flow Builder 和现有的 Paywall Builder,无需在 Adapty 看板侧做任何配置变更。 ## 快速参考 \{#quick-reference\} | v3 | v4 | |---|---| | `adapty.getPaywall({ placementId, locale?, params? })` | `adapty.getFlow({ placementId, params? })` | | `adapty.getPaywallForDefaultAudience({ placementId, locale?, params? })` | `adapty.getFlowForDefaultAudience({ placementId, params? })` | | `adapty.getPaywallProducts({ paywall })` | `adapty.getPaywallProducts({ flow })` | | `adapty.logShowPaywall({ paywall })` | `adapty.logShowFlow({ flow })` | | `AdaptyPaywall` (类型) | `AdaptyFlow` + `AdaptyFlowPaywall` | | `createPaywallView(paywall, params?)` | `createFlowView(flow, params?)` | | `PaywallViewController` | `FlowViewController` | | `EventHandlers` (类型) | `FlowEventHandlers` | | `CreatePaywallViewParamsInput` | `CreateFlowViewParamsInput` | | `onRenderingFailed` | `onError` | `AdaptyPaywallProduct` 保持其名称不变——产品仍属于一个 flow,`getPaywallProducts` 也保持其名称,现在接受一个 `AdaptyFlow` 参数。`getFlow` 和 `getFlowForDefaultAudience` 方法不再接受 `locale` 参数。购买和用户画像相关 API(`makePurchase`、`restorePurchases`、`getProfile`、`identify`、`updateProfile`)以及通过 `setFallback` 设置的备用付费墙均保持不变。视图方法 `present`、`dismiss`、`setEventHandlers`、`showDialog`,以及事件处理器 `onCloseButtonPress`、`onUrlPress`、`onCustomAction`、`onProductSelected`、`onPurchaseStarted`、`onPurchaseCompleted`、`onPurchaseFailed`、`onRestoreStarted`、`onRestoreCompleted`、`onRestoreFailed`、`onLoadingProductsFailed`、`onWebPaymentNavigationFinished` 和 `onAndroidSystemBack` 与 v3 中的名称相同。用户引导方法仍可使用,但已被弃用——详见[用户引导 API 弃用说明](#onboarding-api-deprecation)。部分默认行为已发生变更——详见[默认行为变更](#default-behavior-changes)。 ## 最低版本要求 \{#minimum-versions\} 运行时要求与 v3.16+ 保持不变:**iOS 15.0**、**Android minSdk 24** 以及 **Capacitor 8**,无需更改部署目标。 有一项新的构建要求:**Xcode 26 或更高版本** —— 此版本捆绑的原生 Adapty iOS SDK 4.0.0-beta.2 使用 Swift tools 6.2。 v4 捆绑了原生 Adapty SDK iOS 4.0.0-beta.2 和 Android BOM 4.0.0-beta.1。 ## 安装 \{#installation\} ### 更新包 \{#update-the-package\} v4.0 是预发布版本,请固定精确版本号——npm 不会通过脱字符/波浪号范围选择预发布版本: ```bash showLineNumbers npm install @adapty/capacitor@4.0.0-beta.2 ``` 然后同步原生项目: ```bash showLineNumbers npx cap sync ``` ### iOS:仅支持 Swift Package Manager \{#ios-swift-package-manager-only\} [CocoaPods 的 spec 仓库将于 2026 年 12 月进入只读模式](https://blog.cocoapods.org/CocoaPods-Specs-Repo/),因此从 v4 起 `AdaptyCapacitor.podspec` 已被移除,SDK 在 iOS 上**仅通过 Swift Package Manager(SPM)安装**。你的应用 iOS 工程必须使用 Capacitor 的 SPM 集成方式: - 新项目:使用 SPM 包管理器添加 iOS 平台: ```bash showLineNumbers npx cap add ios --packagemanager SPM ``` - 现有 CocoaPods 项目:按照 [Capacitor 关于在现有项目中使用 SPM 的指南](https://capacitorjs.com/docs/ios/spm#using-spm-in-an-existing-capacitor-project)迁移 iOS 项目。 参阅 [安装 Adapty SDK](sdk-installation-capacitor) 了解完整配置步骤。 ## 获取流程 \{#fetching-flows\} ### getPaywall → getFlow 返回类型从 `AdaptyPaywall` 变更为 `AdaptyFlow`,同时移除了 `locale` 选项——渲染 flow 时,语言环境会自动解析;对于自定义付费墙,所有语言环境均通过 `flow.remoteConfigs` 返回: ```diff showLineNumbers - const paywall = await adapty.getPaywall({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en' }); + const flow = await adapty.getFlow({ placementId: 'YOUR_PLACEMENT_ID' }); ``` `getPaywallForDefaultAudience` 以相同方式重命名: ```diff showLineNumbers - const paywall = await adapty.getPaywallForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID', locale: 'en' }); + const flow = await adapty.getFlowForDefaultAudience({ placementId: 'YOUR_PLACEMENT_ID' }); ``` ### getPaywallProducts(paywall) → getPaywallProducts(flow) `getPaywallProducts` 保持原名,但现在接收一个 `AdaptyFlow`: ```diff showLineNumbers - const products = await adapty.getPaywallProducts({ paywall }); + const products = await adapty.getPaywallProducts({ flow }); ``` ## 数据模型 \{#data-model\} `getFlow` 返回的是 `AdaptyFlow` 而非 `AdaptyPaywall`,对象结构也发生了变化: | v3 `AdaptyPaywall` 字段 | v4 `AdaptyFlow` 字段 | 操作 | |---|---|---| | `remoteConfig?`(单个) | `remoteConfigs?: AdaptyRemoteConfig[]`(数组) | 一个流程为每种已配置的语言携带一个远程配置。读取与用户匹配的那个:`flow.remoteConfigs?.find((c) => c.lang === 'en')`。 | | `products` | `flow.paywalls[i].productIdentifiers` | 产品标识符现在位于每个流程变体上,而不是在流程本身。 | | `webPurchaseUrl?` | `flow.paywalls[i].webPurchaseUrl` | 从流程移至每个付费墙变体。 | | `version?: number` | `flowVersionId?: string` | 已重命名,类型从 `number` 更改为 `string`。 | | `hasViewConfiguration` | 已移除 | 从代码中删除所有 `hasViewConfiguration` 检查——`createFlowView` 现在会直接抛出异常(参见[展示流程](#displaying-flows))。 | | `requestLocale` | 已移除 | 语言区域不再是模型的一部分。 | | _(新增)_ | `paywalls: AdaptyFlowPaywall[]` | 每个条目是流程中的一个付费墙变体。 | | _(新增)_ | `responseCreatedAt: number` | 服务器响应时间戳,单位为毫秒。 | `hasViewConfiguration` 和 `requestLocale` 保留在 `AdaptyOnboarding` 上——只有流程模型移除了它们。 产品标识符从流程移至每个实验变体: ```diff showLineNumbers - const ids = paywall.products; + const ids = flow.paywalls[0].productIdentifiers; ``` ## Web 付费墙方法 \{#web-paywall-methods\} `openWebPaywall` 和 `createWebPaywallUrl` 保持原名不变,但 `paywallOrProduct` 选项现在接收 `AdaptyFlowPaywall`(流程变体)而非 `AdaptyPaywall`。你仍然可以传入 `AdaptyPaywallProduct`。在读取第一个条目之前,请先确认 `flow.paywalls` 非空: ```diff showLineNumbers const flow = await adapty.getFlow({ placementId: 'YOUR_PLACEMENT_ID' }); - await adapty.openWebPaywall({ paywallOrProduct: paywall }); + await adapty.openWebPaywall({ paywallOrProduct: flow.paywalls[0] }); ``` ## 跟踪流程查看次数 \{#tracking-flow-views\} ### logShowPaywall → logShowFlow `logShowPaywall` 已重命名为 `logShowFlow`,现在接受一个 `AdaptyFlow` 参数。该事件仍针对相同的实验变体进行记录,因此现有的漏斗和 A/B 测试数据图表无需修改看板即可继续正常使用。 ```diff showLineNumbers - await adapty.logShowPaywall({ paywall }); + await adapty.logShowFlow({ flow }); ``` 与 v3 一样,当展示由 [Flow Builder](adapty-flow-builder) 或[付费墙编辑工具](adapty-paywall-builder)渲染的流程或付费墙时,无需手动调用此方法——Adapty 会自动追踪这些视图。 ## 显示流程 \{#displaying-flows\} ### createPaywallView → createFlowView 重命名工厂函数并传入 `AdaptyFlow`。返回的控制器从 `PaywallViewController` 更名为 `FlowViewController`,但其方法(`present`、`dismiss`、`setEventHandlers`、`showDialog`)保持不变。参数类型从 `CreatePaywallViewParamsInput` 更名为 `CreateFlowViewParamsInput`: ```diff showLineNumbers - import { createPaywallView } from '@adapty/capacitor'; + import { createFlowView } from '@adapty/capacitor'; - const view = await createPaywallView(paywall); + const view = await createFlowView(flow); await view.present(); ``` 如果 flow 未配置视图,`createFlowView` 会抛出 `AdaptyError`——这取代了 v3 中的 `hasViewConfiguration` 检查: ```diff showLineNumbers - if (paywall.hasViewConfiguration) { - const view = await createPaywallView(paywall); - await view.present(); - } + try { + const view = await createFlowView(flow); + await view.present(); + } catch (error) { + // the flow has no view configured, or view creation failed + } ``` :::note Flow 视图是一次性的:调用 `dismiss()` 后,视图会被销毁,其事件处理器也会被清除。如需再次展示该 flow,请重新调用 `createFlowView`。 ::: ### Android 安全区域内边距 \{#android-safe-area-paddings\} `CreateFlowViewParamsInput` 新增了一个参数:`enableSafeArea`,用于在运行时控制 Android 安全区域内边距。该参数嵌套在 `android` 键下,默认值为 `true`: ```typescript showLineNumbers const view = await createFlowView(flow, { android: { enableSafeArea: true }, }); ``` ## 处理事件 \{#handling-events\} 事件处理器接口从 `EventHandlers` 重命名为 `FlowEventHandlers`,同时有一个回调也进行了重命名。现有的处理器逻辑无需修改代码,只需重命名即可: ```diff showLineNumbers - onRenderingFailed: (error) => { /* … */ }, + onError: (error) => { /* … */ }, ``` 所有其他事件处理函数保持原有名称不变。其中两个新增了第二个参数:`onPurchaseCompleted` 现在是 `(purchaseResult, product)`,`onPurchaseFailed` 现在是 `(error, product)`,其中 `product` 是涉及的 `AdaptyPaywallProduct`。完整列表请参阅[处理 flow 与付费墙事件](capacitor-handling-events)。 v4 还新增了一些可选功能: - `adapty.openWebUrl({ url, openIn })` 和 `adapty.requestAppReview()` 方法——这两个方法支持默认的 `onUrlPress` 和 `onRequestAppReview` 处理器,因此 URL 和应用评价提示默认即可原生处理。仅在覆盖这些处理器时才需直接调用它们。 - 通过新的 `onObserverPurchaseInitiated` / `onObserverRestoreInitiated` 处理器,在流程中支持观察者模式下的购买处理。详见[在观察者模式下展示流程](capacitor-present-flows-in-observer-mode)。 ## 默认行为变更 \{#default-behavior-changes\} 这些变更不会导致编译错误,请在运行时进行测试: - **`onAndroidSystemBack`**:默认行为已从关闭视图改为保持打开状态。若要恢复之前的行为,请在处理程序中返回 `true`。 - **`onPurchaseCompleted`**:默认行为已从关闭视图(用户取消购买时除外)改为始终保持打开状态。若要恢复之前的行为,请在处理程序中返回 `purchaseResult.type !== 'user_cancelled'`。 - **`onRestoreCompleted`**:默认行为已从恢复成功后关闭视图改为保持打开状态。若要恢复之前的行为,请在处理程序中返回 `true`。 - **`onUrlPress`**:默认行为现在通过原生层打开 URL,遵循看板中配置的应用内浏览器或外部浏览器设置。如需自行处理 URL 的打开方式,请覆盖该处理程序。 - **视图仅可使用一次**:调用 `dismiss()` 后,视图将被销毁。如需再次展示该流程,请重新调用 `createFlowView`。 ## 已移除的 API \{#removed-apis\} ### 已移除的导出 \{#removed-exports\} 以下符号已不再从 `@adapty/capacitor` 中导出,请移除相关导入: - **`AdaptyPaywall`**:请改用 `AdaptyFlow` 和 `AdaptyFlowPaywall`。 - **`ProductReference`**:请改用 `AdaptyProductIdentifier`,从 `flow.paywalls[i].productIdentifiers` 中读取。 - **`AdaptyPaywallBuilder`**:已移除。流程和付费墙现在以原生方式渲染。 - **`AdaptyAndroidSubscriptionUpdateParameters`**:请改用嵌套的 `android` 购买参数结构(详见下文)。 ### activate: lockMethodsUntilReady `lockMethodsUntilReady`(在 v3 中已被弃用为空操作)现已移除。请从 `activate` 调用中删除它——保留该参数将无法编译: ```diff showLineNumbers - await adapty.activate({ apiKey: 'PUBLIC_SDK_KEY', params: { lockMethodsUntilReady: true } }); + await adapty.activate({ apiKey: 'PUBLIC_SDK_KEY' }); ``` ### makePurchase:Android 参数 \{#makepurchase-android-parameters\} 已废弃的 `MakePurchaseParamsInput` 扁平 Android 格式已被移除,现在只保留嵌套形式。请将所有 Android 购买参数迁移到 `params: { android: { ... } }` 中。完整示例请参阅[发起购买](capacitor-making-purchases)。 ## 用户引导 API 弃用 \{#onboarding-api-deprecation\} 旧版用户引导 API 已在 v4.0 中弃用,请改用 [Flow Builder](adapty-flow-builder)。该 API 目前仍可正常使用,但将在未来版本中移除,请提前将您的用户引导迁移至 Flow Builder。 已弃用的符号:`getOnboarding`、`getOnboardingForDefaultAudience`、`createOnboardingView` 和 `OnboardingViewController`。 --- # File: migration-to-capacitor-316 --- --- title: "将 Adapty Capacitor SDK 迁移至 v3.16" description: "迁移至 Adapty Capacitor SDK v3.16,享受更佳性能与全新变现功能。" --- 从 Adapty SDK v3.16.0 起,需要使用 Capacitor 8。如果你需要使用 Capacitor 7,请使用 Adapty SDK v3.15。 如需升级至 Capacitor SDK v3.16,请确保你的项目使用的是 Capacitor 8。如果你仍在使用 Capacitor 7,有以下两种选择: 1. **升级到 Capacitor 8**:按照 [Capacitor 官方迁移指南](https://capacitorjs.com/docs/updating/8-0) 更新项目,然后安装 Adapty SDK v3.16。 2. **继续使用 Adapty SDK v3.15**:如果暂时无法升级到 Capacitor 8,可以继续使用支持 Capacitor 7 的 Adapty SDK v3.15。 --- # End of Documentation _Generated on: 2026-07-24T13:01:53.322Z_ _Successfully processed: 45/45 files_ # FLUTTER - 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.323Z Total files: 44 --- # File: sdk-installation-flutter --- --- title: "安装与配置 Flutter SDK" description: "在 Flutter 上安装 Adapty SDK 的分步指南,适用于基于订阅的应用。" --- Adapty SDK 包含两个核心模块,可无缝集成到您的 Flutter 应用中: - **Core Adapty**:这是 Adapty 正常运行所必需的核心 SDK。 - **AdaptyUI**:如果你使用 [Adapty 付费墙编辑工具](adapty-paywall-builder)(一款无需编写代码即可轻松创建跨平台付费墙的可视化工具),则需要此模块。 :::tip 想看看 Adapty SDK 在真实移动应用中是如何集成的?欢迎查看我们的[示例应用](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example),其中展示了完整的集成配置,包括显示付费墙、完成购买以及其他基础功能。 ::: ## 要求 \{#requirements\} Adapty SDK 支持 iOS 13.0+,但要正常使用付费墙编辑工具创建的付费墙,需要 iOS 15.0+。 Adapty Flutter SDK 4.0——新增了 [Flow Builder](adapty-flow-builder) 支持——将最低要求提升至 **iOS 15.0+**、**Xcode 26+** 以及 **Flutter 3.32.0+**(Dart 3.8.0+)。安装详情请参阅下方的 [Adapty SDK 4.0](#adapty-sdk-40-swift-package-manager)。 :::info Adapty 兼容 Google Play Billing Library 8.x 及以下版本。默认情况下,Adapty 使用 Google Play Billing Library v7.0.0,但如果你想强制使用更高版本,可以手动[添加依赖项](https://developer.android.com/google/play/billing/integrate#dependency)。 ::: :::info 安装 SDK 是 Adapty 配置流程的第 5 步。在应用内购买正常运行之前,您还需要将应用连接到各应用商店,然后在 Adapty 看板中创建产品、付费墙和版位。[快速入门指南](quickstart)涵盖了所有必要步骤。 ::: ## 安装 Adapty SDK \{#install-adapty-sdk\} [](https://github.com/adaptyteam/AdaptySDK-Flutter/releases) :::important 以下步骤安装的是最新稳定版 SDK(3.x)。如果你需要 v4([Flow Builder](adapty-flow-builder) 所需,[快速入门](flutter-quickstart-paywalls)也使用该版本),请直接参考下方的 [Adapty SDK 4.0: Swift Package Manager](#adapty-sdk-40-swift-package-manager)。 ::: 1. 将 Adapty 添加到你的 `pubspec.yaml` 文件中: ```yaml showLineNumbers title="pubspec.yaml" dependencies: adapty_flutter: ^
### 登录/注册时 \{#during-loginsignup\}
如果您是在应用启动后才识别用户身份(例如在用户登录或注册之后),请使用 `identify` 方法来设置其 customer user ID。
- 如果您**之前从未使用过该 customer user ID**,Adapty 会自动将其与当前用户画像关联。
- 如果您**之前已使用该 customer user ID 识别过用户**,Adapty 将切换至与该 customer user ID 关联的用户画像。
:::important
客户用户 ID 对每个用户必须唯一。如果将参数值硬编码,所有用户将被视为同一个人。
:::
调用其他 SDK 方法之前,请务必 `await` `identify`。并发调用会产生 `#3006 profileWasChanged` 错误或落到匿名用户画像上。详见 [Flutter SDK 的调用顺序](flutter-sdk-call-order)。
```dart showLineNumbers
try {
await Adapty().identify(customerUserId); // 每个用户唯一
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
```
### 在 SDK 激活期间 \{#during-the-sdk-activation\}
如果在激活 SDK 时已知道用户的 customer user ID,可以直接在 `activate` 方法中传入,无需单独调用 `identify`。
如果已知 customer user ID,但在激活之后才进行设置,则意味着在激活时 Adapty 会创建一个新的匿名用户画像,并仅在你调用 `identify` 后才切换到已有的用户画像。
您可以传入已有的 customer user ID(之前使用过的),也可以传入一个全新的。如果传入新的,激活时创建的新用户画像将自动与该 customer user ID 关联。
:::note
默认情况下,创建匿名用户画像不会影响分析看板,因为安装量是基于设备 ID 统计的。
设备 ID 代表应用在设备上的一次安装实例,仅在应用重新安装后才会重新生成。
无论是首次安装还是重复安装,也无论是否使用了已有的客户用户 ID,设备 ID 均不受影响。
创建用户画像(在 SDK 激活或退出登录时)、登录,或在不重新安装应用的情况下升级应用,均不会产生新的安装事件。
如果您希望按唯一用户而非设备来统计安装量,请前往 **App settings** 并配置 [**Installs definition for analytics**](general#4-installs-definition-for-analytics)。
:::
```dart showLineNumbers"
try {
await Adapty().activate(
configuration: AdaptyConfiguration(apiKey: 'YOUR_API_KEY')
..withCustomerUserId(YOUR_CUSTOMER_USER_ID) // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
);
} catch (e) {
// handle the error
}
```
### 退出用户登录 \{#log-users-out\}
如果您有供用户退出登录的按钮,请使用 `logout` 方法。
:::important
退出用户登录会为该用户创建新的匿名用户画像。
:::
```dart showLineNumbers
try {
await Adapty().logout();
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle unknown error
}
```
:::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`](flutter-check-subscription-status),也可以[监听用户画像更新](flutter-check-subscription-status)以实现数据自动同步。
## 下一步 \{#next-steps\}
恭喜!您已经在应用中完成了应用内支付逻辑的接入!祝您的应用变现一切顺利!
要充分发挥 Adapty 的价值,可以探索以下主题:
- [**测试**](troubleshooting-test-purchases):确保一切正常运行
- [**用户引导**](flutter-onboardings):通过用户引导吸引用户并提升留存率
- [**集成**](configuration):只需一行代码,即可与营销归因和分析服务集成
- [**设置自定义用户画像属性**](flutter-setting-user-attributes):为用户画像添加自定义属性并创建市场细分,以便针对不同用户启动 A/B 测试或展示不同的付费墙
---
# File: adapty-sdk-integration-skill-flutter
---
---
title: "使用 SDK 集成技能将 Adapty 集成到 Flutter 应用"
description: "使用 adapty-sdk-integration 技能,借助 AI 编程工具将 Adapty SDK 端到端集成到 Flutter 应用中。"
---
默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此选项,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在的情况下直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全可靠的。
请注意,缓存在应用重启后依然保留,只有在应用重新安装或手动清除时才会被清空。
Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 来加快付费墙的获取速度,并在 CDN 不可用时提供独立的备用服务器。整套机制旨在确保你始终能获取最新版本的付费墙,同时在网络条件较差时也能保证可靠性。
| | **loadTimeout** | 默认:5 秒 |限制该方法超时时间的 `Duration` 值。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为底层操作可能由多个请求组成。
| 响应参数: | 参数 | 描述 | | :-------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Flow | 一个 `AdaptyFlow` 对象,包含流程的标识符(`instanceIdentity`、`variationId`)、名称、版位、其付费墙实验变体(`paywalls`)以及任何远程配置(`remoteConfigs`)。 | ## 获取视图配置 \{#fetch-the-view-configuration\} :::important 请确保在编辑工具中启用了 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 如果版位是在 **Flow Builder** 或 **Paywall Builder** 中设计的,Adapty 会自动为你渲染 UI —— 所获取的 flow 的 `hasViewConfiguration` 属性为 `true`。使用 `createFlowView` 创建视图,然后[展示 flow 或付费墙](flutter-present-paywalls)。如果版位是未使用编辑工具的自定义付费墙(`hasViewConfiguration` 为 `false`),请改为[将其作为远程配置付费墙处理](present-remote-config-paywalls-flutter)。 :::warning `createFlowView` 方法的返回结果只能呈现一次。如需再次呈现,请重新调用 `createFlowView` 方法。 ::: ```dart showLineNumbers try { final view = await AdaptyUI().createFlowView(flow: flow); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | | :------------------- | :------- | :----------------------------------------------------------- | | **flow** | 必填 | 一个 `AdaptyFlow` 对象,用于获取所需流程/付费墙的视图。 | | **customTags** | 选填 | 定义自定义标签及其对应值的映射。自定义标签作为内容中的占位符,在流程/付费墙中动态替换为指定字符串,实现个性化内容。详情请参阅[付费墙编辑工具中的自定义标签](custom-tags-in-paywall-builder)。 | | **preloadProducts** | 选填 | 启用后可优化产品在屏幕上的显示时机。设为 `true` 时,AdaptyUI 将自动预加载所需产品。默认值:`false`。 | | **loadTimeout** | 选填 | 一个 `Duration`,用于限制视图配置的加载超时时间。若超时,将使用缓存数据或本地备用内容。 | :::note 如果您使用多种语言,请了解如何添加[流程本地化](add-paywall-locale-in-adapty-paywall-builder),以及如何正确使用语言区域代码([点击此处了解](flutter-localizations-and-locale-codes))。 ::: 完成视图设置后,[展示流程/付费墙](flutter-present-paywalls)。 ## 获取默认目标受众的流程或付费墙以加快加载速度 \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,流程和付费墙的加载几乎是即时完成的,无需担心速度问题。但如果你配置了大量目标受众和版位,且用户的网络连接较弱,加载流程或付费墙可能会比预期慢。在这种情况下,你可能希望先展示默认流程或付费墙,以保证用户体验的流畅性,而不是什么都不显示。 为了解决这个问题,你可以使用 `getFlowForDefaultAudience` 方法,该方法会获取指定版位中针对 **All Users** 目标受众的流程或付费墙。但需要特别注意的是,推荐的做法是通过 `getFlow` 方法来获取流程或付费墙,详情请参阅上方的[获取流程/付费墙](#fetch-flowpaywall)章节。 :::warning 为什么我们推荐使用 `getFlow` `getFlowForDefaultAudience` 方法存在一些明显的缺点: - **潜在的向后兼容性问题**:如果你需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会面临挑战。你要么针对当前(旧版)版本设计兼容的付费墙,要么接受旧版用户可能遇到付费墙无法渲染的问题。 - **目标定向失效**:所有用户都将看到针对 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向能力(包括基于国家、营销归因或自定义属性的定向)。 如果你愿意接受这些弊端以换取更快的流程或付费墙加载速度,请按如下方式使用 `getFlowForDefaultAudience` 方法。否则,请继续使用[上文](#fetch-flowpaywall)介绍的 `getFlow`。 ::: ```dart showLineNumbers try { final flow = await Adapty().getFlowForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID'); // the requested flow/paywall } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle unknown error } ``` | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements) 的标识符。这是您在 Adapty 看板中创建版位时所指定的值。 | | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方案,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存数据存在时直接返回缓存。这样一来,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清理时才会被清除。
| ## 自定义资源 \{#customize-assets\} 要自定义流程/付费墙中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,您需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,您可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在播放视频前先显示预览图。 以下是如何通过简单字典提供自定义资源的示例: ```dart final customAssets = { // Show a local image using a custom ID 'custom_image': AdaptyCustomAsset.localImageAsset( assetId: 'assets/images/image_name.png', ), // Show a local video with a preview image 'hero_video': AdaptyCustomAsset.localVideoAsset( assetId: 'assets/videos/custom_video.mp4', ), }; try { final view = await AdaptyUI().createFlowView( flow: flow, customAssets: customAssets, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` :::note 如果找不到某个资源,流程/付费墙将回退到其默认外观。 ::: ## 设置开发者自定义计时器 \{#set-up-developer-defined-timers\} 要在移动应用中使用自定义计时器,请将 `customTimers` 映射传递给 `createFlowView` 方法。映射中每个键为计时器 ID,对应的值为定义计时器结束时间的 `DateTime` 对象。示例如下: ```dart showLineNumbers try { final view = await AdaptyUI().createFlowView( flow: flow, customTimers: { 'CUSTOM_TIMER_6H': DateTime.now().add(const Duration(seconds: 3600 * 6)), 'CUSTOM_TIMER_NY': DateTime(2027, 1, 1), // New Year 2027 }, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` 在此示例中,`CUSTOM_TIMER_NY` 和 `CUSTOM_TIMER_6H` 是你在 Adapty 看板中设置的开发者自定义计时器的 **Timer ID**。`customTimers` 映射可确保你的应用动态地为每个计时器更新正确的值。例如: - `CUSTOM_TIMER_NY`:距计时器结束时间(如元旦)的剩余时间。 - `CUSTOM_TIMER_6H`:用户打开流程后,6 小时倒计时的剩余时间。可选
默认值:`en`
|[付费墙本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符。该参数应为语言代码,由一个或两个子标签组成,子标签之间用连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐用法,请参阅[本地化与语言区域代码](flutter-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时直接返回缓存数据。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清除时才会被清空。
Adapty SDK 在本地以两层方式存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加快付费墙的获取速度,并在 CDN 不可用时提供独立的备用服务器。这套机制旨在确保你始终获取最新版本的付费墙,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为底层操作可能包含多个请求。
对于 Android:你可以使用扩展函数创建 `TimeInterval`(例如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或使用 `TimeInterval.seconds(5)`。如需不设限制,请使用 `TimeInterval.INFINITE`。
| 响应参数: | 参数 | 描述 | | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中开启 **Show on device** 开关。如果未启用此选项,将无法获取视图配置。 ::: 获取付费墙后,检查其是否包含 `ViewConfiguration`,这表明该付费墙是使用付费墙编辑工具创建的。这将帮助你确定如何展示该付费墙。如果存在 `ViewConfiguration`,则将其作为付费墙编辑工具付费墙处理;如果不存在,请[将其作为远程配置付费墙处理](present-remote-config-paywalls-flutter)。 ```dart showLineNumbers try { final view = await AdaptyUI().createPaywallView( paywall: paywall, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` 获取视图后,[展示付费墙](flutter-present-paywalls)。 ## 为默认目标受众获取付费墙以加快加载速度 \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,付费墙的获取几乎是即时完成的,无需担心速度问题。但如果你配置了大量目标受众和付费墙,且用户的网络连接较差,获取付费墙可能会比预期耗时更长。在这种情况下,你可能希望优先展示默认付费墙,以保证流畅的用户体验,而不是让用户看到空白页面。 要解决这个问题,你可以使用 `getPaywallForDefaultAudience` 方法,它会获取指定版位中 **All Users** 目标受众对应的付费墙。但需要特别注意的是,推荐的做法是通过 `getPaywall` 方法来获取付费墙,详见上方的[获取付费墙信息](flutter-get-pb-paywalls#fetch-paywall-designed-with-paywall-builder)章节。 :::warning 为什么我们推荐使用 `getPaywall` `getPaywallForDefaultAudience` 方法存在以下几个明显的缺点: - **潜在的向后兼容问题**:如果你需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会遇到挑战。你要么将付费墙设计为同时兼容当前(旧版)版本,要么接受使用当前(旧版)版本的用户可能遇到付费墙无法渲染的问题。 - **失去定向能力**:所有用户都将看到专为 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向功能(包括基于国家、营销归因或自定义属性的定向)。 如果你愿意接受这些不足,以换取更快的付费墙加载速度,可按如下方式使用 `getPaywallForDefaultAudience` 方法。否则,请继续使用[上文](#fetch-paywall-designed-with-paywall-builder)介绍的 `getPaywall` 方法。 ::: ```dart showLineNumbers try { final paywall = await Adapty().getPaywallForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID'); } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle unknown error } ``` :::note `getPaywallForDefaultAudience` 方法从 Flutter SDK 3.2.0 版本起可用。 ::: | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements)的标识符。这是你在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间用短横线(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络状况不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存数据存在时直接返回缓存。这种方式下用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,缓存在应用重启后依然保留,只有在应用重新安装或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要自定义付费墙中的图片和视频,请实现自定义资源。 主图和主视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你可以通过这些 ID 定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 为部分用户显示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在播放视频前显示预览图片。 :::important 要使用此功能,请将 Adapty Flutter SDK 更新至 3.8.0 或更高版本。 ::: 以下是如何通过简单字典提供自定义资源的示例: ```dart final customAssets = { // Show a local image using a custom ID 'custom_image': AdaptyCustomAsset.localImageAsset( assetId: 'assets/images/image_name.png', ), // Show a local video with a preview image 'hero_video': AdaptyCustomAsset.localVideoAsset( assetId: 'assets/videos/custom_video.mp4', ), }; try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customAssets: customAssets, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` :::note 如果找不到相应素材,付费墙将回退到其默认外观。 ::: ## 设置自定义计时器 \{#set-up-developer-defined-timers\} 要在移动应用中使用自定义计时器,请将 `customTimers` 映射传递给 `createPaywallView` 方法。映射中的每个键是计时器 ID,对应的值是一个 `DateTime` 对象,用于定义计时器的结束时间。示例如下: ```dart showLineNumbers try { final view = await AdaptyUI().createPaywallView( paywall: paywall, customTimers: { 'CUSTOM_TIMER_6H': DateTime.now().add(const Duration(seconds: 3600 * 6)), 'CUSTOM_TIMER_NY': DateTime(2025, 1, 1), // New Year 2025 }, ); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` 在此示例中,`CUSTOM_TIMER_NY` 和 `CUSTOM_TIMER_6H` 是您在 Adapty 看板中设置的开发者自定义计时器的 **Timer ID**。`customTimers` 映射确保您的应用为每个计时器动态更新正确的值。例如: - `CUSTOM_TIMER_NY`:距计时器结束时间(如元旦)的剩余时长。 - `CUSTOM_TIMER_6H`:从用户打开付费墙开始计算的 6 小时倒计时剩余时长。
## 付费墙浏览次数过大 \{#the-paywall-view-number-is-too-big\}
**问题**:付费墙浏览次数显示为预期值的两倍。
**原因**:你可能在代码中调用了 `logShowFlow`(Flutter SDK v4+)/ `logShowPaywall`,如果你正在使用付费墙编辑工具或 Flow Builder,这会导致浏览次数重复计算。对于使用这些工具构建的流程和付费墙,分析数据会自动追踪,无需手动调用此方法。
**解决方案**:如果你正在使用付费墙编辑工具或 Flow Builder,请确保代码中没有调用 `logShowFlow`(Flutter SDK v4+)/ `logShowPaywall`。
## 其他问题 \{#other-issues\}
**问题**:您遇到了上述未涵盖的其他付费墙编辑工具相关问题。
**解决方案**:如有需要,请参考[迁移指南](flutter-sdk-migration-guides)将 SDK 升级至最新版本。许多问题已在较新的 SDK 版本中得到修复。
---
# File: flutter-present-flows-in-observer-mode
---
---
title: "在 Flutter SDK 的 Observer 模式下展示流程"
description: "在 Flutter 应用中以 Observer 模式展示流程和付费墙编辑工具付费墙,同时使用自己的代码处理购买。"
---
如果你使用编辑工具自定义了流程或付费墙,就无需在移动应用代码中手动处理其渲染逻辑来向用户展示它。这类流程或付费墙本身已包含展示内容和展示方式的完整定义。
:::warning
本节仅适用于[观察者模式](observer-vs-full-mode)。如果你不在观察者模式下工作,请参阅[展示流程与付费墙](flutter-present-paywalls)主题。
:::
:::info
此功能需要 Adapty Flutter SDK 4.0 或更高版本——此前仅在 iOS 和 Android 原生 SDK 中提供。请参阅[迁移指南](migration-to-flutter-sdk-v4)进行升级。
:::
默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储付费墙:上述定期更新的缓存,以及[备用付费墙](flutter-use-fallback-paywalls)。我们还使用 CDN 来加快付费墙的获取速度,并在 CDN 不可用时启用独立的备用服务器。整套系统旨在确保您始终能获取最新版本的付费墙,同时在网络连接不稳定的情况下也能保持可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个不同请求组成。
| :::note 在 v4 中,`getFlow` 不接受 `locale` 参数。对于自定义付费墙,所有可用的本地化内容都会通过流程的远程配置(`flow.remoteConfigs`)返回——从中选取与用户设备或应用设置匹配的那个。详见[本地化与语言代码](flutter-localizations-and-locale-codes)。 ::: 返回参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含流程的标识符(`instanceIdentity`、`variationId`)、名称、版位、付费墙实验变体(`paywalls`)以及远程配置(`remoteConfigs`)。 | ## 获取产品 \{#fetch-products\} 获取到 flow 后,你可以查询与其对应的产品数组: ```dart showLineNumbers try { final products = await Adapty().getPaywallProducts(flow: flow); // the requested products array } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle the error } ``` 响应参数: | 参数 | 描述 | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他几个属性。 | 在实现自定义付费墙设计时,您可能需要访问 [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) 对象中的这些属性。下面列出了最常用的属性,有关所有可用属性的完整详情,请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title(标题)** | 要显示产品标题,请使用 `product.localizedTitle`。请注意,本地化基于用户选择的商店国家/地区,而非设备本身的语言区域设置。 | | **Price(价格)** | 要显示本地化版本的价格,请使用 `product.price.localizedString`。该本地化基于设备的语言区域信息。您也可以使用 `product.price.amount` 以数字形式访问价格,该值以本地货币提供。要获取关联的货币符号,请使用 `product.price.currencySymbol`。 | | **Subscription Period(订阅周期)** | 要显示订阅周期(例如周、月、年等),请使用 `product.subscription?.localizedPeriod`。该本地化基于设备的语言区域设置。要以编程方式获取订阅周期,请使用 `product.subscription?.period`。从中可以访问 `unit` 枚举以获取时长(即天、周、月、年或未知)。`numberOfUnits` 值将获取周期单位的数量。例如,对于季度订阅,`unit` 属性中将显示 `AdaptyPeriodUnit.month`,`numberOfUnits` 属性中将显示 `3`。 | | **Introductory Offer(新用户优惠)** | 要显示标记或其他指示符以表明订阅包含新用户优惠,请查看 `product.subscription?.offer?.phases` 属性。这是一个最多可包含两个折扣阶段的列表:免费试用阶段和优惠价格阶段。每个阶段对象包含以下有用属性:默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在时优先返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清除时才会被清空。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个以减号(**-**)分隔的子标签组成的语言代码。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及其推荐用法的更多信息,请参阅[本地化与语言代码](flutter-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 将尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此方式,因为它确保用户始终获得最新数据。
但是,如果您认为用户网络不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,重启应用后缓存保持不变,只有在卸载应用或手动清理时才会被清除。
Adapty SDK 在两层中存储付费墙:上述定期更新的缓存和[备用付费墙](flutter-use-fallback-paywalls)。我们还使用 CDN 加快付费墙获取速度,并在 CDN 不可达时使用独立的备用服务器。该系统旨在确保您始终获得最新版本的付费墙,同时在网络连接不佳的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作可能在内部由不同请求组成。
| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品、产品数量以及特殊优惠(如免费试用)可能随时变化。请确保您的代码能够动态处理这些情况。 例如,如果您最初获取了 2 个产品,您的应用应显示这 2 个产品。但是,如果您后来获取了 3 个产品,您的应用应显示全部 3 个,而无需修改任何代码。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他几个属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,您可以查询与之对应的产品数组: ```dart showLineNumbers try { final products = await Adapty().getPaywallProducts(paywall: paywall); // the requested products array } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` 响应参数: | 参数 | 描述 | | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Products | [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他多个属性。 | 在实现自定义付费墙设计时,你可能需要访问 [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) 对象中的这些属性。以下列出了最常用的属性,完整属性列表请参阅上方链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 使用 `product.localizedTitle` 显示产品名称。请注意,本地化基于用户在商店中选择的国家/地区,而非设备本身的语言环境。 | | **Price** | 使用 `product.price.localizedString` 显示本地化价格,该本地化基于设备的语言环境信息。也可以通过 `product.price.amount` 以数字形式获取价格,其值以本地货币计。如需获取对应的货币符号,请使用 `product.price.currencySymbol`。 | | **Subscription Period** | 使用 `product.subscription?.localizedPeriod` 显示订阅周期(如周、月、年等),该本地化基于设备语言环境。如需以编程方式获取订阅周期,请使用 `product.subscription?.period`,通过 `unit` 枚举可获取周期长度(即 day、week、month、year 或 unknown),`numberOfUnits` 则表示周期单位的数量。例如,对于季度订阅,`unit` 属性值为 `AdaptyPeriodUnit.month`,`numberOfUnits` 属性值为 `3`。 | | **Introductory Offer** | 如需显示徽章或其他标识以表明订阅包含新用户优惠,请查看 `product.subscription?.offer?.phases` 属性。这是一个列表,最多包含两个折扣阶段:免费试用阶段和新用户价格阶段。每个阶段对象包含以下实用属性:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个以减号(**-**)分隔的子标签组成的语言代码。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及其推荐用法的更多信息,请参阅[本地化与语言代码](flutter-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 将尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此方式,因为它确保用户始终获得最新数据。
但是,如果您认为用户网络不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,重启应用后缓存保持不变,只有在卸载应用或手动清理时才会被清除。
|请求成功后,响应中会包含此对象。[AdaptyProfile](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.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()` 方法时传入额外参数: ```dart showLineNumbers try { final subscriptionUpdateParams = AdaptyAndroidSubscriptionUpdateParameters( 'OLD_PRODUCT_ID', AdaptyAndroidSubscriptionUpdateReplacementMode.immediateWithTimeProration, ); final result = await Adapty().makePurchase( product: product, parameters: AdaptyPurchaseParameters( subscriptionUpdateParams: subscriptionUpdateParams, ), ); // successful cross-grade } on AdaptyError catch (adaptyError) { // Handle the error } catch (e) { // Handle the error } ``` 附加请求参数: | 参数 | 是否必填 | 描述 | | :--------------------------- | :------- |:--------------------------------------------------------------------------------------------------------| | **parameters** | required | 一个 `AdaptyPurchaseParameters` 对象,其 `subscriptionUpdateParams` 字段需设置为 [`AdaptyAndroidSubscriptionUpdateParameters`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyAndroidSubscriptionUpdateParameters-class.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\}一个 [`AdaptyProfile`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html) 对象。该模型包含访问等级、订阅和非订阅购买的相关信息。
请检查**访问等级状态**以确定用户是否有权访问应用。
| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: implement-observer-mode-flutter --- --- title: "在 Flutter SDK 中实现观察者模式" description: "在 Adapty 中实现观察者模式,以在 Flutter SDK 中跟踪用户订阅事件。" --- 如果你已有自己的购买基础设施,暂时还不打算完全切换到 Adapty,可以了解一下[观察者模式](observer-vs-full-mode)。在基础形态下,观察者模式提供了高级分析功能,并能与归因和分析系统无缝集成。 如果这满足您的需求,只需: 1. 在配置 Adapty SDK 时,将 `observerMode` 参数设置为 `true` 以启用该功能。请参阅 [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) 的设置说明。 2. 将现有购买基础设施中的[交易上报](report-transactions-observer-mode-flutter)给 Adapty。 ## 观察者模式设置 \{#observer-mode-setup\} 如果你自己处理购买和订阅状态,并使用 Adapty 发送订阅事件和分析数据,请开启观察者模式。 :::important 在观察者模式下运行时,Adapty SDK 不会关闭任何交易,请确保你自己处理。 ::: ```dart showLineNumbers title="main.dart" await Adapty().activate( configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY') ..withObserverMode(true) // Enable observer mode ..withLogLevel(AdaptyLogLevel.verbose), ); ``` 参数: | 参数 | 描述 | | --------------------------- | ------------------------------------------------------------ | | observerMode | 一个布尔值,用于控制[观察者模式](observer-vs-full-mode)。默认值为 `false`。 | ## 在 Observer 模式下使用 Adapty 付费墙 \{#using-adapty-paywalls-in-observer-mode\} 如果你还想使用 Adapty 的付费墙和 A/B 测试功能,也是可以的——但在 Observer 模式下需要一些额外配置。除了上述步骤之外,你还需要完成以下操作: 1. 按照常规方式展示[远程配置付费墙](present-remote-config-paywalls-flutter)。 3. 将付费墙与购买交易[进行关联](report-transactions-observer-mode-flutter)。 :::tip 在 SDK v4 中,你也可以在 Observer 模式下展示 Adapty 渲染的流程和付费墙:注册一个 `AdaptyUIObserverModeResolver`,当用户点击对应按钮时,用你自己的代码执行购买或恢复操作。详见[在 Observer 模式下展示流程](flutter-present-flows-in-observer-mode)。 ::: --- # File: report-transactions-observer-mode-flutter --- --- title: "在 Flutter SDK 的观察者模式下上报交易" description: "在 Adapty 观察者模式下上报购买交易,用于 Flutter SDK 的用户洞察和收入跟踪。" ---phoneNumber
firstName
lastName
| String | | gender | 枚举类型,允许的值为:`female`、`male`、`other` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 你可以设置自己的自定义用户属性,这些属性通常与应用的使用情况相关。例如,健身类应用可以记录每周的锻炼次数,语言学习类应用可以记录用户的知识水平等。你可以在市场细分中使用这些属性来创建精准的付费墙和优惠活动,也可以在分析中利用它们来找出哪些产品数据图表对收入影响最大。 ```dart showLineNumbers try { final builder = AdaptyProfileParametersBuilder() ..setCustomStringAttribute('value1', 'key1') ..setCustomDoubleAttribute(1.0, 'key2'); await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` 要删除已有的键,请使用 `.withRemoved(customAttributeForKey:)` 方法: ```dart showLineNumbers try { final builder = AdaptyProfileParametersBuilder() ..removeCustomAttribute('key1') ..removeCustomAttribute('key2'); await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` 有时你需要查看之前已设置了哪些自定义属性。为此,请使用 `AdaptyProfile` 对象的 `customAttributes` 字段。 :::warning 请注意,`customAttributes` 的值可能已过时,因为用户属性可以随时从不同设备发送,服务器上的属性可能在上次同步后已被修改。 ::: ### 限制 \{#limits\} - 每个用户最多 30 个自定义属性 - 键名最长为 30 个字符。键名可以包含字母数字字符以及以下任意字符:`_` `-` `.` - 值可以是字符串或浮点数,最多 50 个字符。 --- # File: flutter-listen-subscription-changes --- --- title: "在 Flutter SDK 中查看订阅状态" description: "在 Adapty 中追踪和管理用户订阅状态,提升 Flutter 应用的用户留存率。" --- 借助 Adapty,订阅状态管理变得轻而易举。你无需在代码中手动填入产品 ID,只需检查用户是否拥有有效的[访问等级](access-level),即可确认其订阅状态。一个 [AdaptyProfile](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyProfile-class.html) 对象。通常,你只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级访问权限。
`.getProfile` 方法始终尝试请求 API,因此返回的结果是最新的。如果由于某些原因(例如无网络连接)导致 Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。还需注意,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持数据的实时性。
| `.getProfile()` 方法可以获取用户画像,从中你可以了解访问等级的状态。一个应用可以拥有多个访问等级。例如,如果你有一个新闻应用,并对不同主题独立销售订阅,可以创建"sports"和"science"两个访问等级。但大多数情况下,你只需要一个访问等级,此时直接使用默认的"premium"访问等级即可。 以下是检查默认"premium"访问等级的示例: ```dart showLineNumbers try { final profile = await Adapty().getProfile(); if (profile?.accessLevels['premium']?.isActive ?? false) { // grant access to premium features } } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { } ``` ### 监听订阅状态更新 \{#listening-for-subscription-status-updates\} 每当用户的订阅发生变化时,Adapty 都会触发一个事件。 要接收来自 Adapty 的消息,您需要进行一些额外配置: ```dart showLineNumbers Adapty().didUpdateProfileStream.listen((profile) { // handle any changes to subscription state }); ``` Adapty 也会在应用启动时触发一个事件,此时将传递缓存中的订阅状态。 ### 订阅状态缓存 \{#subscription-status-cache\} Adapty SDK 内置了缓存机制,用于存储用户画像的订阅状态。这意味着即使服务器无法访问,也可以从缓存数据中获取用户画像的订阅状态信息。 但需要注意的是,无法直接从缓存中请求数据。SDK 每隔一分钟会定期向服务器查询,检查用户画像是否有任何更新或变更。如果存在修改(例如新的交易记录或其他更新),这些内容将同步到缓存数据中,以保持其与服务器的一致性。 --- # File: flutter-deal-with-att --- --- title: "在 Flutter SDK 中处理 ATT" description: "开始在 Flutter 上使用 Adapty,简化订阅设置与管理。" --- 如果您的应用使用了 AppTrackingTransparency 框架,并向用户展示应用跟踪授权请求,则应将[授权状态](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/)发送给 Adapty。 ```dart showLineNumbers final builder = AdaptyProfileParametersBuilder() ..setAppTrackingTransparencyStatus(AdaptyIOSAppTrackingTransparencyStatus.authorized); try { await Adapty().updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle the error } catch (e) { // handle unknown error } ``` :::warning 我们强烈建议您在该值发生变化时尽早发送,只有这样,数据才能及时传递到您所配置的集成渠道。 ::: --- # File: kids-mode-flutter --- --- title: "Flutter SDK 中的儿童模式" description: "轻松启用儿童模式,遵守 Apple 和 Google 政策。Flutter SDK 中不收集 IDFA、GAID 或广告数据。" --- 如果您的 Flutter 应用面向儿童,则必须遵守 [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) 此外,我们建议谨慎使用 customer user ID。以 `可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获得最新数据,但无论网络状况如何,都能体验更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在重启应用后依然保留,仅在重新安装应用或手动清理时才会清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存层和备用用户引导层。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可访问时使用独立备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接稀缺的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个不同请求组成。
| 响应参数: | 参数 | 描述 | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyOnboarding-class.html) 对象,包含:用户引导标识符和配置、远程配置以及其他几个属性。 | ## 使用默认目标受众用户引导加速获取 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常,用户引导几乎可以即时获取,因此您无需担心加速此过程。但是,当您有大量目标受众和用户引导,且用户网络连接较弱时,获取用户引导可能比预期耗时更长。在这种情况下,您可能希望展示默认用户引导以确保流畅的用户体验,而不是不显示任何内容。 为解决这一问题,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法会获取指定版位中针对**所有用户**目标受众的用户引导。但请务必了解,推荐的方式仍是通过 `getOnboarding` 方法获取用户引导,详见上方的[获取用户引导](#fetch-onboarding)部分。 :::warning 请考虑使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者有以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要设计向后兼容的界面,否则旧版本可能显示异常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法基于国家、归因或自定义属性进行定向。 如果在您的使用场景中更快的获取速度优于上述缺点,请按以下方式使用 `getOnboardingForDefaultAudience`。否则,请按[上方](#fetch-onboarding)所述使用 `getOnboarding`。 ::: ```dart showLineNumbers try { final onboarding = await Adapty().getOnboardingForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID'); } on AdaptyError catch (adaptyError) { // handle error } catch (e) { // handle unknown error } ``` 参数: | 参数 | 是否必填 | 描述 | |-----------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由一个或两个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获得最新数据,但无论网络状况如何,都能体验更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在重启应用后依然保留,仅在重新安装应用或手动清理时才会清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存层和备用用户引导层。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可访问时使用独立备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接稀缺的情况下也能保证可靠性。
| --- # File: flutter-present-onboardings --- --- title: "在 Flutter SDK 中展示用户引导" description: "了解如何有效展示用户引导以提升转化率。" --- :::warning **用户引导功能已在 SDK v4 中弃用,并将在未来版本中移除。** 该功能不再接受修复或改进。请改用 [flows](flutter-get-pb-paywalls):与在 WebView 中运行的用户引导不同,flows 直接在设备上原生渲染,带来更流畅的动画、一致的原生外观体验、更快的加载速度,且无需依赖 WebView 运行时。请参阅 [获取 flows 与付费墙](flutter-get-pb-paywalls) 和 [展示 flows 与付费墙](flutter-present-paywalls) 快速上手。 ::: 如果您已使用编辑工具自定义了用户引导,则无需在 Flutter 应用代码中手动处理渲染逻辑来向用户展示它。这类用户引导已包含展示内容与展示方式的完整配置。 开始之前,请确认以下事项: 1. 您已安装 [Adapty Flutter SDK](sdk-installation-flutter) 3.8.0 或更高版本。 2. 您已[创建用户引导](create-onboarding)。 3. 您已将用户引导添加到[版位](placements)。 Adapty Flutter SDK 提供两种展示用户引导的方式: - **独立页面** - **嵌入式组件** ## 以独立屏幕呈现 \{#present-as-standalone-screen\} 要将用户引导以独立屏幕的形式展示,请对 `createOnboardingView` 方法创建的 `onboardingView` 调用 `onboardingView.present()` 方法。每个 `view` 只能使用一次。如果需要再次展示用户引导,请重新调用 `createOnboardingView` 创建一个新的 `onboardingView` 实例。 :::warning 重复使用同一个 `onboardingView` 而不重新创建,可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```dart showLineNumbers title="Flutter" try { await onboardingView.present(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### 关闭用户引导 \{#dismiss-the-onboarding\} 当需要以编程方式关闭用户引导时,请使用 `dismiss()` 方法: ```dart showLineNumbers title="Flutter" try { await onboardingView.dismiss(); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ### 配置 iOS 展示样式 \{#configure-ios-presentation-style\} 通过向 `present()` 方法传入 `iosPresentationStyle` 参数,可配置用户引导在 iOS 上的展示方式。该参数接受 `AdaptyUIIOSPresentationStyle.fullScreen`(默认值)或 `AdaptyUIIOSPresentationStyle.pageSheet` 值。 ```dart showLineNumbers try { await onboardingView.present(iosPresentationStyle: AdaptyUIIOSPresentationStyle.pageSheet); } on AdaptyError catch (e) { // handle the error } catch (e) { // handle the error } ``` ## 嵌入到 Widget 层级中 \{#embed-in-widget-hierarchy\} 若要将用户引导嵌入到现有的 Widget 树中,可直接在 Flutter Widget 层级里使用 `AdaptyUIOnboardingPlatformView` 组件。 ```dart showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, // The onboarding object you fetched onDidFinishLoading: (meta) { }, onDidFailWithError: (error) { }, onCloseAction: (meta, actionId) { }, onPaywallAction: (meta, actionId) { }, onCustomAction: (meta, actionId) { }, onStateUpdatedAction: (meta, elementId, params) { }, onAnalyticsEvent: (meta, event) { }, ) ``` :::note 要使 Android 平台视图正常工作,请确保你的 `MainActivity` 继承自 `FlutterFragmentActivity`: ```kotlin showLineNumbers title="Kotlin" class MainActivity : FlutterFragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } ``` ::: ## 用户引导加载中 \{#loader-during-onboarding\} 展示用户引导时,你可能会注意到在启动画面和用户引导之间有一个短暂的加载界面,这是底层视图初始化时产生的。你可以根据自己的需求,用不同的方式来处理这个问题。 #### 使用 onDidFinishLoading 控制启动画面 \{#control-splash-screen-using-ondidfinishloading\} :::note 该方式仅在将用户引导作为 widget 嵌入时可用,不支持以独立页面方式展示。 ::: 推荐的跨平台方案是:保持启动屏或自定义遮罩可见,直到用户引导完全加载后,再手动将其隐藏。 使用嵌入式 widget 时,在其上方叠加自定义 widget,并在 `onDidFinishLoading` 触发时隐藏遮罩: ```dart showLineNumbers title="Flutter" AdaptyUIOnboardingPlatformView( onboarding: onboarding, onDidFinishLoading: (meta) { // Hide your custom splash screen or overlay here }, // ... other callbacks ) ``` ### 自定义原生加载界面 \{#customize-native-loader\} :::important 此方式与平台相关,需要维护原生 UI 代码。除非您的应用已维护独立的原生层,否则不建议使用。 ::: 如果需要自定义默认加载界面本身,可以使用平台专属布局进行替换。此方式需要分别针对 Android 和 iOS 进行实现: - **iOS**:将 `AdaptyOnboardingPlaceholderView.xib` 添加到您的 Xcode 项目中 - **Android**:在 `res/layout` 中创建 `adapty_onboarding_placeholder_view.xml` 并在其中定义占位视图 ## 自定义用户引导中的链接打开方式 \{#customize-how-links-open-in-onboardings\} :::important 自定义用户引导中链接的打开方式需要 Adapty SDK v3.15.1 及以上版本。 ::: 默认情况下,用户引导中的链接会在应用内浏览器中打开。这样可以在应用内直接展示网页,用户无需切换应用,体验更流畅。 如果你希望在外部浏览器中打开链接,可以将 `externalUrlsPresentation` 参数设置为 `AdaptyWebPresentation.externalBrowser` 来自定义此行为:
之后,您可以在代码中使用该 ID,并将其作为自定义操作来处理。例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,委托方法 `onboardingController` 会以 `.custom(id:)` 的形式触发,`actionId` 参数即为编辑工具中设置的 **Action ID**。您可以自定义任意 ID,例如 "allowNotifications"。
```dart
// Full-screen presentation
void onboardingViewOnCustomAction(
AdaptyUIOnboardingView view,
AdaptyUIOnboardingMeta meta,
String actionId,
) {
switch (actionId) {
case 'login':
_login();
break;
case 'allow_notifications':
_allowNotifications();
break;
}
}
// Embedded widget
onCustomAction: (meta, actionId) {
_handleCustomAction(actionId);
}
```
:::important
请注意,你需要自行处理用户关闭用户引导后的逻辑。例如,你需要停止显示用户引导界面本身。
:::
```dart showLineNumbers title="Flutter"
// Full-screen presentation
void onboardingViewOnCloseAction(
AdaptyUIOnboardingView view,
AdaptyUIOnboardingMeta meta,
String actionId,
) {
await view.dismiss();
}
// Embedded widget
onCloseAction: (meta, actionId) {
Navigator.of(context).pop();
}
```
2. 点击订阅组名称,在 **Subscriptions** 部分即可看到你的产品列表。
3. 确认你正在测试的产品状态为 **Ready to Submit**。
4. 将表格中的产品 ID 与 Adapty 看板 [**Products**](https://app.adapty.io/products) 标签页中的 ID 进行对比。如果 ID 不匹配,请复制表格中的产品 ID,并在 Adapty 看板中[创建产品](create-product)。
## 第三步:检查产品可用性 \{#step-4-check-product-availability\}
1. 返回 **App Store Connect**,打开同一个 **Subscriptions** 页面。
2. 点击订阅组名称查看产品列表。
3. 选择你正在测试的产品。
4. 向下滚动到 **Availability** 部分,确认所有所需的国家和地区均已列出。
## 第四步:检查产品价格 \{#step-5-check-product-prices\}
1. 再次前往 **App Store Connect** 的 **Monetization** → **Subscriptions** 页面。
2. 点击订阅组名称。
3. 选择你正在测试的产品。
4. 向下滚动到 **Subscription Pricing**,展开 **Current Pricing for New Subscribers** 部分。
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` 警告,并让产品在商店中正常上线。
## 第六步:如果产品卡住,尝试重新创建 \{#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-flutter
---
---
title: "修复 Flutter 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-flutter-sdk-v4
---
---
title: "将 Adapty Flutter SDK 迁移至 v. 4.0"
description: "通过将付费墙 API 替换为 flow API,迁移至 Adapty Flutter SDK v4.0,兼容 Flow Builder 和 Paywall Builder。"
---
Adapty Flutter SDK 4.0 引入了 flow,并相应地对付费墙 API 进行了重命名。新 API 同时兼容全新的 Flow Builder 和现有的 Paywall Builder——无需在 Adapty 看板侧做任何配置变更。
## 快速参考 \{#quick-reference\}
| v3 | v4 |
|---|---|
| `Adapty().getPaywall(placementId: id)` | `Adapty().getFlow(placementId: id)` |
| `Adapty().getPaywallForDefaultAudience(placementId: id)` | `Adapty().getFlowForDefaultAudience(placementId: id)` |
| `Adapty().getPaywallProducts(paywall: paywall)` | `Adapty().getPaywallProducts(flow: flow)` |
| `Adapty().logShowPaywall(paywall: paywall)` | `Adapty().logShowFlow(flow: flow)` |
| `AdaptyPaywall`(类型) | `AdaptyFlow` |
| `AdaptyPaywallFetchPolicy`(类型) | `AdaptyFlowFetchPolicy` |
| `AdaptyUI().createPaywallView(paywall: paywall)` | `AdaptyUI().createFlowView(flow: flow)` |
| `AdaptyUIPaywallView`(类型) | `AdaptyUIFlowView` |
| `AdaptyUIPaywallPlatformView`(widget) | `AdaptyUIFlowPlatformView` |
| `AdaptyUI().presentPaywallView(view)` / `dismissPaywallView(view)` | `AdaptyUI().presentFlowView(view)` / `dismissFlowView(view)` |
| `AdaptyUIPaywallsEventsObserver` | `AdaptyUIFlowsEventsObserver` |
| `AdaptyUI().setPaywallsEventsObserver(observer)` | `AdaptyUI().setFlowsEventsObserver(observer)` |
| `paywallViewDid*` 回调 | `flowViewDid*` 回调 |
| `paywallViewDidFailRendering` | `flowViewDidReceiveError` |
`AdaptyPaywallProduct` 保持其名称不变——产品仍属于某个 flow,`getPaywallProducts` 现在接受 `AdaptyFlow` 作为参数。获取 flow 时不再需要传入 `locale`。购买和用户画像相关的 API(`makePurchase`、`restorePurchases`、`getProfile`、`identify` 等)保持不变,视图方法 `present`、`dismiss` 和 `showDialog` 也同样不变。部分默认行为有所改动——详见[默认行为变更](#default-behavior-changes)。
## 最低版本要求 \{#minimum-versions\}
Adapty Flutter SDK 4.0 提高了最低要求:
- **iOS 15.0** — 最低 iOS 部署目标,从 iOS 13.0 提升。
- **Xcode 26** 或更高版本 — 原生 iOS SDK 使用 Swift tools 6.2。
- **Flutter 3.32.0**(Dart 3.8.0)或更高版本。
## 安装 \{#installation\}
### 更新软件包 \{#update-the-package\}
安装哪个软件包取决于你的应用是否使用了儿童模式。
对于大多数应用,在 `pubspec.yaml` 中将 `adapty_flutter` 更新至 v4.0:
```yaml showLineNumbers title="pubspec.yaml"
dependencies:
adapty_flutter: 4.0.0
```
如果你的应用使用了儿童模式,请改为指定 `adapty_flutter_kids`:
```yaml showLineNumbers title="pubspec.yaml"
dependencies:
adapty_flutter_kids: 4.0.0
```
此**独立软件包**移除了 IDFA 和广告追踪相关代码,以符合 App Store 的要求。请将 Dart 导入路径更新为 `package:adapty_flutter_kids/adapty_flutter.dart`。除此之外,迁移步骤与常规软件包完全相同。
Kids Mode 还需要你在 Adapty 看板中禁用 IP 地址收集——完整配置步骤请参阅 [Kids Mode](kids-mode-flutter)。
### iOS:原生 SDK 现在通过 Swift Package Manager 分发 \{#ios-native-sdks-now-come-through-swift-package-manager\}
[CocoaPods 的 spec 仓库将于 2026 年 12 月进入只读模式](https://blog.cocoapods.org/CocoaPods-Specs-Repo/),因此从 v4 开始,原生 iOS SDK **不再通过 CocoaPods 分发** — 插件仅通过 **Swift Package Manager** 拉取依赖。
如果你使用的是 Flutter 3.32–3.43,请执行以下命令一次性启用 Swift Package Manager 支持:
```bash
flutter config --enable-swift-package-manager
```
Flutter 3.44 及更高版本默认启用 Swift Package Manager,无需额外操作。
## 获取流程 \{#fetching-flows\}
### getPaywall → getFlow
返回类型从 `AdaptyPaywall` 变更为 `AdaptyFlow`,并且不再需要传入 `locale` 参数——渲染 flow 时会自动解析本地化;对于自定义付费墙,所有已配置的语言版本将通过 `flow.remoteConfigs` 返回:
```diff showLineNumbers
- final paywall = await Adapty().getPaywall(placementId: 'YOUR_PLACEMENT_ID', locale: 'en');
+ final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
```
`getPaywallForDefaultAudience` 也以同样的方式重命名:
```diff showLineNumbers
- final paywall = await Adapty().getPaywallForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID', locale: 'en');
+ final flow = await Adapty().getFlowForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID');
```
fetch policy 类型从 `AdaptyPaywallFetchPolicy` 重命名为 `AdaptyFlowFetchPolicy`;其选项(`reloadRevalidatingCacheData`、`returnCacheDataElseLoad`、`returnCacheDataIfNotExpiredElseLoad`)保持不变。
### getPaywallProducts(paywall) → getPaywallProducts(flow)
`getPaywallProducts` 保持名称不变,但现在通过 `flow` 参数接收 `AdaptyFlow`:
```diff showLineNumbers
- final products = await Adapty().getPaywallProducts(paywall: paywall);
+ final products = await Adapty().getPaywallProducts(flow: flow);
```
## 数据模型 \{#data-model\}
`getFlow` 返回的是 `AdaptyFlow` 而非 `AdaptyPaywall`,对象结构也有所变化:
| v3 `AdaptyPaywall` 成员 | v4 `AdaptyFlow` 成员 | 操作 |
|---|---|---|
| `remoteConfig`(单个,可为空) | `remoteConfigs`(列表) | 一个流程为每种已配置的语言各携带一份远程配置。`remoteConfig` getter 仍然存在,返回第一个条目;若需指定语言,可按 `locale` 在 `remoteConfigs` 中查找。 |
| `productIdentifiers` | `productIdentifiers` | 保留,但现在会汇总流程中所有付费墙变体的标识符。各变体的标识符存放在 `flow.paywalls[i].productIdentifiers`。 |
| `hasViewConfiguration` | `hasViewConfiguration` | 不变。 |
| `placementId`(已弃用) | 已移除 | 使用 `flow.placement.id`。 |
| `revision`(已弃用) | 已移除 | 使用 `flow.placement.revision`。 |
| `vendorProductIds`(已弃用) | 已移除 | 使用 `productIdentifiers`。 |
| _(新增)_ | `paywalls`(`AdaptyFlowPaywall` 列表) | 每个条目对应流程中的一个付费墙变体,包含各自的 `name`、`variationId` 和 `productIdentifiers`。 |
`AdaptyPaywallViewConfiguration` 不再对外暴露——视图配置现在是不透明的。请删除所有对该类型的引用。
## Web 付费墙方法 \{#web-paywall-methods\}
`openWebPaywall` 和 `createWebPaywallUrl` 的名称保持不变,但 `paywall` 参数现在接受 `AdaptyFlowPaywall`(流程变体),而不再是 `AdaptyPaywall`。你仍然可以传入 `AdaptyPaywallProduct`。
```diff showLineNumbers
final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
- await Adapty().openWebPaywall(paywall: paywall);
+ if (flow.paywalls.isNotEmpty) {
+ await Adapty().openWebPaywall(paywall: flow.paywalls[0]);
+ }
```
## 追踪流程视图 \{#tracking-flow-views\}
### logShowPaywall → logShowFlow
`logShowPaywall` 已重命名为 `logShowFlow`,现在接收一个 `AdaptyFlow` 参数。事件仍会记录在相同的变体下,因此现有的转化漏斗和 A/B 测试数据图表无需在看板中做任何更改即可继续正常使用。
```diff showLineNumbers
- await Adapty().logShowPaywall(paywall: paywall);
+ await Adapty().logShowFlow(flow: flow);
```
与 v3 相同,当通过[流程编辑工具](adapty-flow-builder)或[付费墙编辑工具](adapty-paywall-builder)渲染流程或付费墙时,无需手动调用此方法——Adapty 会自动追踪这些页面的展示。
## 显示流程 \{#displaying-flows\}
### createPaywallView → createFlowView
将方法重命名,并通过 `flow` 参数传入 `AdaptyFlow`。其他参数(`loadTimeout`、`preloadProducts`、`customTags`、`customTimers`、`customAssets`、`productPurchaseParams`)保持不变,视图方法 `present`、`dismiss` 和 `showDialog` 同样不变:
```diff showLineNumbers
- final view = await AdaptyUI().createPaywallView(paywall: paywall);
+ final view = await AdaptyUI().createFlowView(flow: flow);
await view.present();
```
### AdaptyUIPaywallView → AdaptyUIFlowView
视图类型已重命名。其已废弃的 `paywallVariationId` 属性已移除——请改用 `variationId`:
```diff showLineNumbers
- void flowViewDidAppear(AdaptyUIPaywallView view) {
+ void flowViewDidAppear(AdaptyUIFlowView view) {
```
### AdaptyUIPaywallPlatformView → AdaptyUIFlowPlatformView
如果你将视图作为 widget 嵌入到 widget 树中,请重命名它并传入 `flow` 参数。事件回调(`onDidAppear`、`onDidFinishPurchase` 等)名称保持不变:
```diff showLineNumbers
- AdaptyUIPaywallPlatformView(
- paywall: paywall,
+ AdaptyUIFlowPlatformView(
+ flow: flow,
onDidFinishPurchase: (view, product, purchaseResult) { /* … */ },
)
```
:::note
使用 `createFlowView` 创建的流程视图只能使用一次:调用 `dismiss()` 后,该视图会从内存中释放,无法再次展示——如需再次展示流程,请重新调用 `createFlowView`。
:::
## 处理事件 \{#handling-events\}
观察者类已从 `AdaptyUIPaywallsEventsObserver` 更名为 `AdaptyUIFlowsEventsObserver`,其注册方法已从 `setPaywallsEventsObserver` 更名为 `setFlowsEventsObserver`,所有 `paywallViewDid*` 回调也已更名为 `flowViewDid*`:
```diff showLineNumbers
- class MyObserver extends AdaptyUIPaywallsEventsObserver {
+ class MyObserver extends AdaptyUIFlowsEventsObserver {
@override
- void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) {
+ void flowViewDidPerformAction(AdaptyUIFlowView view, AdaptyUIAction action) {
// …
}
}
- AdaptyUI().setPaywallsEventsObserver(this);
+ AdaptyUI().setFlowsEventsObserver(this);
```
现在有三个回调是**必须实现的**——缺少它们将导致编译错误:
- **`flowViewDidFinishPurchase`**: 在 v3 中为可选项,默认行为是购买后关闭视图。现在由你决定后续操作:继续流程或调用 `view.dismiss()`。
- **`flowViewDidFinishRestore`**: 必填项,与 v3 相同。
- **`flowViewDidReceiveError`**: 替代 `paywallViewDidFailRendering`,同时还可接收其他视图错误。
另外两个小改动:
- `setFlowsEventsObserver`(以及 `setOnboardingsEventsObserver`)现在接受 `null` 来解除之前设置的观察者,SDK 不再持有对它的引用。
- 新增的可选回调 `flowViewDidReceiveAnalyticEvent` 用于接收 flow 中的自定义分析事件。目前 flow 尚未向你的代码发送此类事件,因此无需实现该回调。
v4 还新增了一些可按需启用的功能:
- `AdaptyUI().setObserverModeResolver(...)` 配合 `AdaptyUIObserverModeResolver` — 在 SDK 以[观察者模式](implement-observer-mode-flutter)运行时,处理从流程发起的购买和恢复操作。此前该功能仅在原生 iOS 和 Android SDK 中可用。请参阅[在观察者模式下展示流程](flutter-present-flows-in-observer-mode)。
- `AdaptyUI().setSystemRequestsHandler(...)` 配合 `AdaptyUISystemRequestsHandler` — 用于处理流程中的系统请求(系统权限提示和 App Store 评价请求)。目前流程尚未触发这些请求,因此无需注册处理器。
## 已移除的 API \{#removed-apis\}
以下符号在 3.x 中已被标记为弃用,并在 v4 中正式移除:
### setFallbackPaywalls → setFallback
```diff showLineNumbers
- await Adapty().setFallbackPaywalls(assetId);
+ await Adapty().setFallback(assetId);
```
### withIdfaCollectionDisabled → withAppleIdfaCollectionDisabled
```diff showLineNumbers
configuration: AdaptyConfiguration(apiKey: 'YOUR_PUBLIC_SDK_KEY')
- ..withIdfaCollectionDisabled(true),
+ ..withAppleIdfaCollectionDisabled(true),
```
### 其他已移除的成员 \{#other-removed-members\}
- **`AdaptyPurchaseResultSuccess.jwsTransaction`**:请使用 `appleJwsTransaction`。
- **`AdaptyUIFlowView.paywallVariationId`**:请使用 `variationId`。
- **`AdaptyUIObserver` 和 `AdaptyUI().setObserver(...)`**:请使用 `AdaptyUIFlowsEventsObserver` 和 `setFlowsEventsObserver(...)`。
## 默认行为变更 \{#default-behavior-changes\}
这些变更不会导致编译错误,请在运行时进行测试:
- **成功购买**:在 v3 中,默认的 `paywallViewDidFinishPurchase` 会关闭视图。在 v4 中,`flowViewDidFinishPurchase` 是必须实现的,且没有默认行为——如果你希望关闭视图,需要自行处理。
- **Android 系统返回按钮**:默认情况下,它不再关闭流程。该操作会以 `AndroidSystemBackAction` 的形式传递给 `flowViewDidPerformAction`——如果你希望返回按钮关闭流程,请在此处处理。
- **URL 打开**:默认的 `flowViewDidPerformAction` 现在会通过 `OpenUrlAction` 以原生方式打开 URL(遵循看板中的应用内或外部浏览器设置),同时在 `CloseAction` 时关闭视图。如需自行处理 URL,请覆盖此回调。
- **视图错误**:`flowViewDidReceiveError` 是必须实现的,是否关闭视图取决于你的实现。如果你的 v3 集成依赖于渲染错误时自动关闭视图的行为,请在此回调中调用 `view.dismiss()`。
- **视图生命周期**:关闭流程或用户引导视图后,该视图会从内存中释放。已关闭的视图无法再次显示——请重新创建一个新视图。
## 用户引导 API 已弃用 \{#onboarding-api-deprecation\}
旧版用户引导 API 已在 v4.0 中弃用,请改用 [Flow Builder](adapty-flow-builder)。该 API 目前仍可正常使用,IDE 会通过 `@Deprecated` 注解标记已弃用的符号,不会产生任何运行时警告。这些符号将在未来版本中移除,请提前规划将你的用户引导迁移至 Flow Builder。
已废弃的符号:`getOnboarding`、`getOnboardingForDefaultAudience`、`createOnboardingView`、`presentOnboardingView`、`dismissOnboardingView`、`setOnboardingsEventsObserver`、`AdaptyOnboarding`、`AdaptyUIOnboardingView`、`AdaptyUIOnboardingPlatformView`、`AdaptyUIOnboardingsEventsObserver`,以及用户引导的状态、输入和分析模型。
---
# File: flutter-migration-guide-310
---
---
title: "迁移指南:Flutter Adapty SDK 3.10.0"
description: ""
---
Adapty SDK 3.10.0 是一个重大版本发布,带来了一些改进,但可能需要您执行以下迁移步骤:
1. 更新 `makePurchase` 方法,使用 `AdaptyPurchaseParameters` 替代单独的参数。
2. 在 `AdaptyPaywall` 模型中,将 `vendorProductIds` 替换为 `productIdentifiers`。
## 更新 makePurchase 方法 \{#update-makepurchase-method\}
`makePurchase` 方法现在使用 `AdaptyPurchaseParameters` 替代原有的 `subscriptionUpdateParams` 和 `isOfferPersonalized` 参数。这提供了更好的类型安全性,并为未来扩展购买参数提供了便利。
```diff showLineNumbers
- final purchaseResult = await adapty.makePurchase(
- product: product,
- subscriptionUpdateParams: subscriptionUpdateParams,
- isOfferPersonalized: true,
- );
+ final parameters = AdaptyPurchaseParametersBuilder()
+ ..setSubscriptionUpdateParams(subscriptionUpdateParams)
+ ..setIsOfferPersonalized(true)
+ ..setObfuscatedAccountId('your-account-id')
+ ..setObfuscatedProfileId('your-profile-id');
+ final purchaseResult = await adapty.makePurchase(
+ product: product,
+ parameters: parameters.build(),
+ );
```
如果不需要额外参数,可以直接使用:
```dart showLineNumbers
final purchaseResult = await adapty.makePurchase(
product: product,
);
```
## 更新 AdaptyPaywall 模型的用法 \{#update-adaptypaywall-model-usage\}
`vendorProductIds` 属性已被弃用,推荐使用 `productIdentifiers`。新属性返回 `AdaptyProductIdentifier` 对象而非简单字符串,提供了更结构化的产品信息。
```diff showLineNumbers
- paywall.vendorProductIds.map((vendorId) =>
- ListTextTile(title: vendorId)
- ).toList()
+ paywall.productIdentifiers.map((productId) =>
+ ListTextTile(title: productId.vendorProductId)
+ ).toList()
```
`AdaptyProductIdentifier` 对象通过 `vendorProductId` 属性提供对供应商产品 ID 的访问,在保持原有功能的同时,为未来的功能增强提供了更好的结构支持。
## 向后兼容性 \{#backward-compatibility\}
两项更改均保持向后兼容:
- `makePurchase` 中的旧参数已被弃用,但仍然可以正常使用
- `vendorProductIds` 属性已被弃用,但仍然可以访问
- 现有代码将继续正常运行,但您会看到弃用警告
我们建议更新您的代码以使用新的 API,以确保未来的兼容性,并充分利用改进后的类型安全性和可扩展性。
---
# File: flutter-migration-guide-38
---
---
title: "迁移 Adapty Flutter SDK 至 v3.8"
description: "迁移至 Adapty Flutter SDK v3.8,获得更好的性能和新的货币化功能。"
---
Adapty SDK 3.8.0 是一个重要版本,带来了一些改进,但可能需要你执行若干迁移步骤。
1. 更新 observer 类名和方法名。
2. 更新备用付费墙的方法名。
3. 更新事件处理方法中的 view 类名。
## 更新观察者类和方法名称 \{#update-observer-class-and-method-names\}
观察者类及其注册方法已重命名:
```diff showLineNumbers
- class MyObserver extends AdaptyUIObserver {
+ class MyObserver extends AdaptyUIPaywallsEventsObserver {
@override
void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) {
// Handle action
}
}
// Register observer
- AdaptyUI().setObserver(this);
+ AdaptyUI().setPaywallsEventsObserver(this);
```
## 更新备用付费墙方法名称 \{#update-fallback-paywalls-method-name\}
设置备用付费墙的方法已简化:
```diff showLineNumbers
try {
- await Adapty.setFallbackPaywalls(assetId);
+ await Adapty.setFallback(assetId);
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
```
## 在事件处理方法中更新视图类名 \{#update-view-class-name-in-event-handling-methods\}
所有事件处理方法现在使用新的 `AdaptyUIPaywallView` 类,替代原来的 `AdaptyUIView`:
```diff showLineNumbers
- void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action)
+ void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action)
- void paywallViewDidSelectProduct(AdaptyUIView view, AdaptyPaywallProduct product)
+ void paywallViewDidSelectProduct(AdaptyUIPaywallView view, AdaptyPaywallProduct product)
- void paywallViewDidStartPurchase(AdaptyUIView view, AdaptyPaywallProduct product)
+ void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product)
- void paywallViewDidFinishPurchase(AdaptyUIView view, AdaptyPaywallProduct product, AdaptyProfile profile)
+ void paywallViewDidFinishPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyProfile profile)
- void paywallViewDidFailPurchase(AdaptyUIView view, AdaptyPaywallProduct product, AdaptyError error)
+ void paywallViewDidFailPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product, AdaptyError error)
- void paywallViewDidFinishRestore(AdaptyUIView view, AdaptyProfile profile)
+ void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile)
- void paywallViewDidFailRestore(AdaptyUIView view, AdaptyError error)
+ void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error)
- void paywallViewDidFailLoadingProducts(AdaptyUIView view, AdaptyIOSProductsFetchPolicy? fetchPolicy, AdaptyError error)
+ void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyIOSProductsFetchPolicy? fetchPolicy, AdaptyError error)
- void paywallViewDidFailRendering(AdaptyUIView view, AdaptyError error)
+ void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error)
```
---
# File: migration-to-flutter-sdk-34
---
---
title: "迁移 Adapty Flutter SDK 至 v3.4"
description: "迁移至 Adapty Flutter SDK v3.4,获得更好的性能和全新的变现功能。"
---
Adapty SDK 3.4.0 是一个主要版本,引入了需要您进行迁移操作的改进。
## 更新备用付费墙文件 \{#update-fallback-paywall-files\}
更新您的备用付费墙文件以确保与新 SDK 版本的兼容性:
1. 从 Adapty 看板[下载更新后的备用付费墙文件](fallback-paywalls)。
2. 用新文件[替换移动应用中现有的备用付费墙](flutter-use-fallback-paywalls)。
## 更新 Observer Mode 的实现方式 \{#update-implementation-of-observer-mode\}
如果你正在使用 Observer Mode,请确保更新其实现方式。
此前,向 Adapty 上报交易时使用的是不同的方法。在新版本中,Android 和 iOS 应该统一使用 `reportTransaction` 方法来上报每笔交易,确保 Adapty 能够识别它。如果使用了付费墙,请传入 variation ID,以便将该交易与付费墙关联起来。
:::warning
**不要跳过交易上报!**
如果不调用 `reportTransaction`,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会被发送到集成渠道。
:::
```diff showLineNumbers
- // every time when calling transaction.finish()
- if (Platform.isAndroid) {
- try {
- await Adapty().restorePurchases();
- } on AdaptyError catch (adaptyError) {
- // handle the error
- } catch (e) {
- }
- }
try {
// every time when calling transaction.finish()
await Adapty().reportTransaction(
"YOUR_TRANSACTION_ID",
variationId: "PAYWALL_VARIATION_ID", // optional
);
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
```
---
# File: migration-to-flutter330
---
---
title: "迁移 Adapty Flutter SDK 至 v3.3"
description: "迁移至 Adapty Flutter SDK v3.3,获得更佳性能与全新变现功能。"
---
Adapty SDK 3.3.0 是一个重大版本更新,带来了一些改进,但可能需要你执行一些迁移步骤。
---
title: "迁移指南:从 Adapty iOS SDK v2.x 迁移至 v3.x"
description: "将您的 iOS 应用从 Adapty SDK v2.x 无缝迁移至 v3.x,遵循我们的分步指南,了解关键变更,轻松完成集成升级。"
metadataTitle: "iOS SDK v2.x 至 v3.x 迁移指南 | Adapty 文档"
---
Adapty SDK v3.x 是一个重大版本更新,包含多项破坏性变更。本指南重点介绍这些变更,帮助您顺利完成迁移。
## 公共 API 变更 \{#public-api-changes\}
### AdaptyUI 更名 \{#adaptui-renamed\}
`AdaptyUI` 已重命名为 `AdaptyUI`——抱歉,只是开个玩笑😄 实际上没有变化,但我们确实针对新版付费墙编辑工具对 `AdaptyUI` 进行了大量更新。
### 获取付费墙和产品 \{#fetching-paywalls-and-products\}
在 Adapty SDK v3.x 中,`getPaywall` 方法的加载策略机制已更新。
一个布尔值,用于控制[观察者模式](observer-vs-full-mode)。如果您自行处理购买和订阅状态,并使用 Adapty 发送订阅事件和分析数据,请将其开启。
默认值为 `false`。
🚧 在观察者模式下,Adapty SDK 不会关闭任何交易,请确保您自行处理。
| | **withCustomerUserId** | 可选 | 您系统中的用户标识符。我们会在订阅和分析事件中发送该标识符,以便将事件归因到正确的用户画像。您也可以在 [**Profiles and Segments**](https://app.adapty.io/profiles/users) 菜单中通过 `customerUserId` 查找用户。 | | **withIdfaCollectionDisabled** | 可选 |设为 `true` 可禁用 IDFA 的收集与共享。
以及用户 IP 地址的共享。
默认值为 `false`。
有关 IDFA 收集的更多详情,请参阅[分析集成](analytics-integration#disable-collection-of-advertising-identifiers)部分。
| | **withIpAddressCollectionDisabled** | 可选 |设为 `true` 可禁用用户 IP 地址的收集与共享。
默认值为 `false`。
| ### 激活 Adapty SDK 的 AdaptyUI 模块 \{#activate-adaptyui-module-of-adapty-sdk\} 只有当你计划使用[付费墙编辑工具](adapty-paywall-builder)时,才需要配置 AdaptyUI 模块: ```dart showLineNumbers title="Dart" try { final mediaCache = AdaptyUIMediaCacheConfiguration( memoryStorageTotalCostLimit: 100 * 1024 * 1024, // 100MB memoryStorageCountLimit: 2147483647, // 2^31 - 1, max int value in Dart diskStorageSizeLimit: 100 * 1024 * 1024, // 100MB ); await AdaptyUI().activate( configuration: AdaptyUIConfiguration(mediaCache: mediaCache), observer:
### 在登录/注册时 \{#during-loginsignup\}
如果您在应用启动后才识别用户(例如,在用户登录或注册后),请使用 `identify` 方法设置其 customer user ID。
- 如果您**之前未使用过此 customer user ID**,Adapty 会自动将其与当前用户画像关联。
- 如果您**之前已使用此 customer user ID 识别过该用户**,Adapty 将切换到与该 customer user ID 关联的用户画像。
:::important
客户用户 ID 对每个用户必须是唯一的。如果将该参数值硬编码,所有用户都会被视为同一人。
:::
务必在调用其他 SDK 方法之前 `await` `identify`。并发调用会产生 `#3006 profileWasChanged` 错误或落到匿名用户画像上。详见 [iOS SDK 调用顺序](ios-sdk-call-order)。
默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在时优先返回缓存数据。这样用户可能无法获取最新数据,但加载速度会更快,不受网络质量影响。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后依然保留,仅在应用重新安装或手动清理时才会清除。
Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加快付费墙的加载速度,并设有独立的备用服务器,以防 CDN 不可用。该系统旨在确保你始终获取最新版本的付费墙,同时在网络条件有限的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。若达到超时时间,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能包含多个不同的请求。
| 响应参数: | 参数 | 描述 | | :-------- | :---------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`id`、`variationId`)、名称、远程配置,以及 `hasViewConfiguration` 标志(用于指示该流程是否包含视图配置)。如需预加载产品、自定义 UI 或以编程方式进行检查,请调用 `getPaywallProducts(flow:)`。 | ## 获取视图配置 \{#fetch-the-view-configuration\} 获取流程或付费墙后,通过 `flow.hasViewConfiguration` 检查其是否包含视图配置。该标志用于区分版位在 Adapty 看板中的设计方式: - **`true`** — 该版位使用 **Flow Builder**(流程)或 **Paywall Builder**(付费墙)设计,Adapty 会自动为你渲染 UI。请继续执行以下步骤来获取视图配置,并[展示流程或付费墙](ios-present-paywalls)。 - **`false`** — 该版位是不含 Builder UI 的自定义付费墙。 使用 `getFlowConfiguration` 方法加载视图配置。 ```swift showLineNumbers guard flow.hasViewConfiguration else { // handle as remote config paywall return } let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow) ``` 参数: | 参数 | 必填性 | 描述 | | :----------------------- | :------------- | :---------- | | **forFlow** | 必填 | 通过 `Adapty.getFlow` 获取的 `AdaptyFlow` 对象。 | | **locale** |可选
默认值:`nil`
| [付费墙本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符。格式为语言代码,可包含一个或两个以 `-` 分隔的子标签(如 `en`、`pt-br`)。详见[本地化与语言代码](localizations-and-locale-codes)。 | | **loadTimeout** | 默认值:5 秒 | 该参数限制此方法的超时时间。超时后将返回缓存数据或本地备用数据。请注意,在极少数情况下,由于该方法底层可能包含多个请求,实际超时时间可能略晚于 `loadTimeout` 中设定的值。 | | **products** | 可选 | 提供 `AdaptyPaywallProduct` 对象数组,以优化产品在屏幕上的显示时机。若传入 `nil`,AdaptyUI 将自动获取所需产品。 | | **systemRequestsHandler** | 可选 | 符合 `AdaptySystemRequestsHandler` 协议的对象,用于处理流程操作触发的系统权限请求和评价请求。仅当流程中包含此类操作时才需要提供。 | | **assetsResolver** | 可选 | 类型为 `[String: AdaptyCustomAsset]` 的字典,用于覆盖流程/付费墙中的图片和视频资源。详见[自定义资源](#customize-assets)。 | | **timerResolver** | 可选 | 符合 `AdaptyTimerResolver` 协议的对象,用于为开发者自定义计时器提供结束时间。详见[设置开发者自定义计时器](#set-up-developer-defined-timers)。 | 加载完成后,[展示流程/付费墙](ios-present-paywalls)。 ## 为默认目标受众获取流程或付费墙以加快加载速度 \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,流程和付费墙的获取速度几乎是即时的,无需担心性能问题。但如果你的版位和目标受众数量较多,而用户的网络状况又较差,获取流程或付费墙的时间可能会比预期更长。在这种情况下,你可能希望展示一个默认的流程或付费墙,以保证用户体验的流畅性,而不是让用户看到空白页面。 为了解决这个问题,你可以使用 `getFlowForDefaultAudience` 方法,该方法会获取指定版位中 **All Users** 目标受众的流程或付费墙。但请务必了解,推荐的做法是使用 `getFlow` 方法获取流程或付费墙,详情请参阅上方的[获取付费墙信息](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder)部分。 :::warning 为什么我们推荐使用 `getFlow` `getFlowForDefaultAudience` 方法存在以下几个明显缺点: - **潜在的向后兼容性问题**:如果你需要针对不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会遇到挑战。你要么设计兼容当前(旧版)版本的付费墙,要么接受当前(旧版)用户可能会遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向能力(包括基于国家、营销归因或自定义用户属性的定向)。 如果你愿意接受这些限制,以换取更快的流程或付费墙加载速度,请按如下方式使用 `getFlowForDefaultAudience` 方法。否则,请继续使用[上文](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder)介绍的 `getFlow`。 ::: ```swift showLineNumbers Adapty.getFlowForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(flow): // the requested flow case let .failure(error): // handle the error } } ``` | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements)的标识符,即你在 Adapty 看板中创建版位时指定的值。 | | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——有缓存时直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用后缓存依然保留,只有卸载重装或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要自定义付费墙/流程中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载时,先展示本地预览图。 - 在视频播放前展示预览图。 - 提供视频的像素分辨率,以便播放器在视频加载前预留布局空间(宽高比 = `width / height`)。传入 `nil` 可跳过此项。 下面是一个通过简单字典提供自定义资源的示例: ```swift showLineNumbers let customAssets: [String: AdaptyCustomAsset] = [ // Show a local image using a custom ID "custom_image": .image( .uiImage(value: UIImage(named: "image_name")!) ), // Show a local preview image while a remote main image is loading "hero_image": .image( .remote( url: URL(string: "https://example.com/image.jpg")!, preview: UIImage(named: "preview_image") ) ), // Show a local video with a preview image and a known resolution "hero_video": .video( .file( url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!, preview: .uiImage(value: UIImage(named: "video_preview")!), resolution: CGSize(width: 1080, height: 1920) ) ), ] let flowConfig = try await AdaptyUI.getFlowConfiguration( forFlow: flow, assetsResolver: customAssets ) ``` :::note 如果找不到某个资源,付费墙/流程将回退到其默认外观。 ::: ## 设置开发者定义的计时器 \{#set-up-developer-defined-timers\} 要在移动应用中使用自定义计时器,请创建一个遵循 `AdaptyTimerResolver` 协议的对象。该对象定义每个自定义计时器的渲染方式。如果您愿意,也可以直接使用 `[String: Date]` 字典,因为它已经符合该协议。以下是一个示例: ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` 在此示例中,`CUSTOM_TIMER_NY` 和 `CUSTOM_TIMER_6H` 是你在 Adapty 看板中设置的开发者自定义计时器的 **Timer ID**。`timerResolver` 确保你的应用动态地为每个计时器更新正确的值。例如: - `CUSTOM_TIMER_NY`:距离计时器结束(例如元旦)的剩余时间。 - `CUSTOM_TIMER_6H`:从用户打开付费墙时开始计算的 6 小时倒计时的剩余时间。可选
默认值:`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://swift.adapty.io/documentation/adapty/adaptypaywall) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中开启了 **Show on device** 开关。如果未开启该选项,将无法获取视图配置。 ::: 获取付费墙后,检查其是否包含视图配置——视图配置的存在表明该付费墙是通过付费墙编辑工具创建的。这将指导你如何展示付费墙。如果存在视图配置,则将其视为付费墙编辑工具付费墙;否则,[将其作为远程配置付费墙处理](present-remote-config-paywalls)。 使用 `getPaywallConfiguration` 方法加载视图配置。 ```swift showLineNumbers guard paywall.hasViewConfiguration else { // use your custom logic return } do { let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, products: products ) // use loaded configuration } catch { // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | | :----------------------- | :------------- | :---------- | | **paywall** | 必填 | 用于获取目标付费墙控制器的 `AdaptyPaywall` 对象。 | | **loadTimeout** | 默认:5 秒 | 该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用内容。请注意,由于该方法底层可能包含多个请求,在极少数情况下实际超时时间可能略晚于 `loadTimeout` 中指定的值。 | | **products** | 可选 | 传入 `AdaptyPaywallProduct` 对象数组,以优化产品在屏幕上的展示时机。若传入 `nil`,AdaptyUI 将自动获取所需产品。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder)以及如何正确使用语言代码,详见[此处](localizations-and-locale-codes)。 ::: 加载完成后,[展示付费墙](ios-present-paywalls)。 ## 获取默认目标受众的付费墙以加速获取 \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,付费墙几乎可以立即获取,因此无需担心加速此过程。但是,当您拥有大量目标受众和付费墙,且用户网络连接较弱时,获取付费墙可能需要较长时间。在这种情况下,您可能希望展示默认付费墙以确保流畅的用户体验,而不是不显示任何付费墙。 要解决这个问题,你可以使用 `getPaywallForDefaultAudience` 方法,该方法会获取指定版位中针对 **All Users** 目标受众的付费墙。但请务必注意,推荐的方式仍然是使用 `getPaywall` 方法获取付费墙,详见上方的[获取付费墙信息](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder)章节。 :::warning 为什么我们推荐使用 `getPaywall` `getPaywallForDefaultAudience` 方法存在以下几个明显缺点: - **潜在的向后兼容性问题**:如果您需要为不同版本的应用(当前版本和未来版本)展示不同的付费墙,可能会面临挑战。您要么必须设计支持当前(旧版)版本的付费墙,要么接受使用当前(旧版)版本的用户可能遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一付费墙,这意味着您将失去个性化定向能力(包括基于国家、营销归因或自定义属性的定向)。 如果您愿意接受这些缺点以换取更快的付费墙获取速度,请按如下方式使用 `getPaywallForDefaultAudience` 方法。否则,请坚持使用[上文](#get-pb-paywalls#fetch-paywall-designed-with-paywall-builder)描述的 `getPaywall`。 ::: ```swift showLineNumbers Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in switch result { case let .success(paywall): // the requested paywall case let .failure(error): // handle the error } } ``` :::note `getPaywallForDefaultAudience` 方法从 iOS SDK 2.11.2 版本起可用。 ::: | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 控制台中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个子标签通过减号(**-**)分隔的语言代码。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及推荐使用方式的更多信息,请参阅[本地化与语言代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。这样用户可能无法获得最新数据,但无论网络状况如何,都能享受更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然有效,仅在应用卸载重装或手动清除时才会被清空。
| ## 自定义资源 \{#customize-assets\} 要在付费墙中自定义图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 为部分用户显示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在视频播放前,先显示预览图。 :::important 要使用此功能,请将 Adapty iOS SDK 更新至 3.7.0 或更高版本。 ::: 以下是通过简单字典提供自定义资源的示例: ```swift showLineNumbers let customAssets: [String: AdaptyCustomAsset] = [ // 使用自定义 ID 显示本地图片 "custom_image": .image( .uiImage(value: UIImage(named: "image_name")!) ), // 远程主图加载时显示本地预览图 "hero_image": .image( .remote( url: URL(string: "https://example.com/image.jpg")!, preview: UIImage(named: "preview_image") ) ), // 显示带预览图的本地视频 "hero_video": .video( .file( url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!, preview: .uiImage(value: UIImage(named: "video_preview")!) ) ), ] let paywallConfig = try await AdaptyUI.getPaywallConfiguration( forPaywall: paywall, assetsResolver: customAssets ) ``` :::note 如果找不到某个资源,付费墙将回退到其默认外观。 ::: ## 设置开发者自定义计时器 \{#set-up-developer-defined-timers\} 要在移动应用中使用自定义计时器,需创建一个遵循 `AdaptyTimerResolver` 协议的对象。该对象定义了每个自定义计时器的渲染方式。如果你更喜欢,也可以直接使用 `[String: Date]` 字典,因为它已经符合该协议。以下是示例: ```swift showLineNumbers @MainActor struct AdaptyTimerResolverImpl: AdaptyTimerResolver { func timerEndAtDate(for timerId: String) -> Date { switch timerId { case "CUSTOM_TIMER_6H": Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours case "CUSTOM_TIMER_NY": Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0) default: Date(timeIntervalSinceNow: 3600.0) // 1 hour } } } ``` 在此示例中,`CUSTOM_TIMER_NY` 和 `CUSTOM_TIMER_6H` 是你在 Adapty 看板中设置的开发者自定义计时器的 **Timer ID**。`timerResolver` 确保你的应用动态地为每个计时器更新正确的值。例如: - `CUSTOM_TIMER_NY`:距离计时器结束时间(如元旦)的剩余时间。 - `CUSTOM_TIMER_6H`:用户打开付费墙后开始的 6 小时倒计时的剩余时间。
## 付费墙展示次数过多 \{#the-paywall-view-number-is-too-big\}
**问题**:付费墙的展示次数显示为预期值的两倍。
**原因**:你可能在代码中调用了 `logShowFlow`(iOS SDK v4+)/ `logShowPaywall`,如果你使用的是付费墙编辑工具或 Flow Builder,这样做会导致展示次数重复计算。通过这些工具构建的流程和付费墙会自动追踪数据分析,因此无需手动调用此方法。
**解决方案**:如果你使用的是付费墙编辑工具或 Flow Builder,请确保代码中没有调用 `logShowFlow`(iOS SDK v4+)/ `logShowPaywall`。
## 其他问题 \{#other-issues\}
**问题**:您遇到了上述未涵盖的其他付费墙编辑工具相关问题。
**解决方案**:如有需要,请参考[迁移指南](ios-sdk-migration-guides)将 SDK 升级到最新版本。许多问题已在较新的 SDK 版本中得到修复。
---
# File: ios-present-paywall-builder-paywalls-in-observer-mode
---
---
title: "在 iOS SDK 的观察者模式下展示付费墙编辑工具付费墙"
description: "了解如何在观察者模式下展示付费墙编辑工具付费墙,以获取更深入的洞察。"
---
如果您已使用付费墙编辑工具自定义了付费墙,则无需在移动应用代码中额外编写渲染逻辑来向用户展示它。此类付费墙已包含展示内容与展示方式的完整定义。
:::warning
本节仅适用于[观察者模式](observer-vs-full-mode)。如果您不使用观察者模式,请参阅 [iOS - 展示付费墙编辑工具付费墙](ios-present-paywalls)。
:::
默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐使用这种方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存数据存在时优先返回缓存。这样用户获取到的数据可能不是最新的,但无论网络状况如何,都能获得更快的加载速度。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是完全安全的。
请注意,重启应用后缓存仍然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两个层级存储流程和付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 来加速流程和付费墙的加载,并在 CDN 不可用时提供独立的备用服务器。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。一旦超时,将返回缓存数据或本地备用数据。
请注意,在少数情况下,此方法的实际超时时间可能比 `loadTimeout` 中指定的值略长,因为该操作在底层可能由多个不同的请求组成。
| :::note 在 v4 中,`locale` 参数已从 `getFlow` 移至 `getFlowConfiguration`(仅在使用 AdaptyUI 渲染时使用)。对于自定义付费墙,所有可用的语言区域会一并通过 `flow.remoteConfigs` 返回——请选取与用户设备或应用设置相匹配的语言区域。 ::: 不要硬编码产品 ID!由于流程是远程配置的,可用的产品、产品数量以及特殊优惠(如免费试用)可能会随时间变化。请确保你的代码能处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果后续获取到 3 个产品,应用无需修改代码即可显示全部 3 个。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`id`、`variationId`)、名称、`remoteConfigs` 数组(每个已配置的语言区域对应一条记录)以及 `hasViewConfiguration` 标志。如需获取该流程对应的产品,请调用 `getPaywallProducts(flow:)`。 | ## 获取产品 \{#fetch-products\} 获取流之后,你可以查询与其对应的产品数组:默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐这种方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在时优先返回缓存数据。这种情况下,用户看到的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用不会清除缓存,只有在卸载重装应用或手动清理时,缓存才会被清除。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关区域设置代码及我们推荐的使用方式,请参阅[本地化与区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时返回缓存数据。这样用户获取的数据可能不是最新的,但加载速度更快,无论网络状况如何。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然保留,仅在卸载重装应用或手动清理时才会被清除。
Adapty SDK 以两层方式存储付费墙:上述定期更新的缓存以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可访问时使用独立的备用服务器。该系统旨在确保您始终获取最新版本的付费墙,同时在网络稀缺的情况下保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的超时可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个请求组成。
| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品、产品数量以及特殊优惠(如免费试用)都可能随时变化。请确保你的代码能够处理这些情况。 例如,如果最初获取到 2 个产品,你的应用应显示这 2 个产品;如果后来获取到 3 个产品,应用应在无需修改代码的情况下显示全部 3 个。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,您可以查询与之对应的产品数组:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间用连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐使用方式的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络状况不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在卸载重装应用或手动清理时才会被清除。
|请求成功时,响应中会包含此对象。[AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) 对象提供了用户在应用内的访问等级、订阅及一次性购买的完整信息。
请检查访问等级状态,以确认用户是否具备访问应用所需的权限。
| :::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 废弃。 ::: ## 来自 App Store 的应用内购买 \{#in-app-purchases-from-the-app-store\} 当用户在 App Store 发起购买,且该交易被传递到您的应用时,您有两种处理方式: - **立即处理交易:** 在 `shouldAddStorePayment` 中返回 `true`,Apple 购买系统界面将立即弹出。 - **保存产品对象以便稍后处理:** 在 `shouldAddStorePayment` 中返回 `false`,之后再使用保存的产品调用 `makePurchase`。如果您需要在触发购买前向用户展示自定义内容,这种方式会很有用。 完整代码片段如下: ```swift showLineNumbers title="Swift" final class YourAdaptyDelegateImplementation: AdaptyDelegate { nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool { // 1a. // Return `true` to continue the transaction in your app. The Apple purchase system screen will show automatically. // 1b. // Store the product object and return `false` to defer or cancel the transaction. false } // 2. Continue the deferred purchase later on by passing the product to `makePurchase` when the timing is appropriate func continueDeferredPurchase() async { let storedProduct: AdaptyDeferredProduct = // get the product object from 1b. do { try await Adapty.makePurchase(product: storedProduct) } catch { // handle the error } } } ``` ## 在 iOS 中兑换优惠码 \{#redeem-offer-codes-in-ios\}[`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile) 对象。该模型包含访问等级、订阅及非订阅购买的相关信息。
请检查**访问等级状态**,以确定用户是否有权访问该应用。
| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: ios-transaction-management --- --- title: "iOS SDK 中的高级事务管理" description: "使用 Adapty SDK 在 iOS 应用中手动完成事务。" --- :::note 高级事务管理在 Adapty iOS SDK 3.12 版本起开始支持。 ::: Adapty 中的高级事务管理让您能够更精细地控制事务的处理、验证和完成方式。 高级事务管理引入了三个可选功能,它们协同工作: | 功能 | 用途 | |-------------------------------------------------------------|------| | [`appAccountToken`](#assign-appaccounttoken) | 将 Apple 事务与您的内部用户 ID 关联 | | [`jwsTransaction`](#access-the-jws-representation) | 提供 Apple 的已签名事务载荷以供验证 | | [手动完成](#control-transaction-finishing-behavior) | 允许您仅在后端确认成功后才完成事务 | 这些工具结合使用,可让您在 Adapty 继续与其后端同步事务的同时,构建稳健的自定义验证流程。 :::important 大多数应用不需要此功能。 默认情况下,Adapty 会自动验证并完成 StoreKit 事务。 仅当您运行自己的后端验证或希望完全控制购买生命周期时,才需参考本指南。 ::: ## 分配 `appAccountToken` \{#assign-appaccounttoken\} [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) 是一个 **UUID**,可让您将 App Store 事务与您的内部用户身份关联。 StoreKit 会将此令牌与每笔事务关联,以便您的后端能够将 App Store 数据与您的用户匹配。 请为每位用户生成稳定的 UUID,并在同一账户的不同设备上复用它。 这样可确保购买记录和 App Store 通知始终正确关联。 您可以通过两种方式设置令牌——在 SDK 激活时或在识别用户时。 :::important 您必须始终将 `appAccountToken` 与 `customerUserId` 一起传递。 如果仅传递令牌,则该令牌不会包含在事务中。 :::StoreKit 1:[SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction) 对象。
StoreKit 2:[Transaction](https://developer.apple.com/documentation/storekit/transaction) 对象。
|phoneNumber
firstName
lastName
| String | | gender | 枚举类型,允许的值为:`female`、`male`、`other` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 您可以设置自定义属性,这些属性通常与您的应用使用情况相关。例如,对于健身应用,自定义属性可以是每周锻炼次数;对于语言学习应用,则可以是用户的知识水平,等等。您可以在市场细分中使用自定义属性来创建有针对性的付费墙和优惠,也可以在分析中使用它们来找出哪些产品指标对收益影响最大。 ```swift showLineNumbers do { builder = try builder.with(customAttribute: "value1", forKey: "key1") } catch { // handle key/value validation error } ``` 要移除已有的键,请使用 `.withRemoved(customAttributeForKey:)` 方法: ```swift showLineNumbers do { builder = try builder.withRemoved(customAttributeForKey: "key2") } catch { // handle error } ``` 有时您需要了解此前已设置了哪些自定义属性。为此,请使用 `AdaptyProfile` 对象的 `customAttributes` 字段。 :::warning 请注意,`customAttributes` 的值可能并非最新状态,因为用户属性可以随时从不同设备发送,服务器上的属性可能在上次同步后已发生变更。 ::: ### 限制 \{#limits\} - 每个用户最多可设置 30 个自定义属性 - 键名最长为 30 个字符,可包含字母数字字符及以下任意字符:`_` `-` `.` - 值可以是字符串或浮点数,长度不超过 50 个字符 --- # File: subscription-status --- --- title: "在 iOS SDK 中查看订阅状态" description: "在 Adapty 中追踪和管理用户订阅状态,提升用户留存率。" --- 借助 Adapty,追踪订阅状态变得轻而易举。您无需在代码中手动插入产品 ID,只需检查用户是否拥有有效的[访问等级](access-level),即可轻松确认其订阅状态。 在开始检查订阅状态之前,请先配置 [App Store 服务器通知](enable-app-store-server-notifications)。 ## 访问等级与 AdaptyProfile 对象 \{#access-level-and-the-adaptyprofile-object\} 访问等级是 [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) 对象的属性。建议在应用启动时(例如[识别用户](identifying-users#set-customer-user-id-on-configuration)时)获取用户画像,并在发生变更时及时更新。这样,您便可以直接使用用户画像对象,而无需反复请求。 如需在用户画像更新时收到通知,请按照下方[监听订阅状态更新](subscription-status#listening-for-subscription-status-updates)章节所述,监听用户画像变更事件。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 从服务器获取访问等级 \{#retrieving-the-access-level-from-the-server\} 使用 `.getProfile()` 方法从服务器获取访问等级:[AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) 对象。通常情况下,您只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级访问权限。
`.getProfile` 方法始终尝试查询 API,因此可提供最新的结果。如果由于某种原因(例如无网络连接)Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。值得注意的是,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持信息的最新状态。
| `.getProfile()` 方法返回用户画像,您可以从中获取访问等级状态。每个应用可以设置多个访问等级。例如,如果您有一款新闻应用,并对不同主题单独销售订阅,您可以创建"sports"和"science"两个访问等级。但在大多数情况下,您只需要一个访问等级,此时直接使用默认的"premium"访问等级即可。 以下是检查默认"premium"访问等级的示例:可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由连字符(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及其推荐用法的更多信息,请参阅[本地化与语言代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。
但是,如果您认为用户的网络不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然存在,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存以及备用用户引导。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终获取最新版本的用户引导,同时在网络连接有限的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的超时可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个不同的请求组成。
| 响应参数: | 参数 | 描述 | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://swift.adapty.io/documentation/adapty/adaptyonboarding) 对象,包含:用户引导标识符和配置、远程配置以及其他几个属性。 | ## 通过默认目标受众用户引导加快获取速度 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常,用户引导几乎可以立即获取,因此您无需担心加速此过程。但是,如果您有大量目标受众和用户引导,且用户的网络连接较弱,获取用户引导可能比预期耗时更长。在这种情况下,您可能希望展示默认用户引导,以确保流畅的用户体验,而不是完全不显示用户引导。 为此,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法为**所有用户**目标受众获取指定版位的用户引导。但请务必了解,推荐的方式仍然是使用 `getOnboarding` 方法获取用户引导,详情请参阅上方的[获取用户引导](#fetch-onboarding)部分。 :::warning 建议使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者有以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要设计向后兼容的方案,否则旧版本可能显示异常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法基于国家、归因或自定义属性进行定向。 如果对您的使用场景而言,更快的获取速度足以抵消上述缺点,请按以下方式使用 `getOnboardingForDefaultAudience`。否则,请按[上文](#fetch-onboarding)所述使用 `getOnboarding`。 ::: ```swift showLineNumbers Adapty.getOnboardingForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in switch result { case let .success(onboarding): // the requested onboarding case let .failure(error): // handle the error } } ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 控制台创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|用户引导本地化的标识符。该参数应为由连字符(**-**)分隔的一个或两个子标签组成的语言代码。第一个子标签表示语言,第二个表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及其推荐用法的更多信息,请参阅[本地化与语言代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。
但是,如果您认为用户的网络不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然存在,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存以及备用用户引导。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终获取最新版本的用户引导,同时在网络连接有限的情况下也能保证可靠性。
| --- # File: ios-present-onboardings --- --- title: "Present onboardings in iOS SDK" description: "Discover how to present onboardings on iOS to boost conversions and revenue." --- :::tip **从 SDK v4 开始**,你可以构建[流程](get-pb-paywalls),作为用户引导更强大的替代方案。与在 WebView 中运行的用户引导不同,流程直接在设备上原生渲染——带来更流畅的动画、一致的 iOS 观感、更快的加载速度,以及无需 WebView 运行时依赖。请参阅[获取流程与付费墙](get-pb-paywalls)和[展示流程与付费墙](ios-present-paywalls)以开始使用。 ::: 如果你已通过编辑工具自定义了用户引导,则无需在移动端代码中手动处理其渲染逻辑——该用户引导已包含展示内容与展示方式的完整配置。 在开始之前,请确保: 1. 已安装 [Adapty iOS SDK](sdk-installation-ios) 3.8.0 或更高版本。 2. 已[创建用户引导](create-onboarding)。 3. 已将用户引导添加到[版位](placements)。 ## 在 Swift 中展示用户引导 \{#present-onboardings-in-swift\} 要在设备屏幕上显示可视化用户引导,请按以下步骤操作: 1. 使用 `.getOnboardingConfiguration` 方法获取用户引导视图配置。 2. 使用 `.onboardingController` 方法初始化要显示的可视化用户引导: 请求参数: | 参数 | 是否必填 | 描述 | |:-----------------------------|:---------|:------------------------------------------------------------------------------------------------------------------------------------------------------------| | **onboarding configuration** | 必填 | 一个 `AdaptyUI.OnboardingConfiguration` 对象,包含所有用户引导属性。使用 `AdaptyUI.getOnboardingConfiguration` 方法获取该对象。 | | **delegate** | 必填 | 一个 `AdaptyOnboardingControllerDelegate`,用于监听用户引导事件。 | 返回值: | 对象 | 描述 | |:-------------------------------|:----------------------------------------| | **AdaptyOnboardingController** | 表示所请求的用户引导界面的对象 | 3. 成功创建对象后,您可以将其显示在设备屏幕上: ```swift showLineNumbers title="Swift" import Adapty import AdaptyUI // 0. Get an onboarding if you haven't done it yet let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID") // 1. Obtain the onboarding view configuration: let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding) // 2. Create Onboarding View Controller let onboardingController = try AdaptyUI.onboardingController( with: configuration, delegate:
然后,您可以在代码中使用这个 ID,并将其作为自定义操作来处理。例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,代理方法 `onboardingController` 将以 `.custom(id:)` case 被触发,其中 `actionId` 参数即为编辑工具中设置的 **Action ID**。您可以自定义 ID,例如 "allowNotifications"。
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onCustomAction action: AdaptyOnboardingsCustomAction) {
if action.actionId == "allowNotifications" {
// Request notification permissions
}
}
func onboardingController(_ controller: AdaptyOnboardingController, didFailWithError error: AdaptyUIError) {
// Handle errors
}
```
:::important
请注意,你需要自行处理用户关闭用户引导后的逻辑,例如停止显示用户引导界面。
:::
示例如下:
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onCloseAction action: AdaptyOnboardingsCloseAction) {
controller.dismiss(animated: true)
}
```
2. 点击订阅组名称,你会在 **Subscriptions** 部分看到你的产品列表。
3. 确认你要测试的产品已标记为 **Ready to Submit**。如果没有,请按照 [App Store 产品](app-store-products) 页面上的说明操作。
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** 部分,确认所有所需的国家和地区均已列出。
## 第四步:检查产品价格 \{#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` 警告,并让您的产品在应用商店中正常上线。
## 第六步:如果产品卡住了,重新创建它 \{#step-6-recreate-the-product-if-its-stuck\}
前五步可能全部通过——`Approved` 状态、Bundle ID 匹配、API 密钥有效——但 SDK 仍然返回 `1000 noProductIDsFound`。这种情况下,该产品可能卡在了 Apple 的注册表中。Apple 的产品注册表偶尔会进入一种状态:产品在 App Store Connect 的界面中存在,但无法通过 StoreKit 的查询路径访问。
在 App Store Connect 中删除该产品,然后用相同的产品 ID 重新创建。重新创建后,等待最长 24 小时以完成数据同步。
---
# File: cantMakePayments
---
---
title: "修复 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-ios-sdk-v4
---
---
title: "迁移 Adapty iOS SDK 至 v4.0"
description: "通过将付费墙 API 替换为流程 API,迁移至 Adapty iOS SDK v4.0,兼容流程编辑工具和付费墙编辑工具。"
---
Adapty iOS SDK 4.0 引入了流程概念,并相应地对付费墙 API 进行了重命名。新 API 同时兼容全新的流程编辑工具和现有的付费墙编辑工具——无需在 Adapty 看板侧进行任何配置变更。
## 快速参考 \{#quick-reference\}
| v3 | v4 |
|---|---|
| `Adapty.getPaywall(placementId:locale:)` | `Adapty.getFlow(placementId:)` |
| `AdaptyUI.getPaywallConfiguration(forPaywall:)` | `AdaptyUI.getFlowConfiguration(forFlow:locale:)` |
| `Adapty.getPaywallProducts(paywall:)` | `Adapty.getPaywallProducts(flow:)` |
| `Adapty.logShowPaywall(_:)` | `Adapty.logShowFlow(_:)` |
| `AdaptyPaywallController` | `AdaptyFlowController` |
| `AdaptyPaywallControllerDelegate` | `AdaptyFlowControllerDelegate` |
| `AdaptyUI.paywallController(with:delegate:)` | `AdaptyUI.flowController(with:delegate:)` |
| `.paywall()` (SwiftUI modifier) | `.flow()` |
| `AdaptyPaywallView` | `AdaptyFlowView` |
| `didFailRenderingWith:` / `didFailRendering:` | `didReceiveError:` |
| `didFinishPurchase`(可选,成功后自动关闭) | `didFinishPurchase`(必选,不自动关闭) |
| `Adapty_KidsMode` / `AdaptyUI_KidsMode` 包产品 | `KidsMode` 包特性 |
| `Adapty.updateAttribution(_:source:)`(`source: String`) | `Adapty.updateAttribution(_:source:)`(`source: AdaptyAttributionSource`) |
| `Adapty.setIntegrationIdentifier(key:value:)` | `Adapty.setIntegrationIdentifier(_:)`(`AdaptyIntegrationIdentifier`) |
## 最低 iOS 版本要求 \{#minimum-ios-version\}
Adapty iOS SDK 4.0 将最低部署目标从 iOS 13.0 提升至 **iOS 15.0**。在升级之前,请将项目的 iOS Deployment Target 设置为 15.0 或更高版本。
## 安装:不再支持 CocoaPods \{#installation-cocoapods-no-longer-supported\}
Adapty iOS SDK 4.0 已放弃对 CocoaPods 的支持。请改用 [Swift Package Manager](sdk-installation-ios#install-adapty-sdk) 安装 SDK。
如果你的项目仍在使用 CocoaPods,请从 `Podfile` 中移除 `Adapty` 和 `AdaptyUI` pods,运行 `pod install` 将其清理,然后在 Xcode 中通过 **File → Add Package Dependency**,使用 `https://github.com/adaptyteam/AdaptySDK-iOS.git` 添加该包。
## 儿童模式:独立产品替换为 Package Trait \{#kids-mode-separate-products-replaced-by-a-package-trait\}
在 v3 中,启用[儿童模式](kids-mode)需要选择独立的 **Adapty_KidsMode** 和 **AdaptyUI_KidsMode** 包产品并重命名导入。在 v4.0 中,这些产品已被移除。儿童模式现在是常规 Adapty 包中名为 `KidsMode` 的 Swift Package Trait——启用后,整个 SDK 中的 IDFA 和 AdSupport 都会被编译排除。
迁移步骤:
1. 在 **Choose Package Products** 窗口中,选择常规的 **Adapty** 和 **AdaptyUI** 产品,而非 **Adapty_KidsMode** 和 **AdaptyUI_KidsMode**。
2. 启用 `KidsMode` trait。在 Xcode 26.4 或更高版本中,在项目的 **Package Dependencies** 视图里为 AdaptySDK-iOS 依赖项启用该 trait。如果你在 `Package.swift` 中添加 Adapty 作为依赖项(需要 `swift-tools-version` 6.1 或更高版本),可以在此处启用:
```swift showLineNumbers title="Package.swift"
.package(
url: "https://github.com/adaptyteam/AdaptySDK-iOS.git",
from: "4.0.0",
traits: ["KidsMode"]
)
```
3. 将 import 改回常规模块:
```diff showLineNumbers
- import Adapty_KidsMode
- import AdaptyUI_KidsMode
+ import Adapty
+ import AdaptyUI
```
:::note
早于 26.4 版本的 Xcode 无法通过 UI 为 Xcode 项目启用 traits。此时,请添加一个本地 Swift 包,让其依赖启用了 `KidsMode` trait 的 Adapty,然后让你的应用目标依赖该包。
:::
## 已移除的 API \{#removed-apis\}
- **`Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:)`** — 已移除。所有产品现在均包含优惠信息,因此不再需要单独的资格验证步骤。
- **`AdaptyPaywallProductWithoutDeterminingOffer`** — 已移除。之前传递此类型的回调(例如 `didSelectProduct`)现在改为传递 `AdaptyPaywallProduct`。
## App Store 促销应用内购买功能暂时移除 \{#app-store-promoted-in-app-purchases-temporarily-removed\}
作为 StoreKit 2 迁移的一部分,Adapty iOS SDK 4.0 移除了对 App Store 促销应用内购买的支持。`shouldAddStorePayment(for:)` 代理方法及其接收的 `AdaptyDeferredProduct` 类型在 4.0 中不再可用。
:::warning
此功能的移除是暂时的——促销应用内购买支持将在后续的 4.x 版本中回归。如果您的应用依赖促销应用内购买,请继续使用 iOS SDK 3.x,直到该功能恢复。
:::
## 获取付费墙 \{#fetching-paywalls\}
### getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration
返回类型从 `AdaptyPaywall` / `AdaptyUI.PaywallConfiguration` 变更为 `AdaptyFlow` / `AdaptyUI.FlowConfiguration`。`locale` 参数从 fetch 调用中移出,改为在 `getFlowConfiguration` 中传入:
```diff showLineNumbers
- let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en")
- let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
+ let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")
+ let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow, locale: "en")
```
### getPaywallProducts(paywall:) → getPaywallProducts(flow:)
`getPaywallProducts` 现在接受由 `Adapty.getFlow` 返回的 `AdaptyFlow`:
```diff showLineNumbers
- let products = try await Adapty.getPaywallProducts(paywall: paywall)
+ let products = try await Adapty.getPaywallProducts(flow: flow)
```
## 追踪付费墙展示 \{#tracking-paywall-views\}
### logShowPaywall(_:) → logShowFlow(_:)
`logShowPaywall` 已重命名为 `logShowFlow`,现在接受 `AdaptyFlow` 而非 `AdaptyPaywall`。事件仍会记录在相同的实验变体下,因此现有的漏斗和 A/B 测试数据图表无需修改看板即可继续正常使用。
```diff showLineNumbers
- try await Adapty.logShowPaywall(paywall)
+ try await Adapty.logShowFlow(flow)
```
与 v3 一样,在显示由[流程编辑工具](adapty-flow-builder)或[付费墙编辑工具](adapty-paywall-builder)渲染的流程或付费墙时,无需调用此方法——Adapty 会自动追踪这些浏览记录。
## didFinishPurchase 现在是必须实现的 \{#didfinishpurchase-is-now-required\}
在 v3 中,`didFinishPurchase` 是可选的:如果你没有实现它,付费墙会在购买成功后自动关闭。在 v4.0 中,这个默认的自动关闭行为已被移除,以便流程在购买成功后可以继续——例如,展示流程的后续页面。现在由你决定购买完成后的行为:关闭页面,或者什么都不做以让流程继续。
- **UIKit**:`AdaptyFlowControllerDelegate` 的实现者必须实现 `didFinishPurchase` —— 该方法不再提供默认实现。
- **SwiftUI**:`.flow(...)` 和 `AdaptyFlowView(...)` 的 `didFinishPurchase` 闭包现在为非可选类型,与 `didFailPurchase` 和 `didFinishRestore` 保持一致。
如需保持 v3 的行为,请自行关闭页面:
```swift showLineNumbers title="Swift"
func flowController(
_ controller: AdaptyFlowController,
didFinishPurchase product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
if !purchaseResult.isPurchaseCancelled {
controller.dismiss(animated: true)
}
}
```
## UIKit \{#uikit\}
### AdaptyPaywallController → AdaptyFlowController
重命名控制器类型和工厂方法:
```diff showLineNumbers
- let controller = try AdaptyUI.paywallController(
- with: paywallConfiguration,
- delegate: self
- )
+ let controller = try AdaptyUI.flowController(
+ with: flowConfiguration,
+ delegate: self
+ )
```
### AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate
重命名该协议并更新所有方法签名。请注意,`didSelectProduct` 现在接收 `AdaptyPaywallProduct` 而非已移除的 `AdaptyPaywallProductWithoutDeterminingOffer`,且 `didFinishPurchase` [现在必须实现](#didfinishpurchase-is-now-required) —— 它不再有默认实现。
```diff showLineNumbers
- class YourClass: AdaptyPaywallControllerDelegate {
+ class YourClass: AdaptyFlowControllerDelegate {
- func paywallControllerDidAppear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
- func paywallControllerDidDisappear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPerform action: AdaptyUI.Action) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPerform action: AdaptyUI.Action) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didSelectProduct product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didStartPurchase product: AdaptyPaywallProduct) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didStartPurchase product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishPurchase product: AdaptyPaywallProduct,
- purchaseResult: AdaptyPurchaseResult) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishPurchase product: AdaptyPaywallProduct,
+ purchaseResult: AdaptyPurchaseResult) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailPurchase product: AdaptyPaywallProduct,
- error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailPurchase product: AdaptyPaywallProduct,
+ error: AdaptyError) { }
- func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishRestoreWith profile: AdaptyProfile) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishRestoreWith profile: AdaptyProfile) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRestoreWith error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailRestoreWith error: AdaptyError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRenderingWith error: AdaptyUIError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didReceiveError error: AdaptyUIError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailLoadingProductsWith error: AdaptyError) -> Bool { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailLoadingProductsWith error: AdaptyError) -> Bool { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPartiallyLoadProducts failedIds: [String]) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPartiallyLoadProducts failedIds: [String]) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
- error: AdaptyError?) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
+ error: AdaptyError?) { }
}
```
## SwiftUI \{#swiftui\}
### .paywall() 修饰符 → .flow() \{#paywall-modifier--flow\}
重命名修饰符,更新配置参数名称,并添加[现在必需的](#didfinishpurchase-is-now-required) `didFinishPurchase` 闭包:
```diff showLineNumbers
@State var flowPresented = false // rename freely — the variable name is your choice
var body: some View {
Text("Hello, AdaptyUI!")
- .paywall(
+ .flow(
isPresented: $flowPresented,
- paywallConfiguration: paywallConfiguration,
+ flowConfiguration: flowConfiguration,
+ didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
- didFailRendering: { error in flowPresented = false }
+ didReceiveError: { error in flowPresented = false }
)
}
```
重命名后的回调触发时机与原来的 `didFailRendering` 相同,同时新增了流程脚本产生的运行时错误(`AdaptyUIError` 错误码 `4105`——`.jsException` 对应的 JavaScript 异常)。现有的处理器代码无需修改——只需重命名参数即可。
### AdaptyPaywallView → AdaptyFlowView
重命名视图,更新配置参数,添加[现在必需的](#didfinishpurchase-is-now-required) `didFinishPurchase` 闭包,并更新所有 `didSelectProduct` 闭包——它现在接收 `AdaptyPaywallProduct`,而不是已移除的 `AdaptyPaywallProductWithoutDeterminingOffer`:
```diff showLineNumbers
- AdaptyPaywallView(
- paywallConfiguration: paywallConfiguration,
- didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ },
+ AdaptyFlowView(
+ flowConfiguration: flowConfiguration,
+ didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ },
+ didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
- didFailRendering: { error in /* handle the error */ }
+ didReceiveError: { error in /* handle the error */ }
)
```
## AdaptyUI 自定义资源 \{#adaptyui-custom-assets\}
### AdaptyUICustomVideoAsset
以下两项变更会影响所有已有的调用位置:
- `.player` 现在接受 `AVPlayer` 而非 `AVQueuePlayer`。
- 每个 case 新增了末尾参数 `resolution: CGSize?`。传入 `nil` 可保持现有行为;传入实际像素尺寸后,播放器可在视频加载前预留布局空间(宽高比 = `width / height`)。
```diff showLineNumbers
- case file(url: URL, preview: AdaptyUICustomImageAsset?)
- case remote(url: URL, preview: AdaptyUICustomImageAsset?)
- case player(item: AVPlayerItem, player: AVQueuePlayer, preview: AdaptyUICustomImageAsset?)
+ case file(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case remote(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case player(item: AVPlayerItem, player: AVPlayer, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
```
## 归因与集成标识符 \{#attribution-and-integration-identifiers\}
### updateAttribution(_:source:)
`source` 参数的类型从 `String` 改为新的 `AdaptyAttributionSource` 类型,原来嵌套的 `AdaptyProfile.AttributionSource` 被重命名为顶层的 `AdaptyAttributionSource`。可以使用预定义的来源之一,也可以传入字符串字面量表示其他来源——`AdaptyAttributionSource` 遵循 `ExpressibleByStringLiteral`,因此现有的字符串字面量调用无需修改即可继续编译。
```diff showLineNumbers
- try await Adapty.updateAttribution(attribution, source: "adjust")
+ try await Adapty.updateAttribution(attribution, source: .adjust)
```
预定义来源:`.appleAds`、`.adjust`、`.appsflyer`、`.branch`、`.tenjin`。如果来源存储在 `String` 变量中,请用 `AdaptyAttributionSource(rawValue: yourSource)` 包装。
### setIntegrationIdentifier(_:)
`setIntegrationIdentifier(key:value:)` 已被替换为一个可变参数方法,支持传入一个或多个 `AdaptyIntegrationIdentifier` 值。请使用预定义的工厂方法,而非原始字符串键:
```diff showLineNumbers
- try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ try await Adapty.setIntegrationIdentifier(.appsflyerId(uid))
```
你可以在单次调用中设置多个标识符:
```swift showLineNumbers
try await Adapty.setIntegrationIdentifier(
.appsflyerId(uid),
.adjustDeviceId(adid)
)
```
将每个旧的键字符串替换为其工厂方法:
| v3 key | v4 factory |
|---|---|
| `"adjust_device_id"` | `.adjustDeviceId(_:)` |
| `"airbridge_device_id"` | `.airbridgeDeviceId(_:)` |
| `"amplitude_user_id"` | `.amplitudeUserId(_:)` |
| `"amplitude_device_id"` | `.amplitudeDeviceId(_:)` |
| `"appmetrica_device_id"` | `.appmetricaDeviceId(_:)` |
| `"appmetrica_profile_id"` | `.appmetricaProfileId(_:)` |
| `"appsflyer_id"` | `.appsflyerId(_:)` |
| `"branch_id"` | `.branchId(_:)` |
| `"facebook_anonymous_id"` | `.facebookAnonymousId(_:)` |
| `"firebase_app_instance_id"` | `.firebaseAppInstanceId(_:)` |
| `"mixpanel_user_id"` | `.mixpanelUserId(_:)` |
| `"one_signal_subscription_id"` | `.oneSignalSubscriptionId(_:)` |
| `"one_signal_player_id"` | `.oneSignalPlayerId(_:)` |
| `"posthog_distinct_user_id"` | `.posthogDistinctUserId(_:)` |
| `"pushwoosh_hwid"` | `.pushwooshHWID(_:)` |
| `"tenjin_analytics_installation_id"` | `.tenjinAnalyticsInstallationId(_:)` |
---
# File: migration-to-ios-315
---
---
title: "迁移 Adapty iOS SDK 至 v3.15"
description: "迁移至 Adapty iOS SDK v3.15,获得更好的性能和新的变现功能。"
---
如果你在[观察者模式](observer-vs-full-mode)下使用[付费墙编辑工具](adapty-paywall-builder),从 iOS SDK 3.15 开始,你需要实现一个新方法 `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)`。该方法为恢复逻辑提供了更精细的控制,让你可以在自定义流程中处理购买恢复操作。完整的实现细节,请参阅[在观察者模式下展示付费墙编辑工具付费墙](ios-present-paywall-builder-paywalls-in-observer-mode)。
```diff showLineNumbers
func observerMode(didInitiatePurchase product: AdaptyPaywallProduct,
onStartPurchase: @escaping () -> Void,
onFinishPurchase: @escaping () -> Void) {
// use the product object to handle the purchase
// use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase
}
+ func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void,
+ onFinishRestore: @escaping () -> Void) {
+ // use the onStartRestore and onFinishRestore callbacks to notify AdaptyUI about the process of the restore
+ }
```
---
# File: migration-to-ios-sdk-34
---
---
title: "将 Adapty iOS SDK 迁移至 v3.4"
description: "迁移至 Adapty iOS SDK v3.4,享受更优性能与全新变现功能。"
---
Adapty SDK 3.4.0 是一个重大版本更新,引入了若干改进,需要你在项目中执行相应的迁移操作。
## 更新 Adapty SDK 激活方式 \{#update-adapty-sdk-activation\}
### 登录/注册时 \{#during-loginsignup\}
如果你需要在应用启动后识别用户(例如,在用户登录或注册之后),可以使用 `identify` 方法来设置其 customer user ID。
- 如果你**之前从未使用过该 customer user ID**,Adapty 会自动将其关联到当前用户画像。
- 如果你**之前已使用该 customer user ID 识别过用户**,Adapty 将切换到与该 customer user ID 关联的用户画像。
:::important
每位用户的 Customer user ID 必须唯一。如果将该参数硬编码为固定值,所有用户将被视为同一人。
:::
请等待 `identify` 完成(在其 `onSuccess` 回调中)后再调用其他 SDK 方法。并发调用可能会落在匿名用户画像上。详见 [Kotlin Multiplatform SDK 的调用顺序](kmp-sdk-call-order)。
```kotlin showLineNumbers
Adapty.identify("YOUR_USER_ID") // 每位用户唯一
.onSuccess {
// 成功识别
}
.onError { error ->
// 处理错误
}
```
### 在 SDK 激活期间 \{#during-the-sdk-activation\}
如果在激活 SDK 时已知 customer user ID,可以直接在 `activate` 方法中传入,无需单独调用 `identify`。
如果已知 customer user ID,但在激活之后才设置,则意味着 Adapty 会在激活时先创建一个匿名用户画像,等你调用 `identify` 后才会切换到已有的用户画像。
您可以传入已有的客户用户 ID(之前使用过的),也可以传入新的。如果传入新的,激活时创建的新用户画像将自动关联到该客户用户 ID。
:::note
默认情况下,创建匿名用户画像不会影响分析看板,因为安装量是基于设备 ID 统计的。
设备 ID 代表应用在设备上的一次安装实例,仅在重新安装应用后才会重新生成。
它与此次安装是首次还是重复安装无关,也与是否使用了已有的客户用户 ID 无关。
创建用户画像(在 SDK 激活或登出时)、登录,或在不重新安装应用的情况下升级应用,均不会产生额外的安装事件。
如果您希望按唯一用户而非设备来统计安装量,请前往 **App settings** 并配置 [**Installs definition for analytics**](general#4-installs-definition-for-analytics)。
:::
```kotlin showLineNumbers
AdaptyConfig.Builder("PUBLIC_SDK_KEY")
.withCustomerUserId("user123") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
.build()
```
### 用户退出登录 \{#log-users-out\}
如果您的应用有用户退出登录的按钮,请使用 `logout` 方法。
:::important
用户退出登录会为该用户创建一个新的匿名用户画像。
:::
```kotlin showLineNumbers
Adapty.logout()
.onSuccess {
// successful logout
}
.onError { error ->
// handle the error
}
```
:::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`](kmp-check-subscription-status),也可以[监听用户画像更新](kmp-check-subscription-status),让数据自动同步。
## 后续步骤 \{#next-steps\}
恭喜!您已经在应用中成功实现了应用内购买逻辑!祝您的应用变现之路一切顺利!
要充分发挥 Adapty 的价值,可以深入了解以下内容:
- [**测试**](troubleshooting-test-purchases):确保一切按预期运行
- [**集成**](configuration):只需一行代码,即可与营销归因和分析服务完成集成
- [**设置自定义用户画像属性**](kmp-setting-user-attributes):为用户画像添加自定义属性并创建市场细分,从而针对不同用户发起 A/B 测试或展示不同的付费墙
---
# File: adapty-sdk-integration-skill-kmp
---
---
title: "使用 SDK 集成技能将 Adapty 集成到你的 Kotlin Multiplatform 应用中"
description: "使用 adapty-sdk-integration 技能,通过 AI 编程工具将 Adapty SDK 端到端集成到你的 Kotlin Multiplatform 应用中。"
---
默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果你认为用户的网络连接不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存存在时直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制在本地存储流程和付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加速获取,并在 CDN 不可用时提供独立的备用服务器。这套系统旨在确保你始终获取最新版本,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。达到超时后,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能由多个请求组成。
对于 Kotlin Multiplatform:你可以使用扩展函数创建 `Duration`,例如 `5.seconds`,其中 `.seconds` 来自 `kotlin.time.Duration.Companion.seconds`。
| 响应参数: | 参数 | 描述 | | :-------- | :---------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`instanceIdentity`、`variationId`)、名称、付费墙变体(`paywalls`——`AdaptyFlowPaywall` 列表)以及远程配置(`remoteConfigs`——每个语言区域对应一条记录)。如需预加载产品、自定义 UI 或以编程方式检查,请调用 `getPaywallProducts(flow)`。 | ## 获取视图配置 \{#fetch-the-view-configuration\} 获取流程或付费墙后,使用 `createFlowView` 方法一步完成视图配置的加载和视图的创建。无需单独检查任何标志:如果该版位是在 **Flow Builder**(流程)或 **Paywall Builder**(付费墙)中设计的,`createFlowView` 将返回已准备好展示的视图。如果该版位是没有编辑工具界面的自定义付费墙,`createFlowView` 将返回 `AdaptyResult.Error`——[将其作为远程配置付费墙处理](present-remote-config-paywalls-kmp)。 :::important 请确保在 Flow Builder 中启用 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: ```kotlin showLineNumbers AdaptyUI.createFlowView( flow = flow, loadTimeout = 5.seconds, preloadProducts = true ).onSuccess { view -> // use view }.onError { error -> // the flow has no view configured, or view creation failed } ``` | 参数 | 是否必填 | 描述 | | :--------------------------- | :------------- | :----------------------------------------------------------- | | **flow** | 必填 | 通过 `Adapty.getFlow` 获取的 `AdaptyFlow` 对象。 | | **loadTimeout** | 选填 | 此值限制该方法的超时时间。若超时,将返回缓存数据或本地备用数据。注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作底层可能包含多个请求。可使用 `kotlin.time.Duration.Companion` 中的扩展函数,例如 `5.seconds`。 | | **preloadProducts** | 选填 | 设为 `true` 可预加载产品以提升性能。启用后,产品将提前加载,从而减少显示流程或付费墙所需的时间。 | | **productPurchaseParams** | 选填 | [`AdaptyProductIdentifier`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-product-identifier/) 到 [`AdaptyPurchaseParameters`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-purchase-parameters/) 的映射。用于为流程或付费墙中的各个产品配置特定的购买参数,例如个性化优惠或订阅更新参数。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder)。 ::: 加载完成后,[展示流程或付费墙](kmp-present-paywalls)。 ## 为默认目标受众获取流程或付费墙以加快加载速度 \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,流程和付费墙几乎可以即时获取,无需担心速度问题。但如果你的目标受众和版位数量较多,且用户的网络连接较差,获取流程或付费墙可能会比预期慢。在这种情况下,你可能希望显示默认的流程或付费墙,以确保流畅的用户体验,而不是什么都不展示。 为了解决这个问题,您可以使用 `getFlowForDefaultAudience` 方法,该方法会获取指定版位中 **All Users** 目标受众的流程或付费墙。但需要特别注意的是,推荐的做法是通过 `getFlow` 方法来获取流程或付费墙,详情请参见上方的[获取流程/付费墙](#fetch-flowpaywall)章节。 :::warning 为什么我们推荐使用 `getFlow` `getFlowForDefaultAudience` 方法存在以下几个明显的缺点: - **潜在的向后兼容性问题**:如果你需要针对不同的应用版本(当前版本和未来版本)展示不同的流程,可能会遇到挑战。你要么设计出兼容当前(旧版)版本的流程,要么接受使用当前(旧版)版本的用户可能遇到流程无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一流程,这意味着你将失去个性化定向能力(包括基于国家、营销归因或自定义属性的定向)。 如果你愿意接受这些缺点以换取更快的流程或付费墙加载速度,请按如下方式使用 `getFlowForDefaultAudience` 方法。否则请继续使用[上文](#fetch-flowpaywall)介绍的 `getFlow`。 ::: ```kotlin showLineNumbers Adapty.getFlowForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", fetchPolicy = AdaptyPaywallFetchPolicy.Default, ).onSuccess { flow -> // the requested flow }.onError { error -> // handle the error } ``` | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **fetchPolicy** | 默认值:`AdaptyPaywallFetchPolicy.Default` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它可确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存数据存在时优先返回缓存。这种情况下,用户获取到的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,缓存在应用重启后仍会保留,仅在应用重新安装或手动清理时才会被清除。
| ## 自定义资源 \{#customize-assets\} 要自定义流程或付费墙中的图片和视频,请实现自定义资源。 主图和视频有预定义的 ID:`hero_image` 和 `hero_video`。在自定义资源包中,你通过这些 ID 来定位相应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 向部分用户显示不同的图片或视频。 - 在远程主图加载时,先显示本地预览图。 - 在播放视频前,先显示预览图。 以下是如何通过 map 提供自定义资源的示例: :::info Kotlin Multiplatform SDK 仅支持本地资源。如需使用远程内容,请在将其用于自定义资源之前,先将其下载并缓存到本地。 ::: ```kotlin showLineNumbers // Import generated Res class for accessing resources viewModelScope.launch { // Get URIs for bundled resources using Res.getUri() val heroImagePath = Res.getUri("files/images/hero_image.png") val demoVideoPath = Res.getUri("files/videos/demo_video.mp4") // Or read image as byte data val imageByteData = Res.readBytes("files/images/avatar.png") // Create custom assets map val customAssets: Map可选
默认值:`en`
|[付费墙本地化](add-paywall-locale-in-adapty-paywall-builder)的标识符。此参数应为由一个或两个子标签组成的语言代码,子标签之间以减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关区域代码及使用建议,请参阅[本地化与区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`AdaptyPaywallFetchPolicy.Default` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何都能获得更快的加载速度。缓存会定期更新,因此在会话期间使用它以避免网络请求是安全的。
请注意,缓存在重启应用后保持不变,仅在卸载重装应用或手动清理时才会清除。
Adapty SDK 在本地以两层存储付费墙:上述定期更新的缓存和[备用付费墙](fallback-paywalls)。我们还使用 CDN 更快地获取付费墙,以及在 CDN 不可达时使用独立的备用服务器。此系统旨在确保您始终获取最新版本的付费墙,同时在网络连接不佳的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |此值限制该方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个不同请求组成。
对于 Kotlin Multiplatform:您可以使用扩展函数(如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`)或 `TimeInterval.seconds(5)` 创建 `TimeInterval`。若不设置限制,请使用 `TimeInterval.INFINITE`。
| 响应参数: | 参数 | 描述 | | :-------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | 一个 [`AdaptyPaywall`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall/) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙的视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中开启 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 获取付费墙后,检查其是否包含 `ViewConfiguration`,这表示该付费墙是使用付费墙编辑工具创建的。这将指导您如何展示该付费墙。如果存在 `ViewConfiguration`,则将其视为付费墙编辑工具付费墙;否则,[将其作为远程配置付费墙处理](present-remote-config-paywalls-kmp)。 使用 `createPaywallView` 方法加载视图配置。 ```kotlin showLineNumbers if (paywall.hasViewConfiguration) { AdaptyUI.createPaywallView( paywall = paywall, loadTimeout = 5.seconds, preloadProducts = true ).onSuccess { paywallView -> // use paywallView }.onError { error -> // handle the error } } else { // use your custom logic } ``` | 参数 | 是否必填 | 描述 | | :--------------------------- | :------------- | :----------------------------------------------------------- | | **paywall** | 必填 | 用于获取目标付费墙控制器的 `AdaptyPaywall` 对象。 | | **loadTimeout** | 可选 | 此值限制该方法的超时时间。如果达到超时,将返回缓存数据或本地备用数据。请注意,在极少数情况下,此方法的超时时间可能略晚于 `loadTimeout` 中指定的时间,因为该操作在底层可能由多个不同请求组成。您可以使用 `kotlin.time.Duration.Companion` 中的扩展函数,如 `5.seconds`。 | | **preloadProducts** | 可选 | 设置为 `true` 以预加载产品从而提升性能。启用后,产品将提前加载,减少展示付费墙所需的时间。 | | **productPurchaseParams** | 可选 | 从 [`AdaptyProductIdentifier`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-product-identifier/) 到 [`AdaptyPurchaseParameters`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-purchase-parameters/) 的映射。使用此参数为付费墙中的各个产品配置特定的购买参数,例如个性化优惠或订阅更新参数。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder)。 ::: 加载完成后,[展示付费墙](kmp-present-paywalls)。 ## 为默认目标受众获取付费墙以加快获取速度 \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,付费墙的获取几乎是即时完成的,您无需担心加速此过程。但是,如果您拥有大量目标受众和付费墙,且用户的网络连接较弱,付费墙的获取时间可能比预期更长。在这种情况下,您可能希望展示默认付费墙以确保流畅的用户体验,而不是完全不展示付费墙。 为解决这一问题,您可以使用 `getPaywallForDefaultAudience` 方法,该方法为**所有用户**目标受众获取指定版位的付费墙。但请务必了解,推荐的方式是使用 `getPaywall` 方法获取付费墙,详见上方的[获取付费墙信息](#fetch-paywall-designed-with-paywall-builder)部分。 :::warning 为什么我们推荐使用 `getPaywall` `getPaywallForDefaultAudience` 方法存在一些显著缺点: - **潜在的向后兼容性问题**:如果您需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会面临挑战。您要么必须设计支持当前(旧版)的付费墙,要么接受使用当前(旧版)的用户可能遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为**所有用户**目标受众设计的相同付费墙,这意味着您将失去个性化定向能力(包括基于国家、营销归因或您自己的自定义属性的定向)。 如果您愿意接受这些缺点以换取更快的付费墙获取速度,请按如下方式使用 `getPaywallForDefaultAudience` 方法。否则,请坚持使用[上方](#fetch-paywall-designed-with-paywall-builder)介绍的 `getPaywall`。 ::: ```kotlin showLineNumbers Adapty.getPaywallForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` | 参数 | 是否必填 | 描述 | |---------|--------|-----------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。此参数应为由一个或多个子标签组成的语言代码,子标签之间以减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关区域代码及使用建议,请参阅[本地化与区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`AdaptyPaywallFetchPolicy.Default` |默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存存在时返回缓存数据。在这种情况下,用户可能无法获取最新数据,但无论网络状况如何都能获得更快的加载速度。缓存会定期更新,因此在会话期间使用它以避免网络请求是安全的。
请注意,缓存在重启应用后保持不变,仅在卸载重装应用或手动清理时才会清除。
| ## 自定义素材 \{#customize-assets\} 要自定义付费墙中的图片和视频,请实现自定义素材。 主图和视频具有预定义的 ID:`hero_image` 和 `hero_video`。在自定义素材包中,您通过这些 ID 定位相应元素并自定义其行为。 对于其他图片和视频,您需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,您可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载时展示本地预览图。 - 在播放视频前展示预览图。 :::important 要使用此功能,请将 Adapty SDK 更新至 3.7.0 或更高版本。 ::: 以下是通过映射提供自定义素材的示例: :::info Kotlin Multiplatform SDK 仅支持本地素材。对于远程内容,您应在使用自定义素材之前先将其下载并缓存到本地。 ::: ```kotlin showLineNumbers // Import generated Res class for accessing resources viewModelScope.launch { // Get URIs for bundled resources using Res.getUri() val heroImagePath = Res.getUri("files/images/hero_image.png") val demoVideoPath = Res.getUri("files/videos/demo_video.mp4") // Or read image as byte data val imageByteData = Res.readBytes("files/images/avatar.png") // Create custom assets map val customAssets: Map
## 付费墙浏览次数过多 \{#the-paywall-view-number-is-too-big\}
**问题**:付费墙的浏览次数显示为预期值的两倍。
**原因**:你可能在代码中调用了 `logShowFlow`(SDK v4+)/ `logShowPaywall`,如果你使用的是付费墙编辑工具或 Flow Builder,这会导致浏览次数重复计算。对于使用这些工具构建的流程和付费墙,数据分析会自动追踪,无需手动调用此方法。
**解决方案**:如果你使用的是付费墙编辑工具或 Flow Builder,请确保代码中没有调用 `logShowFlow`(SDK v4+)/ `logShowPaywall`。
---
# File: kmp-implement-paywalls-manually
---
---
title: "在 Kotlin Multiplatform SDK 中手动实现付费墙"
description: "了解如何在 Kotlin Multiplatform 应用中使用 Adapty SDK 手动实现付费墙。"
---
## 接受购买 \{#accept-purchases\}
如果你使用的是自己实现的付费墙,可以将购买处理委托给 Adapty,使用 `makePurchase` 方法即可。这样,我们会处理所有用户场景,你只需处理购买结果。
:::important
`makePurchase` 仅适用于在 Adapty 看板中创建的产品。请确保按照[快速入门指南](quickstart)在看板中配置产品及其获取方式。
:::
默认情况下,SDK 会尝试从服务器加载数据,若失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存存在时直接返回缓存数据。这种情况下,用户获取的数据可能不是最新的,但加载速度更快,不受网络质量影响。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储流程和付费墙:上述定期更新的缓存,以及[备用付费墙](kmp-use-fallback-paywalls)。我们还使用 CDN 来加快流程和付费墙的加载速度,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终获取最新版本的流程和付费墙,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值用于限制此方法的超时时间。达到超时时间后,将返回缓存数据或本地备用内容。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能由多个不同请求组成。
| 不要硬编码产品 ID!由于流程是远程配置的,可用产品的数量以及特殊优惠(例如免费试用)都可能随时间变化。请确保您的代码能够处理这些场景。 例如,如果您最初获取到 2 个产品,应用应显示这 2 个产品;如果之后获取到 3 个产品,应用无需修改任何代码即可显示全部 3 个产品。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含:流程标识符、付费墙变体(`paywalls` — 每个变体有各自的产品标识符)、`remoteConfigs` 列表(每个已配置的语言环境对应一条记录),以及其他若干属性。如需获取该流程的产品,请调用 `getPaywallProducts(flow)`。 | :::note 在 v4 中,`getFlow` 没有 `locale` 参数。当你使用 `createFlowView` 渲染流程时,本地化会自动解析。对于自定义付费墙,所有可用的语言区域会一并通过 `flow.remoteConfigs` 返回——选择与用户设备或应用设置相匹配的语言区域即可。详情请参阅[本地化与语言区域代码](kmp-localizations-and-locale-codes)。 ::: ## 获取产品 \{#fetch-products\} 获取流程后,你可以查询与其对应的产品数组: ```kotlin showLineNumbers Adapty.getPaywallProducts(flow).onSuccess { products -> // the requested products }.onError { error -> // handle the error } ``` 响应参数: | 参数 | 描述 | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他若干属性。 | 在实现自定义流程设计时,您可能需要访问 [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) 对象中的这些属性。以下列出了最常用的属性,完整的属性说明请参阅上方链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 若要显示产品名称,请使用 `product.localizedTitle`。请注意,此本地化基于用户在商店中选择的国家/地区,而非设备本身的语言环境。 | | **Price** | 若要显示本地化价格,请使用 `product.price.localizedString`。此本地化基于设备的语言环境信息。您也可以通过 `product.price.amount` 以数字形式获取价格,该值以本地货币表示。若要获取对应的货币符号,请使用 `product.price.currencySymbol`。 | | **Subscription Period** | 若要显示订阅周期(如周、月、年等),请使用 `product.subscriptionDetails?.localizedSubscriptionPeriod`。此本地化基于设备的语言环境。若要以编程方式获取订阅周期,请使用 `product.subscriptionDetails?.subscriptionPeriod`。通过该属性可访问 `unit` 枚举以获取时长单位(即 DAY、WEEK、MONTH、YEAR 或 UNKNOWN)。`numberOfUnits` 值表示周期单位的数量。例如,对于按季度计费的订阅,`unit` 属性值为 `MONTH`,`numberOfUnits` 属性值为 `3`。 | | **Introductory Offer** | 若要显示标记或其他指示符来表明订阅包含新用户优惠,请查看 `product.subscriptionDetails?.introductoryOfferPhases` 属性。这是一个列表,最多可包含两个折扣阶段:免费试用阶段和优惠价格阶段。每个阶段对象包含以下实用属性:默认情况下,SDK 会尝试从服务器加载数据,失败时返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`——在缓存存在时直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,重启应用后缓存仍然保留,只有在重新安装应用或手动清除时才会被清空。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
| | **fetchPolicy** | 默认值:`AdaptyPaywallFetchPolicy.Default` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存数据存在时优先返回缓存。这样虽然用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在重启应用后仍会保留,只有在卸载重装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储付费墙:上述定期更新的缓存层,以及[备用付费墙](kmp-use-fallback-paywalls)。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终能获取最新版本的付费墙,同时在网络条件较差时也能保证可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的时间,因为底层操作可能涉及多个请求。
| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品、产品数量以及特殊优惠(如免费试用)都可能随时变化。请确保你的代码能够处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果之后获取到 3 个产品,应用应在无需修改代码的情况下显示全部 3 个。唯一需要硬编码的只有版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall/) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,你可以查询与其对应的产品数组: ```kotlin showLineNumbers Adapty.getPaywallProducts(paywall).onSuccess { products -> // the requested products }.onError { error -> // handle the error } ``` 响应参数: | 参数 | 描述 | | :-------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他多个属性。 | 在实现自定义付费墙设计时,您可能需要访问 [`AdaptyPaywallProduct`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-paywall-product/) 对象中的这些属性。以下列出了最常用的属性,完整的属性列表请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **标题** | 要显示产品标题,请使用 `product.localizedTitle`。注意,本地化基于用户所选的商店国家/地区,而非设备本身的语言区域设置。 | | **价格** | 要显示本地化价格,请使用 `product.price.localizedString`。该本地化基于设备的语言区域信息。也可以通过 `product.price.amount` 以数字形式获取价格,值将以当地货币提供。要获取对应的货币符号,请使用 `product.price.currencySymbol`。 | | **订阅周期** | 要显示周期(如周、月、年等),请使用 `product.subscriptionDetails?.localizedSubscriptionPeriod`。该本地化基于设备语言区域。要以编程方式获取订阅周期,请使用 `product.subscriptionDetails?.subscriptionPeriod`。从中可以访问 `unit` 枚举以获取时长单位(即 DAY、WEEK、MONTH、YEAR 或 UNKNOWN)。`numberOfUnits` 值表示周期单位的数量。例如,对于季度订阅,`unit` 属性为 `MONTH`,`numberOfUnits` 属性为 `3`。 | | **新用户优惠** | 要显示徽标或其他指示符以表明订阅包含新用户优惠,请查看 `product.subscriptionDetails?.introductoryOfferPhases` 属性。这是一个列表,最多包含两个折扣阶段:免费试用阶段和新用户优惠价格阶段。每个阶段对象包含以下实用属性:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,子标签之间以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
| | **fetchPolicy** | 默认值:`AdaptyPaywallFetchPolicy.Default` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果您认为用户的网络连接不稳定,可以考虑使用 `AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,重启应用后缓存依然保留,只有在卸载重装应用或手动清除时才会被清空。
|请求成功后,响应中包含此对象。[AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/) 对象提供了用户在应用内的访问等级、订阅及非订阅购买的完整信息。
请检查访问等级状态,以确认用户是否具有所需的应用访问权限。
| :::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\} 当用户选择新订阅而非续订当前订阅时,具体行为取决于所使用的应用商店。对于 Google Play,订阅不会自动更新,你需要按照以下说明在移动应用代码中手动处理切换逻辑。 要在 Android 中将订阅替换为另一个订阅,请在调用 `.makePurchase()` 方法时传入额外参数: ```kotlin showLineNumbers val subscriptionUpdateParams = AdaptyAndroidSubscriptionUpdateParameters( oldSubVendorProductId = "old_subscription_product_id", replacementMode = AdaptyAndroidSubscriptionUpdateReplacementMode.CHARGE_FULL_PRICE ) val purchaseParams = AdaptyPurchaseParameters.Builder() .setSubscriptionUpdateParams(subscriptionUpdateParams) .build() Adapty.makePurchase( product = product, parameters = purchaseParams ).onSuccess { purchaseResult -> when (purchaseResult) { is AdaptyPurchaseResult.Success -> { val profile = purchaseResult.profile // successful cross-grade } is AdaptyPurchaseResult.UserCanceled -> { // user canceled the purchase flow } is AdaptyPurchaseResult.Pending -> { // the purchase has not been finished yet, e.g. user will pay offline by cash } } }.onError { error -> // Handle the error } ``` 附加请求参数: | 参数 | 是否必填 | 描述 | |:---------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **parameters** | 可选 | 通过 [`AdaptyPurchaseParameters`](https://kmp.adapty.io/adapty/com.adapty.kmp.models/-adapty-purchase-parameters/) 传入的 [`AdaptyAndroidSubscriptionUpdateParameters`](https://kmp.adapty.io/////adapty/com.adapty.kmp.models/-adapty-android-subscription-update-parameters/) 对象。 | 您可以在 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\}一个 [`AdaptyProfile`](https://kmp.adapty.io//////adapty/com.adapty.kmp.models/-adapty-profile/) 对象。该模型包含有关访问等级、订阅和非订阅购买的信息。
检查**访问等级状态**以确定用户是否有权访问该应用。
| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: implement-observer-mode-kmp --- --- title: "在 Kotlin Multiplatform SDK 中实现观察者模式" description: "在 Adapty 中实现观察者模式,以便在 Kotlin Multiplatform SDK 中追踪用户订阅事件。" --- 如果您已有自己的购买基础设施,暂时不打算完全切换到 Adapty,可以了解[观察者模式](observer-vs-full-mode)。在基本形态下,观察者模式提供高级数据分析功能,以及与归因和分析系统的无缝集成。 如果这满足您的需求,您只需: 1. 在配置 Adapty SDK 时,将 `observerMode` 参数设置为 `true` 以启用该功能。请按照 [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform) 的设置说明进行操作。 2. 从现有购买基础设施向 Adapty [上报交易记录](report-transactions-observer-mode-kmp)。 :::tip 在 SDK v4 中,你也可以在观察者模式下呈现 Adapty 渲染的流程和付费墙:当用户点击购买或恢复按钮时,SDK 会将操作交给你的代码,由你自行处理购买或恢复逻辑。详见[在观察者模式下呈现流程](kmp-present-flows-in-observer-mode)。 ::: ## Observer 模式设置 \{#observer-mode-setup\} 如果你自行处理购买和订阅状态,并使用 Adapty 发送订阅事件和分析数据,请开启 Observer 模式。 :::important 在 Observer 模式下运行时,Adapty SDK 不会关闭任何交易,请确保你自行处理。 ::: ```kotlin showLineNumbers val config = AdaptyConfig .Builder("PUBLIC_SDK_KEY") .withObserverMode(true) // default false .build() Adapty.activate(configuration = config) .onSuccess { Log.d("Adapty", "SDK initialised in observer mode") } .onError { error -> Log.e("Adapty", "Adapty init error: ${error.message}") } ``` 参数: | 参数 | 描述 | | --------------------------- | ------------------------------------------------------------ | | observerMode | 布尔值,用于控制[观察者模式](observer-vs-full-mode)。默认值为 `false`。 | ## 在观察者模式下使用 Adapty 付费墙 \{#using-adapty-paywalls-in-observer-mode\} 如果您还想使用 Adapty 的付费墙和 A/B 测试功能,也是可以的——但在观察者模式下需要一些额外的设置。除上述步骤外,您还需要: 1. 按照[远程配置付费墙](present-remote-config-paywalls-kmp)的常规方式展示付费墙。 3. 将付费墙与购买交易[关联](report-transactions-observer-mode-kmp)。 --- # File: report-transactions-observer-mode-kmp --- --- title: "在 Kotlin Multiplatform SDK 的 Observer 模式下上报交易" description: "在 Kotlin Multiplatform SDK 的 Adapty Observer 模式下上报购买交易,用于用户洞察和收入追踪。" --- 在 Observer 模式下,Adapty SDK 无法自动追踪通过您现有购买系统完成的购买。您需要手动上报来自应用商店的交易。在发布应用**之前**完成此设置至关重要,以避免分析数据出现错误。 使用 `reportTransaction` 显式上报每笔交易,以便 Adapty 识别。 :::warning **请勿跳过交易上报!** 如果您不调用 `reportTransaction`,Adapty 将无法识别该交易,它不会出现在分析数据中,也不会被发送到集成系统。 ::: 如果您使用 Adapty 付费墙,请在上报交易时包含 `variationId`。这会将购买与触发它的付费墙关联起来,从而确保付费墙分析数据的准确性。 ```kotlin showLineNumbers Adapty.reportTransaction( transactionId = "your_transaction_id", variationId = paywall.variationId ).onSuccess { profile -> // Transaction reported successfully // profile contains updated user data }.onError { error -> // handle the error } ``` 参数说明: | 参数 | 是否必填 | 说明 | | --------------- | -------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | transactionId | 必填 | 来自应用商店购买的交易 ID。通常是商店返回的购买令牌或交易标识符。 | | variationId | 选填 | 实验变体的字符串标识符。您可以通过 [AdaptyPaywall](https://kmp.adapty.io//////adapty/com.adapty.kmp.models/-adapty-paywall/) 对象的 `variationId` 属性获取。 | --- # File: kmp-troubleshoot-purchases --- --- title: "在 Kotlin Multiplatform SDK 中排查购买问题" description: "在 Kotlin Multiplatform SDK 中排查购买问题" --- 本指南帮助您解决在 Kotlin Multiplatform 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 \{#adaptyelrorcantmakepayments-in-observer-mode\} **问题**:在观察者模式下使用 `makePurchase` 时收到 `AdaptyError.cantMakePayments`。 **原因**:在观察者模式下,您应在自己的代码中处理购买,而不是使用 Adapty 的 `makePurchase` 方法。 **解决方案**:如果您使用 `makePurchase` 处理购买,请关闭观察者模式。您需要二选一:使用 `makePurchase`,或在观察者模式下自行处理购买。详情请参阅[实现观察者模式](implement-observer-mode-kmp)。 ## 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` 的问题。 **原因**:这通常与沙盒测试问题有关。 **解决方案**:创建一个新的沙盒用户并重试。这通常可以解决与沙盒相关的购买完成处理程序问题。 --- # File: kmp-user --- --- title: "Kotlin Multiplatform SDK 中的用户与访问管理" description: "了解如何在 Kotlin Multiplatform 应用中使用 Adapty SDK 处理用户和访问等级。" --- 本页汇总了在 Kotlin Multiplatform 应用中处理用户和访问等级的所有指南。请选择您需要的主题: - **[识别用户](kmp-identifying-users)** - 了解如何在应用中识别用户 - **[更新用户数据](kmp-setting-user-attributes)** - 设置用户属性和用户画像数据 - **[监听订阅状态变化](kmp-listen-subscription-changes)** - 实时监控订阅变更 - **[Kids 模式](kids-mode-kmp)** - 为您的应用实现 Kids 模式 --- # File: kmp-identifying-users --- --- title: "在 Kotlin Multiplatform SDK 中识别用户" description: "在 Adapty 中识别用户,以提升个性化订阅体验。" --- Adapty 会为每位用户创建一个内部用户画像 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()` 方法: ```kotlin showLineNumbers Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId("YOUR_USER_ID") .build() ).onSuccess { // successful activation }.onError { error -> // handle the error } } ``` :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ### 在初始化后设置用户 ID \{#setting-customer-user-id-after-configuration\} 如果在 SDK 初始化时没有用户 ID,可以随时通过 `.identify()` 方法进行设置。最常见的使用场景是在用户注册或登录之后,即用户从匿名状态切换为已认证状态时。 ```kotlin showLineNumbers Adapty.identify("YOUR_USER_ID").onSuccess { // successful identify }.onError { error -> // handle the error } ``` 请求参数: - **Customer User ID**(必填):字符串类型的用户标识符。 :::warning 重新提交重要用户数据 在某些情况下,例如用户再次登录其账户时,Adapty 服务器可能已经存储了该用户的信息。在这种情况下,Adapty SDK 会自动切换到新用户。如果你之前向匿名用户传递了任何数据(例如自定义属性或来自第三方网络的归因数据),需要为已识别的用户重新提交这些数据。 同样需要注意的是,识别用户后应重新请求所有付费墙和产品,因为新用户的数据可能有所不同。 ::: ### 登出与登录 \{#logging-out-and-logging-in\} 您可以随时调用 `.logout()` 方法将用户登出: ```kotlin showLineNumbers Adapty.logout().onSuccess { // successful logout }.onError { error -> // handle the error } ``` 之后可以使用 `.identify()` 方法让用户重新登录。 ## 分配 `appAccountToken`(iOS)\{#assign-appaccounttoken-ios\} [`iosAppAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) 是一个 **UUID**,用于将 App Store 交易与你的内部用户身份关联起来。 StoreKit 会将此 token 附加到每笔交易中,这样你的后端就能将 App Store 数据与用户进行匹配。 建议为每个用户生成一个稳定的 UUID,并在同一账号的不同设备上复用它。 这样可以确保购买记录和 App Store 通知始终与正确的用户绑定。 您可以通过两种方式设置 token——在 SDK 激活时或在识别用户时。 :::important 您必须始终将 `iosAppAccountToken` 与 `customerUserId` 一起传递。 如果只传递 token,它将不会包含在交易中。 ::: ```kotlin showLineNumbers // 配置时: Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId( id = "YOUR_USER_ID", iosAppAccountToken = "YOUR_IOS_APP_ACCOUNT_TOKEN" ) .build() ).onSuccess { // 激活成功 }.onError { error -> // 处理错误 } // 或在识别用户时 Adapty.identify( customerUserId = "YOUR_USER_ID", iosAppAccountToken = "YOUR_IOS_APP_ACCOUNT_TOKEN" ).onSuccess { // 识别成功 }.onError { error -> // 处理错误 } ``` ## 设置混淆账户 ID(Android)\{#set-obfuscated-account-ids-android\} Google Play 在某些场景下要求提供混淆账户 ID,以保护用户隐私和安全。这些 ID 可帮助 Google Play 在不暴露用户信息的情况下识别购买记录,对防范欺诈和数据分析尤为重要。 如果你的应用涉及敏感用户数据,或需要遵守特定隐私法规,就可能需要设置这些 ID。混淆 ID 让 Google Play 能够追踪购买行为,同时不会泄露真实的用户标识符。 :::important 您必须始终将 `androidObfuscatedAccountId` 与 `customerUserId` 一起传递。 如果仅传递混淆账号 ID,它将不会包含在交易中。 ::: ```kotlin showLineNumbers // 配置时: Adapty.activate( AdaptyConfig.Builder("PUBLIC_SDK_KEY") .withCustomerUserId( id = "YOUR_USER_ID", androidObfuscatedAccountId = "YOUR_OBFUSCATED_ACCOUNT_ID" ) .build() ).onSuccess { // 激活成功 }.onError { error -> // 处理错误 } // 或在识别用户时 Adapty.identify( customerUserId = "YOUR_USER_ID", androidObfuscatedAccountId = "YOUR_OBFUSCATED_ACCOUNT_ID" ).onSuccess { // 识别成功 }.onError { error -> // 处理错误 } ``` ## 跨设备用户识别 \{#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: kmp-setting-user-attributes --- --- title: "在 Kotlin Multiplatform SDK 中设置用户属性" description: "了解如何在 Adapty 中设置用户属性以实现更好的目标受众细分。" --- 您可以为应用用户设置可选属性,例如电子邮件、电话号码等。您可以使用这些属性来创建用户[市场细分](segments),或直接在 CRM 中查看。 ### 设置用户属性 \{#setting-user-attributes\} 要设置用户属性,请调用 `.updateProfile()` 方法: ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() .withEmail("email@email.com") .withPhoneNumber("+18888888888") .withFirstName("John") .withLastName("Appleseed") .withGender(AdaptyProfile.Gender.FEMALE) .withBirthday(AdaptyProfile.Date(1970, 1, 3)) Adapty.updateProfile(builder.build()) .onSuccess { // profile updated successfully } .onError { error -> // handle the error } ``` 请注意,您之前通过 `updateProfile` 方法设置的属性不会被重置。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ### 允许的键列表 \{#the-allowed-keys-list\} `AdaptyProfileParameters.Builder` 的允许键 `phoneNumber
firstName
lastName
| String | | gender | 枚举,允许的值为:`AdaptyProfile.Gender.FEMALE`、`AdaptyProfile.Gender.MALE`、`AdaptyProfile.Gender.OTHER` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 您可以设置自定义属性,这些属性通常与您的应用使用情况相关。例如,对于健身应用,可以是每周锻炼次数;对于语言学习应用,可以是用户的知识水平,等等。您可以在市场细分中使用这些属性来创建针对性付费墙和优惠,也可以在数据分析中用它们来找出哪些产品指标对收入影响最大。 ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() builder.withCustomAttribute("key1", "value1") ``` 要删除已有的键,请使用 `.withRemovedCustomAttribute()` 方法: ```kotlin showLineNumbers val builder = AdaptyProfileParameters.Builder() builder.withRemovedCustomAttribute("key2") ``` 有时您需要了解哪些自定义属性已经被设置过。为此,可以使用 `AdaptyProfile` 对象的 `customAttributes` 字段。 :::warning 请注意,`customAttributes` 的值可能不是最新的,因为用户属性可以随时从不同设备发送,因此服务器上的属性可能在上次同步后已发生变更。 ::: ### 限制 \{#limits\} - 每个用户最多 30 个自定义属性 - 键名最长 30 个字符,键名可包含字母数字字符及以下任意字符:`_` `-` `.` - 值可以是字符串或浮点数,最长不超过 50 个字符。 --- # File: kmp-listen-subscription-changes --- --- title: "在 Kotlin Multiplatform SDK 中检查订阅状态" description: "在 Adapty 中跟踪和管理用户订阅状态,以提高 Kotlin Multiplatform 应用的用户留存率。" --- 借助 Adapty,追踪订阅状态变得十分简单。您无需在代码中手动插入产品 ID,而是可以通过检查活跃的[访问等级](access-level)来轻松确认用户的订阅状态。 在开始检查订阅状态之前,请先设置[实时开发者通知(RTDN)](enable-real-time-developer-notifications-rtdn)。 ## 访问等级与 AdaptyProfile 对象 \{#access-level-and-the-adaptyprofile-object\} 访问等级是 [AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/) 对象的属性。我们建议在应用启动时获取用户画像(例如在[识别用户](android-identifying-users#setting-customer-user-id-on-configuration)时),并在发生变更时及时更新。这样,您就可以直接使用用户画像对象,而无需反复请求。 如需接收用户画像更新通知,请按照下方[监听用户画像更新(包括访问等级变化)](android-listen-subscription-changes)章节中的说明监听用户画像变更。 :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: ## 从服务器获取访问等级 \{#retrieving-the-access-level-from-the-server\} 如需从服务器获取访问等级,请使用 `.getProfile()` 方法: ```kotlin showLineNumbers Adapty.getProfile().onSuccess { profile -> // check the access }.onError { error -> // handle the error } ``` 响应参数: | 参数 | 说明 | | --------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Profile |[AdaptyProfile](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-profile/) 对象。通常情况下,您只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级访问权限。
`.getProfile` 方法始终会尝试查询 API,因此能提供最新的结果。如果由于某些原因(例如无网络连接)导致 Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。此外,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持信息的实时性。
| `.getProfile()` 方法会返回用户画像,您可以从中获取访问等级状态。一个应用可以设置多个访问等级。例如,如果您有一个新闻应用,并向用户独立销售不同主题的订阅,则可以创建"sports"和"science"两个访问等级。但在大多数情况下,您只需要一个访问等级,此时直接使用默认的"premium"访问等级即可。 以下是检查默认"premium"访问等级的示例: ```kotlin showLineNumbers Adapty.getProfile().onSuccess { profile -> if (profile.accessLevels["premium"]?.isActive == true) { // grant access to premium features } }.onError { error -> // handle the error } ``` ### 监听订阅状态更新 \{#listening-for-subscription-status-updates\} 每当用户的订阅发生变化时,Adapty 都会触发一个事件。 如需接收来自 Adapty 的消息,您需要进行以下额外配置: ```kotlin showLineNumbers Adapty.setOnProfileUpdatedListener { profile -> // handle any changes to subscription state } ``` Adapty 也会在应用启动时触发一个事件,此时将传递缓存中的订阅状态。 ### 订阅状态缓存 \{#subscription-status-cache\} Adapty SDK 中实现的缓存会存储用户画像的订阅状态。这意味着即使服务器不可用,也可以访问缓存数据以获取用户画像的订阅状态信息。 但需要注意的是,无法直接从缓存中请求数据。SDK 会每分钟定期查询服务器,检查是否有与用户画像相关的更新或变更。如果存在任何修改(例如新的交易或其他更新),这些修改将同步至缓存数据,以确保其与服务器保持一致。 --- # File: kmp-deal-with-att --- --- title: "在 Kotlin Multiplatform SDK 中处理 ATT" description: "开始在 Kotlin Multiplatform 上使用 Adapty,以简化订阅设置和管理。" --- 如果您的应用使用了 AppTrackingTransparency 框架,并向用户展示应用追踪授权请求,则您需要将[授权状态](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/)发送给 Adapty。 ```kotlin showLineNumbers val profileParameters = AdaptyProfileParameters.Builder() .withAttStatus(3) // 3 = ATTrackingManagerAuthorizationStatusAuthorized .build() Adapty.updateProfile(profileParameters) .onSuccess { // ATT status updated successfully } .onError { error -> // handle AdaptyError } ``` :::warning 我们强烈建议您在该值发生变化时尽早发送,只有这样,数据才能及时传递到您已配置的集成渠道。 ::: --- # File: kids-mode-kmp --- --- title: "Kotlin Multiplatform SDK 中的儿童模式" description: "轻松启用儿童模式以符合 Google 政策。Kotlin Multiplatform SDK 中不收集 GAID 或广告数据。" --- 如果您的 Kotlin Multiplatform 应用面向儿童用户,则必须遵循 [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。`可选
默认值:`en`
| 用户引导本地化的标识符。该参数应为由一个或两个子标签通过减号(**-**)连接组成的语言代码。第一个子标签表示语言,第二个子标签表示地区。示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此选项,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时优先返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况多差,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后依然保留,仅在应用卸载重装或手动清理时才会清除。
Adapty SDK 通过两层机制在本地存储用户引导:上述定期更新的缓存,以及备用用户引导。我们还使用 CDN 加速用户引导的获取,并设有独立的备用服务器以应对 CDN 不可用的情况。该系统旨在确保您始终获取最新版本的用户引导,同时在网络条件较差时也能保持可靠性。
| | **loadTimeout** | 默认值:5 秒 |该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能包含多个不同的请求。
| 响应参数: | 参数 | 描述 | |:----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://kmp.adapty.io///adapty/com.adapty.kmp.models/-adapty-onboarding/) 对象,包含:用户引导标识符与配置、远程配置以及其他若干属性。 | ## 使用默认目标受众用户引导加速获取用户引导 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常情况下,用户引导的获取几乎是即时完成的,无需担心速度问题。但当您配置了大量目标受众和用户引导,且用户网络连接较差时,获取用户引导可能会比预期花费更长时间。在这种情况下,您可能希望展示一个默认用户引导,以确保流畅的用户体验,而不是什么都不显示。 要解决这个问题,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法会获取指定版位中针对 **All Users** 目标受众的用户引导。但请务必了解,推荐的方式是通过 `getOnboarding` 方法来获取用户引导,详见上文的[获取用户引导](#fetch-onboarding)章节。 :::warning 建议使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者存在以下重要限制: - **兼容性问题**:在支持多个应用版本时可能引发问题,需要进行向后兼容设计,否则旧版本可能显示异常。 - **无个性化**:仅展示"全部用户"目标受众的内容,无法根据国家、归因或自定义属性进行定向。 如果更快的获取速度对你的使用场景更重要,请按以下方式使用 `getOnboardingForDefaultAudience`。否则,请按[上文](#fetch-onboarding)所述使用 `getOnboarding`。 ::: ```kotlin showLineNumbers Adapty.getOnboardingForDefaultAudience( placementId = "YOUR_PLACEMENT_ID", locale = "en", fetchPolicy = AdaptyPaywallFetchPolicy.Default, ).onSuccess { paywall -> // the requested paywall }.onError { error -> // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。该值在 Adapty 看板中创建版位时由您指定。 | | **locale** |可选
默认值:`en`
| 用户引导本地化的标识符。该参数应为语言代码,由一个或两个子标签组成,以连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此选项,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时直接返回缓存数据。这样用户获取到的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后依然保留,只有在卸载重装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制在本地存储用户引导:上述定期更新的缓存,以及备用用户引导。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可用时提供独立的备用服务器。该机制旨在确保您始终能获取最新版本的用户引导,同时在网络条件较差的情况下也能保证可靠性。
| --- # File: kmp-present-onboardings --- --- title: "在 Kotlin Multiplatform SDK 中展示用户引导" description: "了解如何有效地展示用户引导以提升转化率。" --- :::warning **用户引导功能已在 SDK v4 中弃用,并将在未来版本中移除。** 该功能不再接受修复或改进。请改用[流程](kmp-get-pb-paywalls):与在 WebView 中运行的用户引导不同,流程直接在设备上原生渲染——动画更流畅、外观与原生体验一致、加载速度更快,且无需依赖 WebView 运行时。请参阅[获取流程与付费墙](kmp-get-pb-paywalls)和[展示流程与付费墙](kmp-present-paywalls)以开始使用。 ::: 如果您已使用编辑工具自定义了用户引导,则无需在 Kotlin Multiplatform 应用代码中手动处理其渲染逻辑来向用户展示它。此类用户引导已同时包含应显示的内容及其显示方式。 在开始之前,请确保: 1. 您已安装 [Adapty Kotlin Multiplatform SDK](sdk-installation-kotlin-multiplatform) 3.16.1 或更高版本。 2. 您已[创建用户引导](create-onboarding)。 3. 您已将用户引导添加到[版位](placements)。 Adapty Kotlin Multiplatform SDK 提供两种展示用户引导的方式: - **使用 Compose Multiplatform** - **不使用 Compose Multiplatform** ## 使用 Compose Multiplatform \{#with-compose-multiplatform\} 要显示用户引导,请在通过 `createOnboardingView` 方法创建的 `view` 上调用 `view.present()` 方法。每个 `view` 只能使用一次。如果需要再次显示用户引导,请再次调用 `createOnboardingView` 创建一个新的 `view` 实例。 :::warning 在未重新创建的情况下复用同一个 `view` 可能会导致错误。 ::: ```kotlin showLineNumbers title="Kotlin Multiplatform" viewModelScope.launch { AdaptyUI.createOnboardingView(onboarding = onboarding).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` ### 配置 iOS 呈现样式 \{#configure-ios-presentation-style\} 通过向 `present()` 方法传递 `iosPresentationStyle` 参数,可以配置用户引导在 iOS 上的呈现方式。该参数接受 `AdaptyUIIOSPresentationStyle.FULLSCREEN`(默认值)或 `AdaptyUIIOSPresentationStyle.PAGESHEET`。 ```kotlin showLineNumbers viewModelScope.launch { val view = AdaptyUI.createOnboardingView(onboarding = onboarding).getOrNull() view?.present(iosPresentationStyle = AdaptyUIIOSPresentationStyle.PAGESHEET) } ``` ### 自定义用户引导中链接的打开方式 \{#customize-how-links-open-in-onboardings\} 默认情况下,用户引导中的链接会在应用内浏览器中打开。这种方式能让用户无需切换应用即可浏览网页,提供流畅的使用体验。 如果你希望链接在外部浏览器中打开,可以将 `externalUrlsPresentation` 参数设置为 `AdaptyWebPresentation.EXTERNAL_BROWSER` 来自定义此行为: ```kotlin showLineNumbers viewModelScope.launch { AdaptyUI.createOnboardingView( onboarding = onboarding, externalUrlsPresentation = AdaptyWebPresentation.EXTERNAL_BROWSER // default – IN_APP_BROWSER ).onSuccess { view -> view.present() }.onError { error -> // handle the error } } ``` ## 不使用 Compose Multiplatform \{#without-compose-multiplatform\} :::note `createNativeOnboardingView` 是核心模块 `io.adapty:adapty-kmp` 的一部分。如果你的项目不使用 Compose Multiplatform,则无需添加 `io.adapty:adapty-kmp-ui` 依赖。 ::: 如需在不使用 Compose Multiplatform 的情况下嵌入用户引导,请调用 `createNativeOnboardingView`。它会返回一个 `AdaptyNativeOnboardingView`,你可以将其添加到布局中:
例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,委托方法 `onCustomAction` 将被触发,并携带来自编辑工具的操作 ID。您可以创建自己的 ID,例如 "allowNotifications"。
```kotlin
class MyAdaptyUIOnboardingsEventsObserver : AdaptyUIOnboardingsEventsObserver {
override fun onboardingViewOnCustomAction(
view: AdaptyUIOnboardingView,
meta: AdaptyUIOnboardingMeta,
actionId: String
) {
when (actionId) {
"openPaywall" -> {
// Display paywall from onboarding
// You would typically fetch and present a new paywall here
mainUiScope.launch {
// Example: Get paywall by placement ID
// val paywallResult = Adapty.getPaywall("your_placement_id")
// paywallResult.onSuccess { paywall ->
// val paywallViewResult = AdaptyUI.createPaywallView(paywall)
// paywallViewResult.onSuccess { paywallView ->
// paywallView.present()
// }
// }
}
}
"allowNotifications" -> {
// Handle notification permissions
}
else -> {
// Handle other custom actions
}
}
}
}
// Set up the observer
AdaptyUI.setOnboardingsEventsObserver(MyAdaptyUIOnboardingsEventsObserver())
```
2. 点击订阅组名称,即可在 **Subscriptions** 部分看到你的产品列表。
3. 确认要测试的产品已标记为 **Ready to Submit**。如未标记,请参考 [App Store 产品](app-store-products) 页面的说明。
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-kmp
---
---
title: "修复 Kotlin Multiplatform 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: kmp-sdk-migration-guides
---
---
title: "Kotlin Multiplatform SDK 迁移指南"
description: "Adapty Kotlin Multiplatform SDK 各版本的迁移指南。"
---
本页面包含 Adapty Kotlin Multiplatform SDK 的所有迁移指南。请选择您要迁移到的目标版本以查看详细说明:
- **[迁移至 v4.0(测试版)](migration-to-kmp-sdk-v4)**
- **[迁移至 v3.15](migration-to-kmp-315)**
---
# File: migration-to-kmp-sdk-v4
---
---
title: "将 Adapty Kotlin Multiplatform SDK 迁移至 v. 4.0"
description: "通过将付费墙 API 替换为流程 API,迁移至 Adapty Kotlin Multiplatform SDK v4.0(测试版),兼容流程编辑工具和付费墙编辑工具。"
---
Adapty Kotlin Multiplatform SDK 4.0(测试版)引入了流程功能,并相应地重命名了付费墙 API。新 API 同时支持新版流程编辑工具和现有的付费墙编辑工具——Adapty 看板端无需进行任何配置更改。
## 快速参考 \{#quick-reference\}
| v3 | v4 |
|---|---|
| `Adapty.getPaywall(placementId, locale)` | `Adapty.getFlow(placementId)` |
| `Adapty.getPaywallForDefaultAudience(placementId, locale)` | `Adapty.getFlowForDefaultAudience(placementId)` |
| `Adapty.getPaywallProducts(paywall)` | `Adapty.getPaywallProducts(flow)` |
| `Adapty.logShowPaywall(paywall)` | `Adapty.logShowFlow(flow)` |
| `AdaptyPaywall` | `AdaptyFlow` |
| `AdaptyUI.createPaywallView(paywall, ...)` | `AdaptyUI.createFlowView(flow, ...)` |
| `AdaptyUI.createNativePaywallView(...)` → `AdaptyNativePaywallView` | `AdaptyUI.createNativeFlowView(...)` → `AdaptyNativeFlowView` |
| `AdaptyUIPaywallView` | `AdaptyUIFlowView` |
| `AdaptyUI.presentPaywallView(view)` / `dismissPaywallView(view)` | `AdaptyUI.presentFlowView(view)` / `dismissFlowView(view)` |
| `AdaptyUI.setPaywallsEventsObserver(observer)` | `AdaptyUI.setFlowsEventsObserver(observer)` |
| `AdaptyUI.registerPaywallEventsListener` / `unregisterPaywallEventsListener` | `AdaptyUI.registerFlowEventsListener` / `unregisterFlowEventsListener` |
| `AdaptyUIPaywallsEventsObserver` | `AdaptyUIFlowsEventsObserver` |
| `AdaptyUIPaywallPlatformView(paywall, ...)` | `AdaptyUIFlowPlatformView(flow, ...)` |
| `paywallViewDidPerformAction`、`paywallViewDidAppear` 及其他 `paywallView...` 回调 | `flowViewDidPerformAction`、`flowViewDidAppear` 及其他 `flowView...` 回调 |
| `paywallViewDidFailRendering` | `flowViewDidReceiveError` |
`AdaptyPaywallProduct` 保持原名不变——产品仍然属于某个流程,`getPaywallProducts` 方法名也保持不变,现在接受一个 `AdaptyFlow` 参数。`getFlow` 和 `getFlowForDefaultAudience` 方法不再接受 `locale` 参数。购买和用户画像相关的 API(`makePurchase`、`restorePurchases`、`getProfile`、`identify`、`updateProfile`)以及通过 `setFallback` 设置备用付费墙的功能均保持不变。用户引导方法仍然可用,但已被废弃——详见[用户引导 API 废弃说明](#onboarding-api-deprecation)。部分默认行为有所变更——详见[默认行为变更](#default-behavior-changes)。
## 安装 \{#installation\}
v4.0 是预发布版本,因此需要固定精确版本——Gradle 不会通过动态范围选择预发布版本:
```toml showLineNumbers title="libs.versions.toml"
[versions]
adapty-kmp = "4.0.0-beta.1"
[libraries]
adapty-kmp = { module = "io.adapty:adapty-kmp", version.ref = "adapty-kmp" }
adapty-kmp-ui = { module = "io.adapty:adapty-kmp-ui", version.ref = "adapty-kmp" }
```
`adapty-kmp-ui` 模块仅在你通过 Compose Multiplatform 层(`view.present()`)渲染流程和付费墙时才需要用到。完整配置步骤请参阅[安装 Adapty SDK](sdk-installation-kotlin-multiplatform)。
底层原生 Adapty SDK 在两个平台上均已升级至 4.x 版本,且会自动解析——无需修改构建配置。iOS 部署目标仍为 **15.0**,本次发布未作更改。
## 获取流程 \{#fetching-flows\}
### getPaywall → getFlow
返回类型从 `AdaptyPaywall` 变更为 `AdaptyFlow`,同时移除了 `locale` 参数——渲染流程时,语言环境会自动解析;对于自定义付费墙,所有语言环境均通过 `flow.remoteConfigs` 返回:
```diff showLineNumbers
- Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en")
- .onSuccess { paywall ->
- // use the paywall
+ Adapty.getFlow("YOUR_PLACEMENT_ID")
+ .onSuccess { flow ->
+ // use the flow
}
.onError { error ->
// handle the error
}
```
`getPaywallForDefaultAudience` 已按相同方式重命名:
```diff showLineNumbers
- Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en")
+ Adapty.getFlowForDefaultAudience("YOUR_PLACEMENT_ID")
```
### getPaywallProducts(paywall) → getPaywallProducts(flow)
`getPaywallProducts` 保持原名,但现在接受 `AdaptyFlow` 参数:
```diff showLineNumbers
- Adapty.getPaywallProducts(paywall)
+ Adapty.getPaywallProducts(flow)
.onSuccess { products ->
// use the products
}
```
## 数据模型 \{#data-model\}
`getFlow` 返回 `AdaptyFlow` 而非 `AdaptyPaywall`,对象结构也发生了变化:
| v3 `AdaptyPaywall` 属性 | v4 `AdaptyFlow` 属性 | 操作 |
|---|---|---|
| `remoteConfig: AdaptyRemoteConfig?`(单个) | `remoteConfigs: List
### 登录/注册时 \{#during-loginsignup\}
如果你在应用启动后才识别用户身份(例如用户登录或注册之后),请使用 `identify` 方法设置其 customer user ID。
- 如果你**之前从未使用过该 customer user ID**,Adapty 会自动将其关联到当前用户画像。
- 如果你**之前已使用该 customer user ID 识别过用户**,Adapty 会切换到与该 customer user ID 关联的用户画像。
:::important
每位用户的 Customer User ID 必须唯一。如果将该参数硬编码为固定值,所有用户将被视为同一个人。
:::
在调用其他 SDK 方法之前,请务必先 `await` `identify`。并发调用会产生 `#3006 profileWasChanged` 错误,或导致操作落在匿名用户画像上。详见 [React Native SDK 调用顺序](react-native-sdk-call-order)。
```typescript showLineNumbers
try {
await adapty.identify("YOUR_USER_ID"); // Unique for each user
// successfully identified
} catch (error) {
// handle the error
}
```
### 在 SDK 激活期间 \{#during-the-sdk-activation\}
如果在激活 SDK 时已知客户用户 ID,可以直接在 `activate` 方法中传入,无需单独调用 `identify`。
如果已知客户用户 ID,但在激活之后才设置,则意味着在激活时 Adapty 会创建一个新的匿名用户画像,只有在调用 `identify` 后才会切换到已有的用户画像。
您可以传入已有的客户用户 ID(之前使用过的),也可以传入一个新的。如果传入新 ID,激活时创建的新用户画像将自动与该客户用户 ID 关联。
:::note
默认情况下,创建匿名用户画像不会影响分析看板,因为安装量是基于设备 ID 统计的。
设备 ID 代表应用从商店安装到设备上的一次安装实例,仅在应用重新安装后才会重新生成。
它与此次安装是首次还是重复安装无关,也与是否使用了已有的客户用户 ID 无关。
创建用户画像(在 SDK 激活或退出登录时)、登录,或在不重新安装应用的情况下升级应用,均不会产生额外的安装事件。
如果您希望根据唯一用户而非设备来统计安装量,请前往 **App settings** 并配置 [**Installs definition for analytics**](general#4-installs-definition-for-analytics)。
:::
```typescript showLineNumbers
adapty.activate("PUBLIC_SDK_KEY", {
customerUserId: "YOUR_USER_ID" // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
});
```
### 注销用户 \{#log-users-out\}
如果您有用于注销用户的按钮,请使用 `logout` 方法。
:::important
注销用户将为该用户创建新的匿名用户画像。
:::
```typescript showLineNumbers
try {
await adapty.logout();
// successful logout
} catch (error) {
// handle the error
}
```
:::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`](react-native-check-subscription-status),也可以[监听用户画像更新](react-native-check-subscription-status)以自动同步数据。
## 下一步 \{#next-steps\}
恭喜!您已在应用中成功实现了应用内购买逻辑!祝您的应用变现一切顺利!
想要深入探索 Adapty 的更多功能,可以参考以下主题:
- [**测试**](troubleshooting-test-purchases):确保一切按预期正常运行
- [**用户引导**](react-native-onboardings):通过用户引导吸引用户并提升留存率
- [**集成**](configuration):只需一行代码即可与营销归因和数据分析服务完成集成
- [**设置自定义用户画像属性**](react-native-setting-user-attributes):为用户画像添加自定义属性并创建市场细分,从而发起 A/B 测试或向不同用户展示不同的付费墙
---
# File: adapty-sdk-integration-skill-react-native
---
---
title: "使用 SDK 集成技能将 Adapty 集成到您的 React Native 应用"
description: "使用 adapty-sdk-integration 技能,通过 AI 编程工具将 Adapty SDK 端到端集成到您的 React Native 应用中。"
---
[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-react-native)——它会引导你的 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-react-native
---
---
title: "借助 AI 将 Adapty 集成到 React Native 应用"
description: "一步步指引你使用 Cursor、Context7、ChatGPT、Claude 或其他 AI 工具将 Adapty 集成到 React Native 应用中。"
---
本指南将带你一步步完成 React Native 应用与 Adapty 的集成,借助 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\}
在编写任何 SDK 代码之前,Adapty 需要进行一些看板配置。你可以通过交互式 LLM 技能来完成,也可以通过看板手动操作。
### 技能方式(推荐) \{#skill-approach-recommended\}
Adapty CLI 技能让你的 LLM 可以直接设置应用、产品、访问等级、付费墙和版位——无需为每个步骤打开看板。你只需在看板中[连接你的应用商店](integrate-payments)即可。
```
npx skills add adaptyteam/adapty-cli --skill adapty-cli
```
添加技能后,在你的 agent 中运行 `/adapty-cli`。它将引导你完成每个步骤——包括何时打开看板连接你的应用商店。
### 看板配置方式 \{#dashboard-approach\}
如果你更倾向于手动配置,以下是写代码前需要准备的内容。你的 LLM 无法自动查询看板中的值——需要你自行提供。
1. **连接应用商店**:在 Adapty 看板中,前往 **App settings → General**,将 App Store 和 Google Play 都连接上(如果你的应用同时支持两个平台)。这是购买功能正常运行的必要条件。
[连接应用商店](integrate-payments)
2. **复制你的 Public SDK key**:在 Adapty 看板中,进入 **App settings → General**,找到 **API keys** 部分。在代码中,这就是你传给 `adapty.activate("YOUR_PUBLIC_SDK_KEY")` 的字符串。
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)。
## 向你的 LLM 提供 Adapty 文档 \{#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 React Native SDK
```
:::warning
尽管 Context7 省去了手动粘贴文档链接的步骤,但实施顺序仍然很重要。请按照下方的[实施步骤](#implementation-walkthrough)逐步操作,确保一切正常运行。
:::
### 使用纯文本文档 \{#use-plain-text-docs\}
你可以以纯文本 Markdown 格式访问任意 Adapty 文档。在其 URL 末尾添加 `.md`,或点击文章标题下方的 **Copy for LLM**。例如:[adapty-cursor-react-native.md](https://adapty.io/docs/zh/adapty-cursor-react-native.md)。
下面[实施演练](#implementation-walkthrough)中的每个阶段都包含一个"发送给你的 LLM"代码块,其中包含可粘贴的 `.md` 链接。
如需一次获取更多文档,请参阅下面的[索引文件和平台专属子集](#plain-text-doc-index-files)。
## 实施演练 \{#implementation-walkthrough\}
本指南的其余部分按实施顺序介绍 Adapty 集成。每个阶段都包含要发送给 LLM 的文档、完成后应看到的结果以及常见问题。
### 规划集成方案 \{#plan-your-integration\}
在动手写代码之前,先让 LLM 分析你的项目并制定实施计划。如果你使用的 AI 工具支持规划模式(如 Cursor 或 Claude Code 的 plan mode),建议先用规划模式,让 LLM 在编写代码前同时读取你的项目结构和 Adapty 文档。
告诉 LLM 你采用的购买实现方式——这决定了它应该参考哪些指南:
- [**Adapty 付费墙编辑工具**](adapty-paywall-builder):在 Adapty 的无代码编辑工具中创建付费墙,SDK 会自动渲染。
- [**手动创建的付费墙**](react-native-making-purchases):自行编写付费墙 UI 代码,但仍使用 Adapty 获取产品并处理购买流程。
- [**观察者模式**](observer-vs-full-mode):保留现有的购买基础设施,仅使用 Adapty 进行数据分析和集成。
不确定该选哪种?请查看[快速入门中的对比表格](react-native-quickstart-paywalls)。
### 安装与配置 SDK \{#install-and-configure-the-sdk\}
使用 npm(或 yarn)添加 Adapty SDK 依赖,并通过你的 Public SDK key 激活它。这是一切的基础——没有这一步,其他功能都无法使用。
我们为 Expo 和纯 React Native 项目分别提供了独立的安装指南——请根据你的项目类型选择对应的指南。
**指南:**
- [使用 Expo 安装](sdk-installation-react-native-expo)
- [使用纯 React Native 安装](sdk-installation-react-native-pure)
:::tip[Checkpoint]
- **预期结果:** 应用在 iOS 和 Android 上均能成功构建并运行。Metro bundler 日志中可以看到 Adapty 激活日志。
- **常见问题:** 出现 "Public API key is missing" → 检查是否已将占位符替换为 **App settings** 中的真实密钥。
:::
### 展示付费墙并处理购买 \{#show-paywalls-and-handle-purchases\}
通过版位 ID 获取付费墙,展示它,并处理购买事件。所需的指南取决于你处理购买的方式。
建议边开发边在沙盒中测试每次购买,不要等到最后再测。设置说明请参阅[在沙盒中测试购买](test-purchases-in-sandbox)。
默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,当缓存存在时优先返回缓存数据。这种情况下,用户可能无法获取最新数据,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制在本地存储付费墙:上述定期更新的缓存,以及[备用付费墙](fallback-paywalls)。我们还使用 CDN 加速付费墙的获取,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保你始终获取最新版本的付费墙,同时即便在网络条件较差的情况下也能保证可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |该值限制此方法的超时时间。若达到超时时间,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能由多个请求组成。
对于 Android:你可以使用扩展函数创建 `TimeInterval`(例如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或使用 `TimeInterval.seconds(5)`。若不设置限制,请使用 `TimeInterval.INFINITE`。
| 响应参数: | 参数 | 描述 | | :-------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Flow | 一个 `AdaptyFlow` 对象,包含流程的标识符(`id`、`variationId`)、名称、版位、付费墙实验变体(`paywalls`)以及任何远程配置(`remoteConfigs`)。 | ## 获取视图配置 \{#fetch-the-view-configuration\} :::important 请确保在编辑工具中启用 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 如果版位是在 **Flow Builder** 或 **Paywall Builder** 中设计的,Adapty 会为你渲染 UI。使用 `createFlowView` 创建视图,然后[展示流程或付费墙](react-native-present-paywalls)。如果版位是没有编辑工具 UI 的自定义付费墙,请改为[将其作为远程配置付费墙处理](present-remote-config-paywalls-react-native)。 在 React Native SDK 中,直接调用 `createFlowView` 即可——无需提前获取视图配置。 :::warning `createFlowView` 方法的返回结果只能使用一次。如需再次使用,请重新调用 `createFlowView` 方法。若不重新创建而直接调用两次,可能会导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```typescript showLineNumbers try { const view = await createFlowView(flow); } catch (error) { // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | | :------------------- | :------- | :----------------------------------------------------------- | | **flow** | 必填 | 一个 `AdaptyFlow` 对象,用于获取所需流程/付费墙的控制器。 | | **customTags** | 可选 | 定义自定义标签及其解析值的字典。自定义标签作为内容中的占位符,在流程/付费墙中动态替换为指定字符串,以实现个性化内容。详情请参阅付费墙编辑工具中自定义标签相关主题。 | | **prefetchProducts** | 可选 | 开启后可优化产品在屏幕上的显示时机。设为 `true` 时,AdaptyUI 将自动预取所需产品。默认值:`false`。 | | **android.enableSafeArea** | 可选 | 仅限 Android(iOS 上会被忽略)。以嵌套对象形式传入:`android: { enableSafeArea: true }`。设为 `true` 时,流程视图会应用安全区域内边距。在模态弹窗(`createFlowView` + `present()`)场景下默认为 `true`,在嵌入式 `AdaptyFlowView` 组件中默认为 `false`。默认值适用于大多数场景。 | :::note 如果您使用多种语言,请了解如何添加[流程本地化](add-paywall-locale-in-adapty-paywall-builder),以及如何正确使用语言区域代码([查看详情](react-native-localizations-and-locale-codes))。 ::: 获取视图后,请[展示流程/付费墙](react-native-present-paywalls)。 ## 为默认目标受众获取流程或付费墙以加快获取速度 \{#get-a-flow-or-paywall-for-a-default-audience-to-fetch-it-faster\} 通常,流程和付费墙的获取几乎是即时的,无需担心速度问题。但当你拥有大量目标受众和版位,且用户网络连接较弱时,获取流程或付费墙的时间可能会比预期更长。在这种情况下,你可能希望展示一个默认的流程或付费墙,以确保良好的用户体验,而不是让用户看到空白页面。 为了解决这个问题,你可以使用 `getFlowForDefaultAudience` 方法,该方法会获取指定版位中 **All Users** 目标受众的流程或付费墙。但请务必了解,推荐的做法是通过 `getFlow` 方法来获取流程或付费墙,详情请参阅上方的[获取流程/付费墙](#fetch-flowpaywall)章节。 :::warning 为什么我们推荐使用 `getFlow` `getFlowForDefaultAudience` 方法存在以下几个明显的缺陷: - **潜在的向后兼容性问题**:如果你需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会遇到挑战。你要么必须设计能够支持当前(旧版)版本的付费墙,要么接受使用当前(旧版)版本的用户可能遇到付费墙无法渲染的问题。 - **失去精准定向**:所有用户都将看到为 **All Users** 目标受众设计的同一个付费墙,这意味着你将失去个性化定向能力(包括基于国家/地区、营销归因或自定义属性的定向)。 如果您愿意接受这些缺点以换取更快的 flow 或付费墙获取速度,请按以下方式使用 `getFlowForDefaultAudience` 方法。否则,请继续使用[上文](#fetch-flowpaywall)介绍的 `getFlow`。 ::: ```typescript showLineNumbers try { const id = 'YOUR_PLACEMENT_ID'; const flow = await adapty.getFlowForDefaultAudience(id); // the requested flow/paywall } catch (error) { // handle the error } ``` | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
不过,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,缓存在应用重启后依然保留,只有在应用重新安装或手动清除时才会被清空。
| ## 自定义素材 \{#customize-assets\} 要自定义流程/付费墙中的图片和视频,请实现自定义素材。 主图和视频有预定义 ID:`hero_image` 和 `hero_video`。在自定义素材包中,你通过这些 ID 来定位对应元素并自定义其行为。 对于其他图片和视频,你需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,你可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载时先显示本地预览图。 - 在播放视频前先展示预览图。 :::important 要使用此功能,请将 Adapty React Native SDK 更新至 3.8.0 或更高版本。 ::: 以下是通过简单字典提供自定义素材资源的示例: ```javascript const customAssets: Record可选
默认值:`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 不可用时提供独立的备用服务器。该机制旨在确保您始终获取最新版本的付费墙,同时在网络条件较差的情况下也能保证可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |该值限制此方法的超时时间。达到超时后,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能由多个请求组成。
对于 Android:您可以使用扩展函数创建 `TimeInterval`(例如 `5.seconds`,其中 `.seconds` 来自 `import com.adapty.utils.seconds`),或使用 `TimeInterval.seconds(5)`。如需不设限制,请使用 `TimeInterval.INFINITE`。
| ## 响应参数 \{#response-parameters\} | 参数 | 描述 | | :-------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Paywall | 一个 [`AdaptyPaywall`](https://react-native.adapty.io/interfaces/adaptypaywall) 对象,包含产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取使用付费墙编辑工具设计的付费墙的视图配置 \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\} :::important 请确保在付费墙编辑工具中启用了 **Show on device** 开关。如果未开启此选项,将无法获取视图配置。 ::: 获取付费墙后,检查它是否包含 `ViewConfiguration`,这表明它是使用付费墙编辑工具创建的。这将指导您如何展示该付费墙。如果存在 `ViewConfiguration`,将其作为付费墙编辑工具付费墙处理;否则,[将其作为远程配置付费墙处理](present-remote-config-paywalls-react-native)。 在 React Native SDK 中,直接调用 `createPaywallView` 方法,无需手动预先获取视图配置。 :::warning `createPaywallView` 方法的返回结果只能使用一次。如果需要再次使用,请重新调用 `createPaywallView` 方法。不重新创建而重复调用可能导致 `AdaptyUIError.viewAlreadyPresented` 错误。 ::: ```typescript showLineNumbers // for the Adapty SDK < 3.14 – import {createPaywallView} from 'react-native-adapty/dist/ui'; if (paywall.hasViewConfiguration) { try { const view = await createPaywallView(paywall); } catch (error) { // handle the error } } else { //use your custom logic } ``` 参数: | 参数 | 是否必填 | 说明 | | :------------------- | :------- | :----------------------------------------------------------- | | **paywall** | 必填 | `AdaptyPaywall` 对象,用于获取目标付费墙的控制器。 | | **customTags** | 可选 | 定义自定义标签及其对应值的字典。自定义标签作为付费墙内容中的占位符,在运行时动态替换为特定字符串,实现付费墙内容的个性化展示。详情请参阅付费墙编辑工具中的自定义标签相关主题。 | | **prefetchProducts** | 可选 | 启用后可优化产品在屏幕上的显示时机。设为 `true` 时,AdaptyUI 将自动预取所需产品。默认值:`false`。 | :::note 如果您使用多种语言,请了解如何添加[付费墙编辑工具本地化](add-paywall-locale-in-adapty-paywall-builder)以及如何正确使用语言区域代码,详见[此处](react-native-localizations-and-locale-codes)。 ::: 获取视图后,[展示付费墙](react-native-present-paywalls)。 ## 为默认目标受众获取付费墙以加快获取速度 \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\} 通常情况下,付费墙几乎可以立即获取,因此您无需担心加快此过程。但是,当您拥有大量目标受众和付费墙,且用户网络连接较弱时,获取付费墙可能需要比预期更长的时间。在这种情况下,您可能希望展示默认付费墙,以确保流畅的用户体验,而非不展示任何付费墙。 为解决这一问题,您可以使用 `getPaywallForDefaultAudience` 方法,该方法获取指定版位针对**所有用户**目标受众的付费墙。但请务必了解,推荐的方式是通过 `getPaywall` 方法获取付费墙,详见上方[获取付费墙信息](#fetch-paywall-designed-with-paywall-builder)部分。 :::warning 为什么我们推荐使用 `getPaywall` `getPaywallForDefaultAudience` 方法存在一些显著缺点: - **潜在的向后兼容性问题**:如果您需要为不同的应用版本(当前版本和未来版本)展示不同的付费墙,可能会面临挑战。您要么必须设计支持当前(旧版)版本的付费墙,要么接受使用当前(旧版)版本的用户可能遇到付费墙无法渲染的问题。 - **失去定向能力**:所有用户都将看到针对**所有用户**目标受众设计的同一付费墙,这意味着您将失去个性化定向(包括基于国家/地区、营销归因或自定义属性的定向)。 如果您愿意接受这些缺点以换取更快的付费墙获取速度,请按如下方式使用 `getPaywallForDefaultAudience` 方法。否则,请使用[上述](#fetch-paywall-designed-with-paywall-builder) `getPaywall` 方法。 ::: ```typescript showLineNumbers try { const id = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const paywall = await adapty.getPaywallForDefaultAudience(id, locale); // the requested paywall } catch (error) { // handle the error } ``` :::note `getPaywallForDefaultAudience` 方法从 React Native SDK 2.11.2 版本开始可用。 ::: | 参数 | 是否必需 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必需 | [版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个以减号(**-**)分隔的子标签组成。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及推荐用法,请参阅[本地化与语言区域代码](react-native-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐使用此方式,因为它可确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,以便在缓存数据存在时直接返回。这种方式下,用户可能无法获取最新数据,但无论网络状况如何,都能享受更快的加载速度。缓存会定期更新,因此在会话期间使用缓存以避免网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在卸载应用或手动清理时才会被清除。
| ## 自定义素材资源 \{#customize-assets\} 要自定义付费墙中的图片和视频,请实现自定义素材资源。 主图和视频具有预定义的 ID:`hero_image` 和 `hero_video`。在自定义素材资源包中,您通过这些 ID 定位对应元素并自定义其行为。 对于其他图片和视频,您需要在 Adapty 看板中[设置自定义 ID](custom-media)。 例如,您可以: - 向部分用户展示不同的图片或视频。 - 在远程主图加载期间展示本地预览图。 - 在播放视频前展示预览图。 :::important 要使用此功能,请将 Adapty React Native SDK 更新至 3.8.0 或更高版本。 ::: 以下示例展示了如何通过简单的字典来提供自定义资源: ```javascript const customAssets: Record
## 付费墙浏览量数字过大 \{#the-paywall-view-number-is-too-big\}
**问题**:付费墙浏览次数显示的是预期数值的两倍。
**原因**:你可能在代码中调用了 `logShowFlow`(React Native SDK v4+)/ `logShowPaywall`,如果你使用的是付费墙编辑工具或 Flow Builder,这会导致浏览次数重复计算。对于通过这些工具构建的 flow 和付费墙,数据分析会自动追踪,无需手动调用此方法。
**解决方案**:如果你使用的是付费墙编辑工具或 Flow Builder,请确保代码中没有调用 `logShowFlow`(React Native SDK v4+)/ `logShowPaywall`。
## 其他问题 \{#other-issues\}
**问题**:您遇到了上述未涵盖的其他付费墙编辑工具相关问题。
**解决方案**:如有需要,请参考[迁移指南](react-native-sdk-migration-guides)将 SDK 升级至最新版本。许多问题已在较新版本的 SDK 中得到修复。
---
# File: react-native-quickstart-manual
---
---
title: "在 React Native SDK 中为自定义付费墙启用购买功能"
description: "将 Adapty SDK 集成到您的 React Native 自定义付费墙中,以启用应用内购买功能。"
---
本指南介绍如何将 Adapty 集成到您的自定义付费墙中。您可以完全掌控付费墙的实现方式,同时由 Adapty SDK 负责获取产品、处理新购买以及恢复历史购买记录。
:::important
**本指南适用于自行实现自定义付费墙的开发者。** 如果您希望以最简便的方式开启购买功能,请使用 [Adapty Flow Builder](react-native-quickstart-paywalls)。使用 Flow Builder,您可以在无代码可视化编辑器中创建流程,Adapty 自动处理所有购买逻辑,无需重新发布应用即可测试不同设计方案。
:::
## 开始之前 \{#before-you-start\}
### 配置产品 \{#set-up-products\}
要启用应用内购买,你需要了解三个核心概念:
- [**产品**](product) – 用户可以购买的任何内容(订阅、消耗型商品、永久授权)
- [**付费墙**](paywalls) – 定义向用户展示哪些产品的配置。在 Adapty 中,付费墙是获取产品的唯一方式,这种设计让你无需改动应用代码就能修改产品、价格和优惠。
- [**版位**](placements) – 应用中展示付费墙的位置和时机(如 `main`、`onboarding`、`settings`)。你在看板中为版位配置付费墙,然后在代码中通过版位 ID 请求。这让你能轻松运行 A/B 测试,并向不同用户展示不同的付费墙。
即使使用自定义付费墙,也请务必理解这些概念——它们本质上是管理应用内销售产品的方式。
要实现自定义付费墙,你需要创建一个**付费墙**并将其添加到**版位**中。这样才能获取你的产品信息。如需了解在看板中的具体操作步骤,请参阅[快速入门指南](quickstart)。
### 管理用户 \{#manage-users\}
您可以选择使用或不使用后端身份验证。
但是,Adapty SDK 对匿名用户和已识别用户的处理方式不同。请阅读[身份识别快速入门指南](react-native-quickstart-identify),了解其中的差异,确保正确处理用户数据。
## 第一步:获取产品 \{#step-1-get-products\}
要为自定义付费墙获取产品,你需要:
1. 通过将[版位](placements) ID 传入 `getFlow` 方法来获取 `flow` 对象。
2. 使用 `getPaywallProducts` 方法获取该流程的产品数组。
```typescript showLineNumbers
async function loadPaywall() {
try {
const flow: AdaptyFlow = await adapty.getFlow('YOUR_PLACEMENT_ID');
const products: AdaptyPaywallProduct[] = await adapty.getPaywallProducts(flow);
// Use products to build your custom paywall UI
} catch (error) {
// Handle the error
}
}
```
## 第二步:处理购买 \{#step-2-accept-purchases\}
当用户在自定义付费墙中点击某个产品时,调用 `makePurchase` 方法并传入所选产品。该方法会处理购买流程并返回更新后的用户画像。
```typescript showLineNumbers
async function purchaseProduct(product: AdaptyPaywallProduct) {
try {
const purchaseResult: AdaptyPurchaseResult = await adapty.makePurchase(product);
switch (purchaseResult.type) {
case 'success':
// Purchase successful, profile updated
break;
case 'user_cancelled':
// User canceled the purchase
break;
case 'pending':
// Purchase is pending (e.g., user will pay offline with cash)
break;
}
} catch (error) {
// Handle the error
}
}
```
## 第三步:恢复购买 \{#step-3-restore-purchases\}
应用商店要求所有包含订阅功能的应用提供恢复购买的入口。
当用户点击恢复购买按钮时,调用 `restorePurchases` 方法。该方法会将用户的购买历史与 Adapty 同步,并返回最新的用户画像。
```typescript showLineNumbers
async function restorePurchases() {
try {
const profile: AdaptyProfile = await adapty.restorePurchases();
// Restore successful, profile updated
} catch (error) {
// Handle the error
}
}
```
## 后续步骤 \{#next-steps\}
:::tip
有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助!
:::
您的付费墙已准备好在应用中展示。在 [App Store 沙盒](test-purchases-in-sandbox)或 [Google Play Store](testing-on-android) 中测试购买流程,确保您可以从付费墙完成测试购买。如需查看生产就绪实现的示例,请参考我们示例应用中的 [CustomPurchaseScreen.tsx](https://github.com/adaptyteam/AdaptySDK-React-Native/blob/master/examples/ExpoGoWebMock/src/CustomPurchaseScreen.tsx),其中演示了包含完善错误处理、加载状态和 UI 状态管理的购买处理流程。
接下来,[检查用户是否已完成购买](react-native-check-subscription-status),以确定是否应显示付费墙或授予付费功能访问权限。
---
# File: fetch-paywalls-and-products-react-native
---
---
title: "在 React Native SDK 中获取远程配置付费墙的付费墙和产品"
description: "通过 Adapty React Native SDK 获取付费墙和产品,提升用户变现效果。"
---
默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存存在时优先返回缓存数据。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,都能享受更快的加载速度。缓存会定期更新,因此在会话期间使用缓存来避免网络请求是安全的。
请注意,重启应用后缓存仍会保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储 flow 和付费墙:上述定期更新的缓存,以及[备用付费墙](react-native-use-fallback-paywalls)。我们还使用 CDN 来加快 flow 和付费墙的加载速度,并在 CDN 不可用时提供独立的备用服务器。该系统旨在确保您始终获取最新版本的 flow,同时在网络条件较差的情况下也能保持可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |该值限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,此方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为该操作在底层可能由多个请求组成。
| :::note 在 v4 中,`getFlow` 不再接受 `locale` 参数。对于自定义付费墙,所有可用的语言环境都会在流程的远程配置(`flow.remoteConfigs`)中返回——选择与用户设备或应用设置相匹配的那个即可。 ::: 不要硬编码产品 ID!由于流程是远程配置的,可用产品的数量以及特殊优惠(如免费试用)都可能随时变化。请确保你的代码能够处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果后来获取到 3 个产品,应用无需修改任何代码即可显示全部 3 个。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Flow | 一个 `AdaptyFlow` 对象,包含版位、标识符(`id`、`variationId`)、名称、付费墙变体(`paywalls`)以及 `remoteConfigs` 数组(每个已配置的语言区域对应一个条目)。如需获取该流程的产品,请调用 `getPaywallProducts(flow)`。 | ## 获取产品 \{#fetch-products\} 获取流程后,你可以查询与之对应的产品数组: ```typescript showLineNumbers try { // ...flow const products = await adapty.getPaywallProducts(flow); // the requested products list } catch (error) { // handle the error } ``` 响应参数: | 参数 | 描述 | | :-------- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他多个属性。 | 在实现自定义付费墙设计时,你可能需要访问 [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) 对象中的以下属性。下面列出了最常用的属性,完整属性列表请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 要显示产品标题,请使用 `product.localizedTitle`。请注意,本地化基于用户在商店中选择的国家/地区,而非设备本身的语言环境。 | | **Price** | 要显示本地化价格,请使用 `product.price?.localizedString`。该本地化基于设备的语言环境信息。您也可以通过 `product.price?.amount` 以数字形式获取价格,值将以当地货币表示。要获取对应的货币符号,请使用 `product.price?.currencySymbol`。 | | **Subscription Period** | 要显示订阅周期(如周、月、年等),请使用 `product.subscription?.localizedSubscriptionPeriod`,该本地化基于设备语言环境。若需以编程方式获取订阅周期,请使用 `product.subscription?.subscriptionPeriod`,通过其 `unit` 属性可获取周期单位(即 `'day'`、`'week'`、`'month'`、`'year'` 或 `'unknown'`),`numberOfUnits` 则表示周期单位的数量。例如,对于按季度订阅,`unit` 属性值为 `'month'`,`numberOfUnits` 值为 `3`。 | | **Introductory Offer** | 要显示订阅包含新用户优惠的标识或其他提示,请查看 `product.subscription?.offer?.phases` 属性。这是一个列表,最多包含两个折扣阶段:免费试用阶段和新用户优惠价格阶段。每个阶段对象包含以下实用属性:默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果你认为用户的网络环境不稳定,可以考虑使用 `.returnCacheDataElseLoad`——有缓存时直接返回缓存数据。这样用户获取的数据可能不是最新的,但加载速度更快,无论网络状况如何都能流畅体验。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清除时才会被清空。
|可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为语言代码,由一个或多个子标签组成,各子标签之间用连字符(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及其推荐用法的更多信息,请参阅[本地化与语言区域代码](react-native-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若加载失败则返回缓存数据。我们推荐此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户网络状况不稳定,可以考虑使用 `.returnCacheDataElseLoad`——当缓存数据存在时直接返回缓存。这种情况下,用户获取的数据可能不是最新的,但无论网络状况如何,加载速度都会更快。缓存会定期更新,因此在会话期间使用缓存以减少网络请求是安全的。
请注意,缓存在应用重启后仍然保留,只有在卸载应用或手动清理时才会被清除。
Adapty SDK 通过两层机制存储付费墙:上述定期更新的缓存,以及[备用付费墙](react-native-use-fallback-paywalls)。我们还使用 CDN 加速付费墙的拉取,并在 CDN 不可达时启用独立的备用服务器。该机制旨在确保您始终获取最新版本的付费墙,同时在网络条件较差的情况下也能保持可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |该值用于限制此方法的超时时间。若超时,将返回缓存数据或本地备用数据。
请注意,在极少数情况下,该方法的实际超时时间可能略晚于 `loadTimeout` 中指定的值,因为底层操作可能由多个请求组成。
| 不要硬编码产品 ID!由于付费墙是远程配置的,可用产品的数量以及特别优惠(如免费试用)随时可能发生变化。请确保你的代码能够处理这些情况。 例如,如果你最初获取到 2 个产品,应用应显示这 2 个产品;但如果之后获取到 3 个产品,应用无需任何代码改动就应显示全部 3 个。唯一需要硬编码的是版位 ID。 响应参数: | 参数 | 描述 | | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Paywall | 一个 [`AdaptyPaywall`](https://react-native.adapty.io/interfaces/adaptypaywall) 对象,包含:产品 ID 列表、付费墙标识符、远程配置及其他若干属性。 | ## 获取产品 \{#fetch-products\} 获取付费墙后,你可以查询与之对应的产品数组: ```typescript showLineNumbers try { // ...paywall const products = await adapty.getPaywallProducts(paywall); // the requested products list } catch (error) { // handle the error } ``` 响应参数: | 参数 | 描述 | | :-------- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Products | [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) 对象列表,包含:产品标识符、产品名称、价格、货币、订阅时长及其他多个属性。 | 在实现自定义付费墙设计时,你可能需要访问 [`AdaptyPaywallProduct`](https://react-native.adapty.io/interfaces/adaptypaywallproduct) 对象中的以下属性。下面列出了最常用的属性,完整的属性说明请参阅链接文档。 | 属性 | 描述 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Title** | 要显示产品标题,请使用 `product.localizedTitle`。请注意,本地化基于用户所选的商店国家/地区,而非设备本身的语言区域设置。 | | **Price** | 要显示本地化价格,请使用 `product.price?.localizedString`。本地化基于设备的语言区域信息。您也可以通过 `product.price?.amount` 以数字形式获取价格,该值将以本地货币为单位。要获取对应的货币符号,请使用 `product.price?.currencySymbol`。 | | **Subscription Period** | 要显示订阅周期(如周、月、年等),请使用 `product.subscription?.localizedSubscriptionPeriod`,本地化基于设备的语言区域。如需以编程方式获取订阅周期,请使用 `product.subscription?.subscriptionPeriod`,通过其 `unit` 属性可获取周期单位(即 `'day'`、`'week'`、`'month'`、`'year'` 或 `'unknown'`),`numberOfUnits` 则表示周期单位数量。例如,对于按季度订阅,`unit` 属性显示 `'month'`,`numberOfUnits` 显示 `3`。 | | **Introductory Offer** | 要显示徽章或其他指示器以表明订阅包含新用户优惠,请查看 `product.subscription?.offer?.phases` 属性。这是一个最多包含两个折扣阶段的列表:免费试用阶段和新用户优惠价格阶段。每个阶段对象包含以下实用属性:可选
默认值:`en`
|[付费墙本地化](add-remote-config-locale)的标识符。该参数应为由一个或多个子标签组成的语言代码,子标签之间用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言代码及推荐使用方式的更多信息,请参阅[本地化与语言代码](react-native-localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,若请求失败则返回缓存数据。我们推荐使用此方式,因为它能确保用户始终获取最新数据。
但如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`——在缓存存在时直接返回缓存数据。这样用户获取的数据可能不是最新的,但无论网络状况如何,都能体验到更快的加载速度。缓存会定期更新,因此在会话期间使用缓存来减少网络请求是安全的。
请注意,缓存在应用重启后仍会保留,只有在重新安装应用或手动清除时才会被清空。
|请求成功后,响应中会包含此对象。[AdaptyProfile](https://react-native.adapty.io/interfaces/adaptyprofile) 对象提供了用户在应用内的访问等级、订阅及一次性购买的完整信息。
请检查访问等级状态,以确认用户是否拥有所需的应用访问权限。
| :::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()` 方法并传入额外参数: ```typescript showLineNumbers try { const purchaseResult = await adapty.makePurchase(product, params); switch (purchaseResult.type) { case 'success': const isSubscribed = purchaseResult.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // 授予付费功能访问权限 } break; case 'user_cancelled': // 处理用户取消购买的情况 break; case 'pending': // 处理延迟购买(例如,用户将以现金线下支付) break; } } catch (error) { // 处理错误 } ``` 额外请求参数: | 参数 | 是否必填 | 描述 | | :--------- | :------- | :----------------------------------------------------------- | | **params** | 必填 | [`MakePurchaseParamsInput`](https://react-native.adapty.io/types/makepurchaseparamsinput) 类型的对象。 | :::info **3.8.2+ 版本**:`MakePurchaseParamsInput` 结构已更新。`oldSubVendorProductId` 和 `prorationMode` 现已嵌套在 `subscriptionUpdateParams` 下,`isOfferPersonalized` 已移至上层。 ```javascript makePurchase(product, { android: { subscriptionUpdateParams: { oldSubVendorProductId: 'old_product_id', prorationMode: 'charge_prorated_price' }, isOfferPersonalized: true } }); ``` ::: 如需了解更多关于订阅和替换模式的内容,请参阅 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\}一个 [`AdaptyProfile`](https://react-native.adapty.io/interfaces/adaptyprofile) 对象。该模型包含访问等级、订阅和非订阅购买的相关信息。
检查**访问等级状态**以确定用户是否有权访问应用。
| :::tip 想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的[示例应用](sample-apps),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。 ::: --- # File: implement-observer-mode-react-native --- --- title: "在 React Native SDK 中实现观察者模式" description: "在 Adapty 中实现观察者模式,以跟踪 React Native SDK 中的用户订阅事件。" --- 如果您已有自己的购买基础设施,暂时不打算完全切换到 Adapty,可以了解[观察者模式](observer-vs-full-mode)。在基本形式下,观察者模式提供高级分析功能,并与归因和分析系统无缝集成。 如果这满足您的需求,您只需: 1. 在配置 Adapty SDK 时将 `observerMode` 参数设置为 `true` 来开启观察者模式。请参阅 [React Native](sdk-installation-reactnative) 的设置说明。 2. 从您现有的购买基础设施向 Adapty [上报交易](report-transactions-observer-mode-react-native)。 ### 观察者模式设置 \{#observer-mode-setup\} 如果您自行处理购买和订阅状态,仅使用 Adapty 发送订阅事件和分析数据,请开启观察者模式。 :::important 在观察者模式下运行时,Adapty SDK 不会关闭任何交易,请确保您自行处理这一操作。 ::: ```typescript showLineNumbers title="App.tsx" adapty.activate('YOUR_PUBLIC_SDK_KEY', { observerMode: true, // Enable observer mode }); ``` 参数: | 参数 | 描述 | | --------------------------- | ------------------------------------------------------------ | | observerMode | 一个布尔值,用于控制[观察者模式](observer-vs-full-mode)。默认值为 `false`。 | ## 在观察者模式下使用 Adapty 付费墙 \{#using-adapty-paywalls-in-observer-mode\} 如果您还希望使用 Adapty 的付费墙和 A/B 测试功能,也是可以的——但在观察者模式下需要一些额外的设置。除了上述步骤之外,您还需要: 1. 按照[远程配置付费墙](present-remote-config-paywalls-react-native)的常规方式展示付费墙。 3. 将付费墙与购买交易[关联](report-transactions-observer-mode-react-native)。 --- # File: report-transactions-observer-mode-react-native --- --- title: "在 React Native SDK 的 Observer Mode 中上报交易" description: "在 React Native SDK 中通过 Adapty Observer Mode 上报购买交易,用于用户洞察和收入追踪。" ---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://react-native.adapty.io/interfaces/adaptypaywall) 对象的 `variationId` 属性获取。 |phoneNumber
firstName
lastName
| String | | gender | 枚举类型,允许的值为:`female`、`male`、`other` | | birthday | Date | ### 自定义用户属性 \{#custom-user-attributes\} 您可以设置自定义属性。这些属性通常与应用使用情况相关。例如,对于健身应用,可能是每周锻炼次数;对于语言学习应用,可能是用户的知识水平等。您可以在市场细分中使用它们来创建有针对性的付费墙和优惠,也可以在分析中使用它们来了解哪些产品指标对收入影响最大。 ```typescript showLineNumbers try { await adapty.updateProfile({ codableCustomAttributes: { key_1: 'value_1', key_2: 2, }, }); } catch (error) { // handle `AdaptyError` } ``` 要删除已有的键,请使用 `.withRemoved(customAttributeForKey:)` 方法: ```typescript showLineNumbers try { // to remove a key, pass null as its value await adapty.updateProfile({ codableCustomAttributes: { key_1: null, key_2: null, }, }); } catch (error) { // handle `AdaptyError` } ``` 有时您需要查看之前已设置了哪些自定义属性。为此,请使用 `AdaptyProfile` 对象的 `customAttributes` 字段。 :::warning 请注意,`customAttributes` 的值可能不是最新的,因为用户属性可以随时从不同设备发送,服务器上的属性可能在上次同步后已发生变化。 ::: ### 限制 \{#limits\} - 每位用户最多 30 个自定义属性 - 键名最长 30 个字符。键名可包含字母数字字符及以下任意字符:`_` `-` `.` - 值可以是字符串或浮点数,最多 50 个字符。 --- # File: react-native-listen-subscription-changes --- --- title: "在 React Native SDK 中检查订阅状态" description: "在 Adapty 中跟踪和管理用户订阅状态,提升 React Native 应用的用户留存率。" --- 借助 Adapty,订阅状态的跟踪变得轻而易举。您无需在代码中手动插入产品 ID,只需检查用户是否拥有有效的[访问等级](access-level),即可轻松确认其订阅状态。[AdaptyProfile](https://react-native.adapty.io/interfaces/adaptyprofile) 对象。通常情况下,您只需检查用户画像的访问等级状态,即可判断用户是否拥有应用的高级访问权限。
`.getProfile` 方法始终尝试查询 API,因此返回的结果是最新数据。如果由于某些原因(例如无网络连接)导致 Adapty SDK 无法从服务器获取信息,则会返回缓存中的数据。值得注意的是,Adapty SDK 会定期更新 `AdaptyProfile` 缓存,以尽可能保持信息的实时性。
| `.getProfile()` 方法会返回用户画像,您可以从中获取访问等级状态。每个应用可以设置多个访问等级。例如,如果您运营一款新闻应用,并针对不同主题单独销售订阅,可以创建"sports"和"science"等访问等级。但在大多数情况下,您只需要一个访问等级,此时可以直接使用默认的"premium"访问等级。 以下是检查默认"premium"访问等级的示例: ```typescript showLineNumbers try { const profile = await adapty.getProfile(); const isActive = profile.accessLevels?.["premium"]?.isActive; if (isActive) { // grant access to premium features } } catch (error) { // handle the error } ``` ### 监听订阅状态更新 \{#listening-for-subscription-status-updates\} 每当用户的订阅发生变化时,Adapty 都会触发一个事件。 要接收来自 Adapty 的消息,您需要进行一些额外配置: ```typescript showLineNumbers // Create an "onLatestProfileLoad" event listener adapty.addEventListener('onLatestProfileLoad', profile => { // handle any changes to subscription state }); ``` Adapty 也会在应用启动时触发一次事件,此时传递的是缓存中的订阅状态。 ### 订阅状态缓存 \{#subscription-status-cache\} Adapty SDK 实现的缓存机制会存储用户画像的订阅状态。这意味着即使服务器不可用,也可以通过缓存数据获取用户画像的订阅状态信息。 但需要注意的是,无法直接从缓存中请求数据。SDK 会每分钟定期向服务器发起查询,检查用户画像是否有任何更新或变更。如果存在修改(例如新的交易记录或其他更新),这些变更将同步写入缓存数据,以确保缓存与服务器保持一致。 --- # File: react-native-deal-with-att --- --- title: "在 React Native SDK 中处理 ATT" description: "在 React Native 上开始使用 Adapty,以简化订阅设置和管理。" --- 如果您的应用程序使用了 AppTrackingTransparency 框架并向用户呈现应用追踪授权请求,则您应将[授权状态](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/)发送给 Adapty。 ```typescript showLineNumbers try { await adapty.updateProfile({ // you can also pass a string value (validated via tsc) if you prefer appTrackingTransparencyStatus: AppTrackingTransparencyStatus.Authorized, }); } catch (error) { // handle `AdaptyError` } ``` :::warning 我们强烈建议您在该值发生变化时尽早发送此值,只有这样,数据才能及时发送到您已配置的集成渠道。 ::: --- # File: kids-mode-react-native --- --- title: "React Native SDK 中的儿童模式" description: "轻松启用儿童模式,符合 Apple 和 Google 政策。React Native SDK 不会收集 IDFA、GAID 或广告数据。" --- 如果你的 React Native 应用面向儿童,则必须遵守 [Apple](https://developer.apple.com/kids/) 和 [Google](https://support.google.com/googleplay/android-developer/answer/9893335) 的相关政策。如果你正在使用 Adapty SDK,只需几个简单的步骤即可将其配置为符合这些政策,并顺利通过应用商店审核。 :::important 在 iOS 上,Kids Mode 通过 `KidsMode` Swift package trait 启用,该 trait 会在编译时移除所有 IDFA、AdSupport 和 AppTrackingTransparency 相关代码。此功能需要 v4 SDK(通过 Swift Package Manager 安装原生 iOS SDK)以及 **Xcode 26** 或更高版本。详见下方的[更新 iOS Podfile](#updates-in-your-ios-podfile)。 ::: ## 需要做什么?\{#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) 此外,我们建议谨慎使用 customer user ID。格式为 `可选
默认值:`en`
|用户引导本地化的标识符。该参数应为语言代码,由一个或两个子标签组成,用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及其推荐用法的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此选项,因为它确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能获取不到最新数据,但无论网络状况如何,都能获得更快的加载速度。缓存会定期更新,因此在会话期间使用它来避免网络请求是安全的。
请注意,缓存在重启应用后依然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存和备用用户引导。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可访问时提供独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不稳定的情况下也能保证可靠性。
| | **loadTimeoutMs** | 默认值:5 秒 |该值限制此方法的超时时间。如果达到超时时间,将返回缓存数据或本地备用内容。
请注意,在极少数情况下,此方法的超时时间可能比 `loadTimeout` 中指定的时间稍长,因为该操作在底层可能由多个不同的请求组成。
| 响应参数: | 参数 | 描述 | |:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | Onboarding | 一个 [`AdaptyOnboarding`](https://react-native.adapty.io/interfaces/adaptyonboarding) 对象,包含:用户引导标识符和配置、远程配置以及其他若干属性。 | ## 使用默认目标受众用户引导加速获取 \{#speed-up-onboarding-fetching-with-default-audience-onboarding\} 通常情况下,用户引导的获取几乎是即时完成的,无需担心加速问题。但是,当您拥有大量目标受众和用户引导,且用户网络连接较弱时,获取用户引导的时间可能比预期更长。在这种情况下,您可能希望显示默认的用户引导,以确保流畅的用户体验,而不是不显示任何内容。 为解决这一问题,您可以使用 `getOnboardingForDefaultAudience` 方法,该方法会获取指定版位中**所有用户**目标受众的用户引导。但请务必了解,推荐的方式是使用 `getOnboarding` 方法获取用户引导,详见上方[获取用户引导](#fetch-onboarding)部分。 :::warning 建议使用 `getOnboarding` 而非 `getOnboardingForDefaultAudience`,因为后者存在以下重要限制: - **兼容性问题**:在支持多个应用版本时可能产生问题,需要向后兼容的设计,否则旧版本可能显示异常。 - **无个性化**:仅显示"所有用户"目标受众的内容,无法基于国家、归因或自定义属性进行定向。 如果对您的使用场景而言更快的获取速度超过了上述缺点,请按如下所示使用 `getOnboardingForDefaultAudience`。否则,请按[上述](#fetch-onboarding)方式使用 `getOnboarding`。 ::: ```typescript showLineNumbers try { const placementId = 'YOUR_PLACEMENT_ID'; const locale = 'en'; const onboarding = await adapty.getOnboardingForDefaultAudience(placementId, locale); // the requested onboarding } catch (error) { // handle the error } ``` 参数: | 参数 | 是否必填 | 描述 | |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **placementId** | 必填 | 所需[版位](placements)的标识符。这是您在 Adapty 看板中创建版位时指定的值。 | | **locale** |可选
默认值:`en`
|用户引导本地化的标识符。该参数应为语言代码,由一个或两个子标签组成,用减号(**-**)分隔。第一个子标签表示语言,第二个子标签表示地区。
示例:`en` 表示英语,`pt-br` 表示巴西葡萄牙语。
有关语言区域代码及其推荐用法的更多信息,请参阅[本地化与语言区域代码](localizations-and-locale-codes)。
| | **fetchPolicy** | 默认值:`.reloadRevalidatingCacheData` |默认情况下,SDK 会尝试从服务器加载数据,如果失败则返回缓存数据。我们推荐此选项,因为它确保用户始终获得最新数据。
但是,如果您认为用户的网络连接不稳定,可以考虑使用 `.returnCacheDataElseLoad`,在缓存数据存在时直接返回缓存。在这种情况下,用户可能获取不到最新数据,但无论网络状况如何,都能获得更快的加载速度。缓存会定期更新,因此在会话期间使用它来避免网络请求是安全的。
请注意,缓存在重启应用后依然保留,只有在重新安装应用或手动清理时才会被清除。
Adapty SDK 在本地以两层方式存储用户引导:上述定期更新的缓存和备用用户引导。我们还使用 CDN 加速用户引导的获取,并在 CDN 不可访问时提供独立的备用服务器。该系统旨在确保您始终获得最新版本的用户引导,同时在网络连接不稳定的情况下也能保证可靠性。
| --- # File: react-native-present-onboardings --- --- title: "在 React Native SDK 中展示用户引导" description: "了解如何在 React Native 中展示用户引导,以提升转化率和收入。" --- :::warning **用户引导功能在 SDK v4 中已弃用,将在未来版本中移除。** 该功能不再接受修复或改进。请改用 [flows](react-native-get-pb-paywalls):与在 WebView 中运行的用户引导不同,flows 直接在设备上进行原生渲染——带来更流畅的动画、一致的原生外观体验、更快的加载速度,以及无需 WebView 运行时依赖。请参阅 [获取 flows 和付费墙](react-native-get-pb-paywalls) 和 [展示 flows 和付费墙](react-native-present-paywalls) 以开始使用。 ::: 如果你使用编辑工具自定义了用户引导,就无需在移动端代码中手动处理渲染逻辑来向用户展示它。这类用户引导已经包含了展示内容和展示方式的完整配置。 开始之前,请确认: 1. 已安装 [Adapty React Native SDK](sdk-installation-reactnative) 3.8.0 或更高版本。 2. 已[创建用户引导](create-onboarding)。 3. 已将用户引导添加到[版位](placements)。 Adapty React Native SDK 提供两种展示用户引导的方式: - **React 组件**:嵌入式组件,可将其集成到应用的架构和导航系统中。 - **模态呈现** ## React 组件 \{#react-component\} 如需将用户引导嵌入现有组件树,可在 React Native 组件层级中直接使用 `AdaptyOnboardingView` 组件。嵌入式组件让你能够将其集成到应用的架构和导航系统中。 :::note 在 Android 上,我们建议对 `AdaptyOnboardingView` 进行额外配置,以避免视觉渲染异常。详见[系统界面遮挡 Android 用户引导内容](#system-ui-overlaps-onboarding-content-on-android)。 :::
然后,你可以在代码中使用这个 ID,并将其作为自定义操作来处理。例如,当用户点击自定义按钮(如 **Login** 或 **Allow notifications**)时,事件处理程序会被触发,并附带与编辑工具中 **Action ID** 对应的 `actionId` 参数。你可以自定义 ID,比如 "allowNotifications"。
:::important
请注意,当用户关闭用户引导时,你需要自行处理后续逻辑,例如停止显示用户引导界面。
:::
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** 部分,确认所有所需的国家和地区均已列出。
## 第四步:检查产品价格 \{#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` 警告,并使您的产品在商店中正常上线。
## 第六步:如果产品卡住了,尝试删除重建 \{#step-6-recreate-the-product-if-its-stuck\}
前五步可能全部通过——状态为 `Approved`、Bundle ID 匹配、API Key 有效——但 SDK 仍然返回 `1000 noProductIDsFound`。这种情况下,产品可能卡在了 Apple 的注册表中。Apple 的产品注册表偶尔会出现这样的状态:产品在 App Store Connect 的界面中存在,但无法通过 StoreKit 的查找路径访问到。
在 App Store Connect 中删除该产品,然后用相同的产品 ID 重新创建。重新创建后,最多等待 24 小时以完成数据同步。
---
# File: cantMakePayments-react-native
---
---
title: "修复 React Native 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-react-native-sdk-v4
---
---
title: "将 Adapty React Native SDK 迁移至 v. 4.0"
description: "通过将付费墙 API 替换为 flow API,迁移至 Adapty React Native SDK v4.0(测试版),兼容 Flow Builder 和 付费墙编辑工具。"
---
Adapty React Native SDK 4.0(测试版)引入了 flow 功能,并相应地重命名了付费墙 API。新 API 同时兼容全新的 Flow Builder 和现有的付费墙编辑工具——无需在 Adapty 看板端进行任何配置变更。
## 快速参考 \{#quick-reference\}
| v3 | v4 |
|---|---|
| `adapty.getPaywall(placementId, locale?, params?)` | `adapty.getFlow(placementId, params?)` |
| `adapty.getPaywallForDefaultAudience(placementId, locale?, params?)` | `adapty.getFlowForDefaultAudience(placementId, params?)` |
| `adapty.getPaywallProducts(paywall)` | `adapty.getPaywallProducts(flow)` |
| `adapty.logShowPaywall(paywall)` | `adapty.logShowFlow(flow)` |
| `AdaptyPaywall`(类型) | `AdaptyFlow` |
| `createPaywallView(paywall)` | `createFlowView(flow)` |
| `AdaptyPaywallView`(组件) | `AdaptyFlowView` |
| `EventHandlers`(类型) | `FlowEventHandlers` |
| `onPaywallShown` | `onAppeared` |
| `onPaywallClosed` | `onDisappeared` |
| `onRenderingFailed` | `onError` |
`AdaptyPaywallProduct` 保持原有命名——产品仍归属于流程,`getPaywallProducts` 现在接收 `AdaptyFlow` 参数。`getFlow` 和 `getFlowForDefaultAudience` 方法不再接受 `locale` 参数。视图方法 `present`、`dismiss`、`setEventHandlers`、`showDialog`,以及事件处理器 `onCloseButtonPress`、`onUrlPress`、`onCustomAction`、`onProductSelected`、`onPurchaseStarted`、`onPurchaseCompleted`、`onPurchaseFailed`、`onRestoreStarted`、`onRestoreCompleted`、`onRestoreFailed`、`onLoadingProductsFailed`、`onWebPaymentNavigationFinished` 和 `onAndroidSystemBack` 均与 v3 保持相同命名。部分默认行为有所变更——详见[默认行为变更](#default-behavior-changes)。
## 最低 iOS 版本 \{#minimum-ios-version\}
Adapty React Native SDK 4.0 将最低 iOS 部署目标从 iOS 13.0 提升至 **iOS 15.0**。升级前,请将您的 iOS 部署目标设置为 15.0 或更高版本。
## 安装 \{#installation\}
### 更新软件包 \{#update-the-package\}
v4.0 为预发布版本,请固定精确版本号——npm 不会通过 caret/tilde 范围选取预发布版本:
```bash showLineNumbers
npm install react-native-adapty@4.0.0
# or
yarn add react-native-adapty@4.0.0
```
### iOS:原生 SDK 现在通过 Swift Package Manager 分发 \{#ios-native-sdks-now-come-through-swift-package-manager\}
[CocoaPods 的 spec 仓库将于 2026 年 12 月变为只读](https://blog.cocoapods.org/CocoaPods-Specs-Repo/),因此从 v4 开始,原生的 `Adapty`、`AdaptyUI` 和 `AdaptyPlugin` SDK **不再作为 CocoaPods 子依赖项引入** —— podspec 改为通过 **Swift Package Manager**(借助 `spm_dependency` 帮助函数)来拉取它们。这需要满足以下两个条件:
- **React Native 0.75 或更高版本** — 需要 `spm_dependency` podspec 辅助函数。在旧版本上,`pod install` 会报明确错误;请先升级 React Native,或继续使用 `react-native-adapty` 3.x。
- **动态框架** — SPM 依赖项需要动态链接。启用方式因 Expo 和裸 React Native 而有所不同。
#### Expo
添加 [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/) 配置插件,并在 `app.json`(或 `app.config.js`)中将 iOS 框架设置为动态:
```json showLineNumbers title="app.json"
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": {
"useFrameworks": "dynamic"
}
}
]
]
}
}
```
然后安装插件并重新生成原生项目:
```bash showLineNumbers
npx expo install expo-build-properties
npx expo prebuild --clean
```
#### Bare React Native
在你的 iOS target 中添加动态框架,然后重新安装 pods:
```ruby showLineNumbers title="ios/Podfile"
use_frameworks! :linkage => :dynamic
```
```bash showLineNumbers
cd ios && pod install --repo-update
```
如果你之前通过 CocoaPods 子依赖的方式引入了 `Adapty`、`AdaptyUI` 或 `AdaptyPlugin`,请先从 `Podfile` 中删除所有显式的 `pod 'Adapty'`、`pod 'AdaptyUI'` 或 `pod 'AdaptyPlugin'` 行。
:::warning
从默认静态链接切换到动态框架可能与尚不支持模块化头文件的库产生冲突,且与 Flipper 不兼容。如果遇到构建问题,请参阅这篇[关于将 Swift Package Manager 与 React Native 库集成的文章](https://www.callstack.com/blog/integrating-swift-package-manager-with-react-native-libraries)。
:::
完整的安装步骤,请参阅[安装 Adapty SDK](sdk-installation-reactnative)。
## 获取流程 \{#fetching-flows\}
### getPaywall → getFlow
返回类型从 `AdaptyPaywall` 变更为 `AdaptyFlow`,同时移除了 `locale` 参数——渲染 flow 时,语言环境会自动解析;对于自定义付费墙,所有语言环境均通过 `flow.remoteConfigs` 返回:
```diff showLineNumbers
- const paywall = await adapty.getPaywall('YOUR_PLACEMENT_ID', 'en');
+ const flow = await adapty.getFlow('YOUR_PLACEMENT_ID');
```
`getPaywallForDefaultAudience` 也以同样的方式重命名:
```diff showLineNumbers
- const paywall = await adapty.getPaywallForDefaultAudience('YOUR_PLACEMENT_ID', 'en');
+ const flow = await adapty.getFlowForDefaultAudience('YOUR_PLACEMENT_ID');
```
### getPaywallProducts(paywall) → getPaywallProducts(flow)
`getPaywallProducts` 保持名称不变,但现在接受 `AdaptyFlow`:
```diff showLineNumbers
- const products = await adapty.getPaywallProducts(paywall);
+ const products = await adapty.getPaywallProducts(flow);
```
## 数据模型 \{#data-model\}
`getFlow` 返回的是 `AdaptyFlow` 而非 `AdaptyPaywall`,且对象结构有所变化:
| v3 `AdaptyPaywall` 字段 | v4 `AdaptyFlow` 字段 | 操作 |
|---|---|---|
| `remoteConfig?`(单个) | `remoteConfigs?: AdaptyRemoteConfig[]`(数组) | 一个流程为每种已配置的语言各携带一份远程配置。读取与用户匹配的那份:`flow.remoteConfigs?.find((c) => c.lang === 'en')`。 |
| `products` | `flow.paywalls[i].productIdentifiers` | 产品标识符现在位于每个流程变体上,而非流程本身。 |
| `webPurchaseUrl?` | `flow.paywalls[i].webPurchaseUrl` | 从流程移至每个付费墙变体。 |
| `version?: number` | `flowVersionId?: string` | 已重命名,类型从 `number` 改为 `string`。 |
| `hasViewConfiguration` | 已移除 | 从代码中删除所有 `hasViewConfiguration` 检查。 |
| `requestLocale` | 已移除 | 语言区域不再是模型的一部分。 |
| _(新增)_ | `paywalls: AdaptyFlowPaywall[]` | 每个条目代表流程中的一个付费墙变体。 |
| _(新增)_ | `responseCreatedAt: number` | 服务器响应时间戳,单位为毫秒。 |
产品标识符已从流程移至每个实验变体:
```diff showLineNumbers
- const ids = paywall.products;
+ const ids = flow.paywalls[0].productIdentifiers;
```
## Web 付费墙方法 \{#web-paywall-methods\}
`openWebPaywall` 和 `createWebPaywallUrl` 方法名保持不变,但第一个参数现在是 `AdaptyFlowPaywall`(流程变体),而非 `AdaptyPaywall`。你仍然可以传入 `AdaptyPaywallProduct`。
```diff showLineNumbers
const flow = await adapty.getFlow('YOUR_PLACEMENT_ID');
- await adapty.openWebPaywall(paywall);
+ await adapty.openWebPaywall(flow.paywalls[0]);
```
## 追踪流程查看次数 \{#tracking-flow-views\}
### logShowPaywall → logShowFlow
`logShowPaywall` 已重命名为 `logShowFlow`,现在接收一个 `AdaptyFlow` 参数。事件仍会记录到同一实验变体,因此现有的漏斗和 A/B 测试数据图表无需修改看板即可继续正常使用。
```diff showLineNumbers
- await adapty.logShowPaywall(paywall);
+ await adapty.logShowFlow(flow);
```
与 v3 相同,当使用 [Flow Builder](adapty-flow-builder) 或[付费墙编辑工具](adapty-paywall-builder)渲染流程或付费墙时,无需手动调用此方法——Adapty 会自动追踪这些页面的展示。
## 展示流程 \{#displaying-flows\}
### createPaywallView → createFlowView
重命名工厂函数并传入 `AdaptyFlow`。返回的控制器方法(`present`、`dismiss`、`setEventHandlers`、`showDialog`)保持不变:
```diff showLineNumbers
- import { createPaywallView } from 'react-native-adapty';
+ import { createFlowView } from 'react-native-adapty';
- const view = await createPaywallView(paywall);
+ const view = await createFlowView(flow);
await view.present();
```
### AdaptyPaywallView → AdaptyFlowView
如果你使用 React 组件渲染,请将其重命名并传入 `flow` prop:
```diff showLineNumbers
- import { AdaptyPaywallView } from 'react-native-adapty';
+ import { AdaptyFlowView } from 'react-native-adapty';
- ```diff showLineNumbers - subscriptionDetails?: AdaptySubscriptionDetails; + subscription?: AdaptySubscriptionDetails; ``` 2. [AdaptySubscriptionDetails](https://react-native.adapty.io/interfaces/adaptysubscriptiondetails): - `promotionalOffer` 已移除。现在促销活动仅在可用时通过 `offer` 属性提供。此时 `offer?.identifier?.type` 的值为 `'promotional'`。 - `introductoryOfferEligibility` 已移除(优惠仅在用户符合条件时才会返回)。 - `offerId` 已移除。优惠 ID 现在存储在 `AdaptySubscriptionOffer.identifier` 中。 - `offerTags` 已移至 `AdaptySubscriptionOffer.android`。
3. [AdaptyDiscountPhase](https://react-native.adapty.io/interfaces/adaptydiscountphase): - `AdaptyDiscountPhase` 模型中移除了 `identifier` 字段。优惠标识符现在存储在 `AdaptySubscriptionOffer.identifier` 中。
```diff showLineNumbers - ios?: { - readonly identifier?: string; - }; ``` ### 已移除的模型 \{#remove-models\} 1. `AttributionSource`: - 在之前使用 `AttributionSource` 的地方,现在直接使用字符串。 2. `OfferEligibility`: - 该模型已被移除,因为它不再需要。现在,仅当用户符合资格时才返回优惠。 ## 移除 `getProductsIntroductoryOfferEligibility` 方法 \{#remove-getproductsintroductoryoffereligibility-method\} 在 Adapty SDK 3.3.1 之前,产品对象始终包含优惠,即使用户不符合资格也是如此。这需要您在使用优惠之前手动检查资格。 从 3.3.1 版本起,产品对象仅在用户符合资格时才包含优惠。这简化了流程,因为只要存在优惠,即可认为用户符合资格。 ## 更新购买流程 \{#update-making-purchase\} 在早期版本中,已取消和待处理的购买会被视为错误,分别返回代码 `2: 'paymentCancelled'` 和 `25: 'pendingPurchase'`。 从版本 3.3.1 开始,已取消和待处理的购买现在被视为成功结果,应按相应方式处理: ```typescript showLineNumbers try { const purchaseResult = await adapty.makePurchase(product); switch (purchaseResult.type) { case 'success': const isSubscribed = purchaseResult.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // Grant access to the paid features } break; case 'user_cancelled': // Handle the case where the user canceled the purchase break; case 'pending': // Handle deferred purchases (e.g., the user will pay offline with cash) break; } } catch (error) { // Handle the error } ``` ## 更新付费墙编辑工具付费墙的展示方式 \{#update-paywall-builder-paywall-presentation\} 有关更新后的示例,请参阅[在 React Native 中展示新版付费墙编辑工具付费墙](react-native-present-paywalls)文档。 ```diff showLineNumbers - import { createPaywallView } from '@adapty/react-native-ui'; + import { createPaywallView } from 'react-native-adapty/dist/ui'; const view = await createPaywallView(paywall); view.registerEventHandlers(); // handle close press, etc try { await view.present(); } catch (error) { // handle the error } ``` ## 更新开发者自定义计时器的实现方式 \{#update-developer-defined-timer-implementation\} 将 `timerInfo` 参数重命名为 `customTimers`: ```diff showLineNumbers - let timerInfo = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) } + let customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) } //and then you can pass it to createPaywallView as follows: - view = await createPaywallView(paywall, { timerInfo }) + view = await createPaywallView(paywall, { customTimers }) ``` ## 修改付费墙编辑工具的购买事件 \{#modify-paywall-builder-purchase-events\} 之前: - 取消购买会触发 `onPurchaseCancelled` 回调。 - 待处理的购买会返回错误码 `25: 'pendingPurchase'`。 现在: - 两者均由 `onPurchaseCompleted` 回调处理。 #### 迁移步骤: \{#steps-to-migrate\} 1. 移除 `onPurchaseCancelled` 回调。 2. 移除对错误码 `25: 'pendingPurchase'` 的处理逻辑。 3. 更新 `onPurchaseCompleted` 回调: ```typescript showLineNumbers const view = await createPaywallView(paywall); const unsubscribe = view.registerEventHandlers({ // ... other optional callbacks onPurchaseCompleted(purchaseResult, product) { switch (purchaseResult.type) { case 'success': const isSubscribed = purchaseResult.profile?.accessLevels['YOUR_ACCESS_LEVEL']?.isActive; if (isSubscribed) { // Grant access to the paid features } break; // highlight-start case 'user_cancelled': // Handle the case where the user canceled the purchase break; case 'pending': // Handle deferred purchases (e.g., the user will pay offline with cash) break; // highlight-end } // highlight-start return purchaseResult.type !== 'user_cancelled'; // highlight-end }, }); ``` ## 修改付费墙编辑工具自定义操作事件 \{#modify-paywall-builder-custom-action-events\} 已移除的回调: - `onAction` - `onCustomEvent` 新增的回调: - 新增 `onCustomAction(actionId)` 回调,用于处理自定义操作。 ## 修改 `onProductSelected` 回调 \{#modify-onproductselected-callback\} 之前,`onProductSelected` 需要传入 `product` 对象。现在改为接受字符串类型的 `productId`。 ## 从 `updateProfile` 方法中移除第三方集成参数 \{#remove-third-party-integration-parameters-from-updateprofile-method\} 第三方集成标识符现在通过 `setIntegrationIdentifier` 方法进行设置。`updateProfile` 方法不再接受这些参数。 ## 更新第三方集成 SDK 配置 \{#update-third-party-integration-sdk-configuration\} 为确保集成能够与 Adapty React Native SDK 3.3.1 及更高版本正常工作,请按照以下各节的说明更新您的 SDK 配置。 此外,如果您之前使用 `AttributionSource` 获取归因标识符,请将代码修改为以字符串形式提供所需标识符。 ### Adjust 按照以下说明更新您的移动应用代码。完整代码示例请参阅 [Adjust 集成的 SDK 配置](adjust#connect-your-app-to-adjust)。 ```diff showLineNumbers import { Adjust, AdjustConfig } from "react-native-adjust"; import { adapty } from "react-native-adapty"; var adjustConfig = new AdjustConfig(appToken, environment); // Before submiting Adjust config... adjustConfig.setAttributionCallbackListener(attribution => { // Make sure Adapty SDK is activated at this point // You may want to lock this thread awaiting of `activate` adapty.updateAttribution(attribution, "adjust"); }); // ... Adjust.create(adjustConfig); + Adjust.getAdid((adid) => { + if (adid) + adapty.setIntegrationIdentifier("adjust_device_id", adid); + }); ``` ### AirBridge \{#airbridge\} 按如下方式更新您的移动应用代码。完整代码示例请参阅 [AirBridge 集成的 SDK 配置](airbridge#connect-your-app-to-airbridge)。 ```diff showLineNumbers import Airbridge from 'airbridge-react-native-sdk'; import { adapty } from 'react-native-adapty'; try { const deviceId = await Airbridge.state.deviceUUID(); - await adapty.updateProfile({ - airbridgeDeviceId: deviceId, - }); + await adapty.setIntegrationIdentifier("airbridge_device_id", deviceId); } catch (error) { // handle `AdaptyError` } ``` ### Amplitude \{#amplitude\} 按如下方式更新您的移动应用代码。完整代码示例请参阅 [Amplitude 集成的 SDK 配置](amplitude#sdk-configuration)。 ```diff showLineNumbers import { adapty } from 'react-native-adapty'; try { - await adapty.updateProfile({ - amplitudeDeviceId: deviceId, - amplitudeUserId: userId, - }); + await adapty.setIntegrationIdentifier("amplitude_device_id", deviceId); + await adapty.setIntegrationIdentifier("amplitude_user_id", userId); } catch (error) { // handle `AdaptyError` } ``` ### AppMetrica 按照以下示例更新您的移动应用代码。完整代码示例请参阅 [AppMetrica 集成的 SDK 配置](appmetrica#sdk-configuration)。 ```diff showLineNumbers import { adapty } from 'react-native-adapty'; import AppMetrica, { DEVICE_ID_KEY, StartupParams, StartupParamsReason } from '@appmetrica/react-native-analytics'; // ... const startupParamsCallback = async ( params?: StartupParams, reason?: StartupParamsReason ) => { const deviceId = params?.deviceId if (deviceId) { try { - await adapty.updateProfile({ - appmetricaProfileId: 'YOUR_ADAPTY_CUSTOMER_USER_ID', - appmetricaDeviceId: deviceId, - }); + await adapty.setIntegrationIdentifier("appmetrica_profile_id", 'YOUR_ADAPTY_CUSTOMER_USER_ID'); + await adapty.setIntegrationIdentifier("appmetrica_device_id", deviceId); } catch (error) { // handle `AdaptyError` } } } AppMetrica.requestStartupParams(startupParamsCallback, [DEVICE_ID_KEY]) ``` ### AppsFlyer 按照以下示例更新你的移动应用代码。完整代码示例请参阅 [AppsFlyer 集成的 SDK 配置](appsflyer#connect-your-app-to-appsflyer)。 ```diff showLineNumbers import { adapty, AttributionSource } from 'react-native-adapty'; import appsFlyer from 'react-native-appsflyer'; appsFlyer.onInstallConversionData(installData => { try { - const networkUserId = appsFlyer.getAppsFlyerUID(); - adapty.updateAttribution(installData, AttributionSource.AppsFlyer, networkUserId); + const uid = appsFlyer.getAppsFlyerUID(); + adapty.setIntegrationIdentifier("appsflyer_id", uid); + adapty.updateAttribution(installData, "appsflyer"); } catch (error) { // handle the error } }); // ... appsFlyer.initSdk(/*...*/); ``` ### Branch \{#branch\} 按如下方式更新您的移动应用代码。完整代码示例请参阅 [Branch 集成的 SDK 配置](branch#connect-your-app-to-branch)。 ```diff showLineNumbers import { adapty, AttributionSource } from 'react-native-adapty'; import branch from 'react-native-branch'; branch.subscribe({ enComplete: ({ params, }) => { - adapty.updateAttribution(params, AttributionSource.Branch); + adapty.updateAttribution(params, "branch"); }, }); ``` ### Facebook Ads 按照以下方式更新您的移动应用代码。完整代码示例请参阅 [Facebook Ads 集成的 SDK 配置](facebook-ads#connect-your-app-to-facebook-ads)。 ```diff showLineNumbers import { adapty } from 'react-native-adapty'; import { AppEventsLogger } from 'react-native-fbsdk-next'; try { const anonymousId = await AppEventsLogger.getAnonymousID(); - await adapty.updateProfile({ - facebookAnonymousId: anonymousId, - }); + await adapty.setIntegrationIdentifier("facebook_anonymous_id", anonymousId); } catch (error) { // handle `AdaptyError` } ``` ### Firebase 与 Google Analytics \{#firebase-and-google-analytics\} 按以下方式更新您的移动应用代码。完整代码示例请参阅 [Firebase 与 Google Analytics 集成的 SDK 配置](firebase-and-google-analytics)。 ```diff showLineNumbers import analytics from '@react-native-firebase/analytics'; import { adapty } from 'react-native-adapty'; try { const appInstanceId = await analytics().getAppInstanceId(); - await adapty.updateProfile({ - firebaseAppInstanceId: appInstanceId, - }); + await adapty.setIntegrationIdentifier("firebase_app_instance_id", appInstanceId); } catch (error) { // handle `AdaptyError` } ``` ### Mixpanel \{#mixpanel\} 按如下方式更新您的移动应用代码。完整代码示例请参阅 [Mixpanel 集成的 SDK 配置](mixpanel#sdk-configuration)。 ```diff showLineNumbers import { adapty } from 'react-native-adapty'; import { Mixpanel } from 'mixpanel-react-native'; // ... try { - await adapty.updateProfile({ - mixpanelUserId: mixpanelUserId, - }); + await adapty.setIntegrationIdentifier("mixpanel_user_id", mixpanelUserId); } catch (error) { // handle `AdaptyError` } ``` ### OneSignal 按照以下示例更新您的移动应用代码。完整代码示例请参阅 [OneSignal 集成的 SDK 配置](onesignal#sdk-configuration)。
:::important
**接下来的步骤取决于您是否已在 App Store 和/或 Google Play 中拥有产品:**
:::
5. 点击 **Save & Continue**,然后切换到 **App Store** 或 **Google Play** 标签页,填写该商店的产品详情。
您将在应用代码中渲染此付费墙。
在你的应用代码中,你只需硬编码版位 ID。其他一切——运行哪个付费墙、销售哪些产品、远程配置——都在 Adapty 看板中配置,随时可以修改,无需更新应用。
:::tip
Adapty 支持向不同用户群体展示不同的付费墙,并分析其效果。了解更多关于[目标受众](audience)和 [A/B 测试](ab-tests)的内容。
:::
## 后续步骤 \{#next-steps\}
恭喜你成功完成 Adapty 的用户引导!现在你已准备好提升应用内购买收益。
为正式发布做好准备:
或者,你也可以继续进行以下操作:
- **[A/B 测试](ab-tests)**:尝试不同的价格、订阅时长、试用期和视觉元素,以找出最有效的组合。
- **[分析](how-adapty-analytics-works)**:深入了解详细的变现数据图表,以理解用户行为并优化收益表现。
- **集成**:Adapty 将[订阅事件](events)发送到第三方分析和归因工具,例如 [Amplitude](amplitude)、[AppsFlyer](appsflyer)、[Adjust](adjust)、[Branch](branch)、[Mixpanel](mixpanel)、[Facebook Ads](facebook-ads)、[AppMetrica](appmetrica) 以及自定义 [Webhook](webhook)。
:::tip
有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助!
:::
---
# File: release-checklist
---
---
title: "发布检查清单"
description: "遵循 Adapty 的发布检查清单,确保应用更新过程顺畅无误。"
---
我们非常高兴您决定使用 Adapty!希望集成过程一切顺利。本指南将引导您完成确保应用准备好在商店发布所需的各个步骤,让您确信变现流程运行正常。
## 起飞前必备事项 \{#pre-flight-essentials\}
开始验证前您需要准备:
- 一台配置了沙盒账号的真实设备
- 访问 Adapty 看板的权限
- 访问 App Store Connect / Google Play Console 的权限
:::note
虽然沙盒购买可以在模拟器上运行,但要完整测试所有流程(包括支付对话框和生物识别提示),仍需要真实设备。
:::
## 通用验证 \{#universal-validations\}
- [ ] **商店连接**:确保已将 Adapty 连接至 App Store 和/或 Google Play:
- [ ] [App Store](initial_ios)
- [ ] [Google Play](initial-android)
- [ ] **订阅事件推送**:确认服务器通知已配置:
- [ ] [App Store 服务器通知](enable-app-store-server-notifications)
- [ ] [实时开发者通知(RTDN)](enable-real-time-developer-notifications-rtdn)
- [ ] **用户画像识别**:验证用户识别逻辑,确保购买记录关联到正确的用户画像:
- [ ] [检查应用代码中的识别逻辑是否符合你的使用场景](ios-quickstart-identify)
- [ ] [了解用于在用户画像之间共享付费访问权限的父级/继承逻辑](sharing-paid-access-between-user-accounts)
- [ ] **优惠活动**:如果应用中包含 App Store 促销活动,请确保已将内购密钥[添加到主字段和 **App Store promotional offers** 部分](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers)。
- [ ] **数据收集**:确保符合隐私合规要求:
- [ ] 如需遵守 GDPR、CCPA 等隐私法规,或应用面向儿童用户,请控制是否[启用 IDFA 和 IP 的收集与共享](sdk-installation-ios#data-policies)。
- [ ] 如果应用使用了 AppTrackingTransparency,请确保已[将授权状态发送给 Adapty](ios-deal-with-att)。
- [ ] **隐私标签**:[了解更多](apple-app-privacy) Adapty 收集的数据,以及审核时需要设置哪些标志。
## 购买验证 \{#purchase-validations\}
:::tip
有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助!
:::
在正式上线之前,请确保应用内购买功能正常运行,且付费墙已准备好通过应用商店审核。
验证应用内购买的方式取决于你的具体实现方案:
- 你展示的是通过 Adapty 付费墙编辑工具创建的付费墙
- 你实现了自定义付费墙,并在其中使用 `makePurchase` 方法处理购买
- 你以观察者模式使用 Adapty(无论是配合付费墙编辑工具还是自定义付费墙)
可行,但需要大量额外的编码和配置,比完整模式工作量更大。
| ✅ | | **实施时间** |用于分析和集成:不足一小时
包含 A/B 测试:经充分测试后最多需要一周
| 数小时 | ## 观察者模式的工作原理 \{#how-observer-mode-works\} 在观察者模式下,您需要将来自 Apple/Google 的新交易上报给 Adapty SDK,Adapty SDK 再将其转发至 Adapty 后端。您负责管理应用中付费内容的访问权限、完成交易、处理续订、解决账单问题等。 ## 如何设置观察者模式 \{#how-to-set-up-observer-mode\} 1. 完成 Adapty 与 [Google Play](initial-android) 和 [App Store](initial_ios) 的初始集成设置。 2. 在配置 Adapty SDK 时将 `observerMode` 参数设置为 `true` 以启用该模式。请参阅以下平台的设置说明:[iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk)、[Android](sdk-installation-android#activate-adapty-module-of-adapty-sdk)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk)、[Kotlin Multiplatform](sdk-installation-kotlin-multiplatform#activate-adapty-sdk) 和 [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk)。 3. 针对 iOS 及基于 iOS 的跨平台框架,将现有购买基础设施中的交易[上报至 Adapty](report-transactions-observer-mode)。 4. (可选)如需使用第三方集成,请按照[配置第三方集成](configuration)文档中的说明进行设置。 :::warning 在观察者模式下运行时,Adapty SDK 不会最终确认交易,请确保您自行处理这一环节。 ::: ## 如何在观察者模式中使用付费墙和 A/B 测试 \{#how-to-use-paywalls-and-ab-tests-in-observer-mode\} 在观察者模式下,Adapty SDK 无法确定购买来源,因为购买操作在您自己的基础设施中完成。因此,如果您打算在观察者模式中使用付费墙和/或 A/B 测试,则需要在上报交易时,在移动应用代码中将来自应用商店的交易与对应的付费墙进行关联。 此外,使用付费墙编辑工具设计的付费墙在观察者模式下需要以特殊方式展示: - 在观察者模式下展示付费墙:[iOS](implement-observer-mode) 或 [Android](android-present-paywall-builder-paywalls-in-observer-mode)。 - 在观察者模式下上报交易时,[将付费墙与购买交易进行关联](report-transactions-observer-mode)。 --- # File: migration-from-revenuecat --- --- title: "从 RevenueCat 迁移" description: "按照我们的分步指南,从 RevenueCat 迁移到 Adapty。" --- 整个迁移计划共分 5 个步骤,平均耗时约 2 小时。90% 的迁移工作可在一个工作日内完成。 1. 了解核心差异;创建并准备 Adapty 账户 _(5 分钟)_; 2. 为您的平台安装 Adapty SDK([iOS](sdk-installation-ios)、[Android](sdk-installation-android)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter)、[Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)、[Unity](sdk-installation-unity)),替换 RevenueCat SDK _(1 小时)_; 3. 为 Adapty 设置 [Apple App Store 服务器通知](enable-app-store-server-notifications),并(可选)设置[原始事件转发](enable-app-store-server-notifications#raw-events-forwarding) _(5 分钟)_; 4. 测试并发布您的应用更新 _(30 分钟)_; 5. (可选)向 RevenueCat 客服申请 CSV 格式的历史数据 _(5 分钟)_; 6. (可选)通过 Adapty 客服导入历史数据 _(30 分钟)_。 :::info 您的订阅用户将自动迁移 所有曾经激活过订阅的用户,只要打开集成了 Adapty SDK 的新版应用,就会立即迁移到 Adapty。订阅状态验证和高级功能访问权限将自动恢复。 ::: 在发布集成了 Adapty SDK 的新版应用之前,请务必查看我们的[发布清单](release-checklist)。 ## 了解核心差异;创建并准备 Adapty 账户 \{#learn-the-core-differences-create-and-prepare-an-adapty-account\} Adapty 与 RevenueCat 的 SDK 设计思路相似,最大的区别在于网络使用方式和响应速度:Adapty SDK 专为按需快速获取信息而设计,当你发起请求时,能以最快速度返回结果。例如,在请求付费墙时,你会先获取[远程配置](customize-paywall-with-remote-config),用于预构建用户引导或付费墙界面,然后再通过专门的请求获取产品信息。 命名方式略有不同: | RevenueCat | Adapty | | :---------- | :-------------- | | Package | 产品 | | Offering | 付费墙 | | Paywall | 付费墙编辑工具 | | Entitlement | 访问等级 | Adapty 有一个[版位](placements)的概念。它是应用内用户可以发起购买的逻辑位置。大多数情况下,你会有一到两个版位: - 用户引导(因为 80% 的购买都发生在这里); - 通用版位(在用户完成引导后,在设置页面或应用内展示)。
## 安装 Adapty SDK 并替换 RevenueCat SDK \{#install-adapty-sdk-and-replace-revenuecat-sdk\}
为你的平台安装 Adapty SDK([iOS](sdk-installation-ios)、[Android](sdk-installation-android)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter)、[Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)、[Unity](sdk-installation-unity)),并集成到你的应用中。
你需要在应用端替换几个 SDK 方法。下面介绍最常用的函数及其对应的 Adapty SDK 替换方式。
### SDK 激活 \{#sdk-activation\}
将 `Purchases.configure` 替换为 `Adapty.activate`。
### 获取付费墙(产品组合)\{#getting-paywalls-offerings\}
将 `Purchases.shared.getOfferings` 替换为 [`Adapty.getPaywall`](fetch-paywalls-and-products#fetch-paywall-information)。
在 Adapty 中,你始终通过[版位 ID](placements) 来请求付费墙。实际上,每次最多只会获取 1-2 个付费墙,这样设计是有意为之,目的是加快 SDK 速度并减少网络请求。
### 获取用户(客户用户画像)\{#getting-a-user-customer-profile\}
将 `Purchases.shared.getCustomerInfo` 替换为 `Adapty.getProfile`。
### 获取产品 \{#getting-products\}
在 RevenueCat 中,你使用以下结构:`Purchases.shared.getOfferings`,然后 `self.offering?.availablePackages`。
在 Adapty 中,你首先请求一个付费墙(见上文)以立即访问 Adapty 的[远程配置](customize-paywall-with-remote-config),然后通过 [`Adapty.getPaywallProducts`](fetch-paywalls-and-products#fetch-products) 获取产品。
### 进行购买 \{#making-a-purchase\}
将 `Purchases.shared.purchase` 替换为 [`Adapty.makePurchase`](making-purchases#make-purchase)。
### 检查访问等级(权益)\{#checking-access-level-entitlement\}
先获取用户画像(请先阅读上文),然后将
`customerInfo?.entitlements["premium"]?.isActive == true`
替换为
[`profile.accessLevels["premium"]?.isActive == true`](subscription-status#retrieving-the-access-level-from-the-server)。
### 恢复购买 \{#restore-purchase\}
将 `Purchases.shared.restorePurchases` 替换为 [`Adapty.restorePurchases`](restore-purchase)。
### 检查用户是否已登录 \{#check-if-the-user-is-logged-in\}
将 `Purchases.shared.isAnonymous` 替换为 `if profile.customerUserId == nil`。
### 登录用户 \{#log-in-user\}
将 `Purchases.shared.logIn` 替换为 [`Adapty.identify`](identifying-users#set-customer-user-id-after-configuration)。
### 退出用户登录 \{#log-out-user\}
将 `Purchases.shared.logOut` 替换为 [`Adapty.logout`](identifying-users#logging-out-and-logging-in)。
## 将 App Store 服务器端通知切换到 Adapty \{#switch-app-store-server-side-notifications-to-adapty\}
具体操作方法请参阅[此处](migrate-to-adapty-from-another-solutions#changing-apple-server-notifications)。
## 测试并发布新版本应用 \{#test-and-release-a-new-version-of-your-app\}
如果你看到这里,说明你已经完成了:
- [x] 配置 Adapty 看板
- [x] 安装 Adapty SDK
- [x] 用 Adapty 函数替换了原有 SDK 逻辑
- [x] 将 App Store 服务端通知切换至 Adapty,并可选择开启原始事件转发至 RevenueCat
- [ ] 沙盒购买测试
- [ ] 发布新版本应用
完成以上步骤后,在沙盒环境中进行一次测试购买,然后发布应用即可。
:::info
请参阅[发布检查清单](release-checklist)。
使用我们的清单对现有集成进行最终检查,或添加[归因](attribution-integration)或[分析](analytics-integration)集成等其他功能。
:::
## (可选)以 CSV 格式导出 RevenueCat 历史数据 \{#optional-export-your-revenuecat-historical-data-in-csv-format\}
:::warning
不要急于导入历史数据
建议在集成 SDK 并发版后,至少等待一周再进行历史数据导入。在此期间,我们将通过 SDK 获取所有购买价格信息,从而使导入的数据更加准确。
:::
请按照 [RevenueCat 官方文档](https://www.revenuecat.com/docs/integrations/scheduled-data-exports) 中的说明,以 CSV 格式从 RevenueCat 导出您的历史数据。
## (可选)向 RevenueCat 支持团队索取 Google Purchase Tokens \{#optional-ask-revenuecat-support-for-google-purchase-tokens\}
如果你需要导入 Google Play 交易记录,请通过 RevenueCat 的[支持页面](https://app.revenuecat.com/settings/support)联系其支持团队,索取包含 Google Purchase Tokens 的 CSV 文件。Google Purchase Token 是 Google Play 为每笔交易提供的唯一标识符,对于在 Adapty 中准确追踪和验证购买记录至关重要。该信息不包含在标准导出文件中。该文件包含以下三列:
- `user_id`
- `google_purchase_token`
- `google_product_id`
## 联系我们以导入历史数据 \{#write-us-to-import-your-historical-data\}
请通过网站聊天工具或发送邮件至 [support@adapty.io](mailto:support@adapty.io) 联系我们,并附上您的 CSV 文件。
1. 将您从 RevenueCat 导出的 CSV 文件直接发送给我们的支持团队。
2. 如果需要导入 Google Play 交易记录,请同时附上从 RevenueCat 支持团队获取的包含 Google Purchase Token 的 CSV 文件。
3. 请告知我们应使用哪个用户 ID 作为 Customer User ID(Adapty 的主要用户标识符):`rc_original_app_user_id` 或 `rc_last_seen_app_user_id_alias`。
我们的支持团队将为您把交易记录导入 Adapty。每笔交易将导入以下数据:
| 参数 | 描述 |
| ----------------------------- | ------------------------------------------------------------ |
| user_id | 客户用户 ID,即用户在 Adapty 和您系统中的主要标识符。 |
| apple_original_transaction_id | 对于订阅链,这是原始交易的购买日期,通过 `store_original_transaction_id` 关联。 |
| google_product_id | Google Play 商店中的产品 ID。 |
| google_purchase_token | Google Play 为每笔交易提供的唯一标识符,用于验证。 |
| country | 用户所在国家/地区。 |
| created_at | 用户创建的日期和时间。 |
| subscription_expiration_date | 订阅到期的日期和时间。 |
| email | 终端用户的电子邮件。 |
| phone_number | 终端用户的手机号码。 |
| idfa | 广告主标识符(IDFA),由 Apple 分配给用户设备。 |
| idfv | 供应商标识符(IDFV),由同一开发者分配给其所有应用,并在该设备上的这些应用间共享。 |
| advertising_id | 由 Android 操作系统提供的唯一标识符,广告主可用于广告追踪。 |
| attribution_channel | 营销渠道名称。 |
| attribution_campaign | 营销活动名称。 |
| attribution_ad_group | 归因广告组。 |
| attribution_ad_set | 归因广告集。 |
| attribution_creative | 归因创意关键词。 |
此外,以下集成的集成标识符也将被导入:Amplitude、Mixpanel、AppsFlyer、Adjust 和 FacebookAds。
## 常见问题 \{#faq\}
### 我已成功安装 Adapty SDK 并发布了包含它的新版本。那些没有更新到包含 Adapty SDK 版本的老订阅用户会怎样?\{#i-successfully-installed-adapty-sdk-and-released-a-new-app-version-with-it-what-will-happen-to-my-legacy-subscribers-who-did-not-update-to-a-version-with-adapty-sdk\}
大多数用户会在夜间充电时让手机自动更新所有应用,因此这通常不是问题。确实可能还有少量付费订阅用户没有升级,但他们仍然可以访问高级内容。你不需要为此担心,也无需强制他们更新。
### 我需要尽快从 RevenueCat 导出历史数据吗?还是说不导出会丢失数据?\{#do-i-need-to-export-my-historical-data-from-revenuecat-as-quickly-as-possible-or-will-i-lose-it\}
不需要那么着急,先发布集成了 Adapty SDK 的版本,之后再向我们提供历史数据即可。我们会还原用户的付款记录,并填充[用户画像](profiles-crm)和[数据图表](charts)。
### 我使用 MMP(AppsFlyer、Adjust 等)和分析工具(Mixpanel、Amplitude 等)。如何确保一切正常运行?\{#i-use-mmp-appsflyer-adjust-etc-and-analytics-mixpanel-amplitude-etc-how-do-i-make-sure-that-everything-will-work\}
您首先需要通过我们的 SDK 将您希望我们发送数据的第三方服务 ID 传递给我们。请阅读[归因集成](attribution-integration)和[分析集成](analytics-integration)的相关指南。对于历史数据和老用户,**请确保您从 RevenueCat 导出的数据中将这些 ID 传递给我们。**
---
# File: migration-from-superwall
---
---
title: "从 Superwall 迁移"
description: "通过逐步指南将 Superwall 迁移到 Adapty,涵盖每个 SDK 调用和概念的对应关系。"
---
从 Superwall 迁移到 Adapty 通常只需约两小时。你只需替换 SDK、将应用商店服务器通知指向 Adapty,然后发布新版本即可。付费订阅用户的权益会自动保留——Adapty 在用户首次启动时即可从 App Store 和 Google Play 收据中恢复。
:::info
你的订阅用户将自动完成迁移
所有曾经激活过订阅的用户,只要打开集成了 Adapty SDK 的新版应用,就会自动迁移到 Adapty。订阅状态验证和高级访问权限会自动恢复。
:::
## 本指南的结构 \{#how-this-guide-is-organized\}
迁移共分六个步骤:
1. [将 Superwall 概念映射到 Adapty](#map-your-superwall-concepts-to-adapty) _(5 分钟)_
2. [安装 Adapty SDK](#install-the-adapty-sdk) _(15 分钟)_
3. [替换 SDK 调用](#replace-sdk-calls) _(1 小时)_
4. [切换 App Store 和 Google Play 服务器通知](#switch-app-store-and-google-play-server-notifications) _(5 分钟)_
5. [测试与发布](#test-and-release) _(30 分钟)_
6. [(可选)导入历史数据](#optional-import-historical-data)
## 将 Superwall 概念映射到 Adapty \{#map-your-superwall-concepts-to-adapty\}
大多数 Superwall 概念在 Adapty 中都有对应的概念:
| Superwall | Adapty | 变更说明 |
| :------------------- | :------------------------------------------------ | :--------------------------------------------------------------------------- |
| Campaign | [版位](placements) + [目标受众](audience) | Campaign 逻辑拆分为版位(位置)和目标受众(规则)。 |
| Placement | [版位](placements) | 概念相同,名称相同。 |
| Audience filter | [目标受众](audience) | 规则集位于版位内部。 |
| Entitlement | [访问等级](access-level) | 命名标识符(例如 `premium`)。 |
| WebView paywall | [付费墙编辑工具付费墙](adapty-paywall-builder) | 由 Adapty SDK 原生渲染,而非使用 `WKWebView`。 |
| `PurchaseController` | 内置 | 无需实现协议 —— Adapty 自动处理购买流程。 |
| Feature gating | [访问等级](access-level)检查 | 检查 `profile.accessLevels["premium"]?.isActive`。 |
在接触代码之前,有两个思维转变值得注意:
- **获取与展示是两个独立步骤**:Superwall 的 `register` 方法在一次调用中完成付费墙获取、营销活动评估和 UI 展示。Adapty 将这些步骤拆分开来——你需要先获取付费墙,拿到其配置,再进行展示。虽然多了几行代码,但这让你可以预加载配置、显示自定义加载状态,或根据自己的逻辑取消展示。
- **订阅状态按访问等级区分**:Superwall 暴露单一的 `subscriptionStatus` 发布属性。Adapty 返回一个包含命名访问等级的 [`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile),因此同一用户可以同时持有 `sports` 和 `science` 两个独立的访问等级。如需同步读取,建议从 `AdaptyDelegate` 缓存用户画像,而不是每次视图加载时都调用 `getProfile()`。
## 安装 Adapty SDK \{#install-the-adapty-sdk\}
为你的平台安装 Adapty SDK —— [iOS](sdk-installation-ios)、[Android](sdk-installation-android)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter)、[Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)、[Unity](sdk-installation-unity) 或 [Capacitor](sdk-installation-capacitor) —— 同时从项目中移除 SuperwallKit。
## 替换 SDK 调用 \{#replace-sdk-calls\}
逐一检查集成的各个部分,将 Superwall 调用替换为对应的 Adapty 调用。每个小节末尾都附有链接,涵盖全部七个平台的 SDK——请根据你的应用选择对应链接。
### 初始化 SDK \{#initialize-the-sdk\}
将 `Superwall.configure` 替换为 `Adapty.activate`。
请查阅适用于你所在平台的安装指南 —— [iOS](sdk-installation-ios)、[Android](sdk-installation-android)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter)、[Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)、[Unity](sdk-installation-unity) 或 [Capacitor](sdk-installation-capacitor)。
### 识别和登出用户 \{#identify-and-log-out-users\}
将 `Superwall.shared.identify` 替换为 `Adapty.identify`,将 `Superwall.shared.reset` 替换为 `Adapty.logout`。两个 SDK 都会在首次启动时生成匿名用户画像,因此只有在用户登录或登出时才需要调用这些方法。识别用户后需重新获取付费墙——新用户可能会匹配到不同的目标受众。
请参阅适用于您平台的识别指南 — [iOS](identifying-users)、[Android](android-identifying-users)、[React Native](react-native-identifying-users)、[Flutter](flutter-identifying-users)、[Kotlin Multiplatform](kmp-identifying-users)、[Unity](unity-identifying-users) 或 [Capacitor](capacitor-identifying-users)。
### 获取并展示付费墙 \{#fetch-and-present-a-paywall\}
将 `Superwall.shared.register` 替换为两步流程:先用 `Adapty.getPaywall` 获取付费墙,再用 `AdaptyUI.getPaywallConfiguration` 加载其视图配置,最后进行展示。
需要注意两点区别:
- **功能门控取代了 `feature:` 闭包**:付费墙关闭后,检查返回的用户画像(或通过 `Adapty.getProfile` 获取)上的有效访问等级,再据此进行分支处理。
- **付费墙由 SDK 渲染**:Superwall 在 `WKWebView` 中渲染付费墙。Adapty 则通过付费墙编辑工具以原生方式渲染付费墙——字体、产品信息和按钮均由 SDK 直接绘制。
请参阅适用于您平台的付费墙快速入门 — [iOS](ios-quickstart-paywalls)、[Android](android-quickstart-paywalls)、[React Native](react-native-quickstart-paywalls)、[Flutter](flutter-quickstart-paywalls)、[Kotlin Multiplatform](kmp-quickstart-paywalls)、[Unity](unity-quickstart-paywalls) 或 [Capacitor](capacitor-quickstart-paywalls)。
### 检查订阅状态 \{#check-subscription-status\}
将 `Superwall.shared.subscriptionStatus` 替换为对用户画像中指定访问等级的检查:`profile.accessLevels["premium"]?.isActive`。通过 `AdaptyDelegate.didLoadLatestProfile(_:)` 监听变更,而非使用 `@Published` 属性模式,并在本地缓存用户画像以便同步读取。
请参阅适用于您平台的订阅状态指南 — [iOS](ios-check-subscription-status)、[Android](android-check-subscription-status)、[React Native](react-native-check-subscription-status)、[Flutter](flutter-check-subscription-status)、[Kotlin Multiplatform](kmp-check-subscription-status)、[Unity](unity-check-subscription-status) 或 [Capacitor](capacitor-check-subscription-status)。
### 处理购买与恢复 \{#handle-purchases-and-restores\}
使用付费墙编辑工具时,两个 SDK 都会在付费墙界面内自动处理购买流程——**此步骤可跳过**。
对于自定义付费墙,Superwall 需要实现 `PurchaseController`,而 Adapty 不需要:将 `PurchaseController.purchase` 替换为 `Adapty.makePurchase`,将 `PurchaseController.restorePurchases` 替换为 `Adapty.restorePurchases`。SDK 会自行处理验证逻辑。
请参阅适用于您平台的自定义付费墙快速入门指南 — [iOS](ios-quickstart-manual)、[Android](android-quickstart-manual)、[React Native](react-native-quickstart-manual)、[Flutter](flutter-quickstart-manual)、[Kotlin Multiplatform](kmp-quickstart-manual)、[Unity](unity-quickstart-manual) 或 [Capacitor](capacitor-quickstart-manual)。
### 设置用户属性 \{#set-user-attributes\}
将 `Superwall.shared.setUserAttributes` 替换为 `Adapty.updateProfile`。
请参阅适用于您平台的用户属性指南 — [iOS](setting-user-attributes)、[Android](android-setting-user-attributes)、[React Native](react-native-setting-user-attributes)、[Flutter](flutter-setting-user-attributes)、[Kotlin Multiplatform](kmp-setting-user-attributes)、[Unity](unity-setting-user-attributes) 或 [Capacitor](capacitor-setting-user-attributes)。
## 切换 App Store 和 Google Play 服务器通知 \{#switch-app-store-and-google-play-server-notifications\}
将应用商店的服务器通知指向 Adapty。Adapty 不依赖这些通知也能正常运行,但分析数据、第三方集成以及 A/B 测试数据图表都需要它们:
- **App Store**:请参阅[启用 App Store 服务器通知](enable-app-store-server-notifications)。
- **Google Play**:请参阅[启用实时开发者通知](enable-real-time-developer-notifications-rtdn)。
如果您想在推出过程中并行运行 Superwall 和 Adapty,请使用[原始事件转发](enable-app-store-server-notifications#raw-events-forwarding) —— Adapty 会将商店事件代理回 Superwall,同时您可以验证新的集成。
## 测试与发布 \{#test-and-release\}
发布前,请逐一确认以下各项:
- [x] 已配置 Adapty 看板(产品、付费墙、版位、访问等级)
- [x] 已安装 Adapty SDK
- [x] 已将 Superwall SDK 调用替换为 Adapty 等效调用
- [x] 已将 App Store 和 Google Play 服务器通知指向 Adapty
- [ ] 已完成沙盒购买
- [ ] 已提交新版本应用
请参阅[发布检查清单](release-checklist)进行最终验证。
## (可选)导入历史数据 \{#optional-import-historical-data\}
Superwall 并不拥有您的订阅状态——App Store 和 Google Play 才是。Adapty 在首次启动时会验证收据,因此付费用户无需任何导入即可保留其访问权限。
如果您希望将历史交易数据回填到 Adapty 分析中,请参考[向 Adapty 导入历史数据](importing-historical-data-to-adapty)。建议在 SDK 发布后至少等待一周,以便 SDK 有足够时间收集最新的购买价格。
## 常见问题 \{#faq\}
### 不更新应用的订阅者会怎样?\{#what-happens-to-subscribers-who-dont-update-the-app\}
大多数用户会在夜间自动更新应用,因此使用旧版本的用户比例会迅速下降。留在旧版本的订阅者可以直接通过 App Store 或 Google Play 继续使用其权益,无需强制更新。
### 我的 Superwall 活动目标受众会自动迁移吗?\{#do-my-superwall-campaign-audiences-carry-over\}
不会。Superwall 的受众过滤器和 Adapty 的目标受众分别在不同的看板中配置,且使用不同的标识符。请在 Adapty 的[版位](placements)中重新创建你的定向规则,作为[目标受众](audience)进行设置。大多数应用只有一两个版位(用户引导和通用应用内触发),因此重建工作通常很快就能完成。
### Adapty 是否有与 `getPresentationResult` 等效的方法?\{#does-adapty-have-an-equivalent-to-getpresentationresult\}
没有单独的调用方法。如需判断某个版位是否会显示付费墙,请调用 `Adapty.getPaywall(placementId:)` 并根据结果进行分支处理。若调用成功,说明该用户的目标受众已分配付费墙;若调用失败(原因是未配置付费墙),则跳过展示并执行备用逻辑。
---
# File: importing-historical-data-to-adapty
---
---
title: "将历史数据导入 Adapty"
description: "将历史数据导入 Adapty 以获取详细分析。"
---
安装 Adapty SDK 并发布应用后,你可以在 [Profiles](profiles-crm) 部分查看用户和订阅者。但如果你有旧系统需要迁移到 Adapty,或者只是想在 Adapty 中查看现有数据,该怎么办?
:::note
数据导入并非必须
一旦用户打开集成了 Adapty SDK 的应用,Adapty 会自动为历史用户授予访问等级并恢复其购买事件。在这种情况下,无需导入历史数据。不过,如果你有大量历史交易记录,导入数据可以确保分析数据的准确性,但对于迁移而言通常并非必须。
:::
将数据导入 Adapty 的步骤如下:
1. 将交易记录导出为 CSV 文件(iOS、Android 和 Stripe 需分别提供独立文件)。详细格式要求请参阅下方的[导入文件格式说明](importing-historical-data-to-adapty#import-file-format)。
2. 如果任意文件超过 1 GB,请准备一个约 100 行的数据样本。
3. 将所有文件上传至 Google Drive(可以压缩,但需保持独立文件)。
4. 对于 iOS 交易记录,即使使用的是 StoreKit 1,也请确保 [**App settings**](https://app.adapty.io/settings/ios-sdk) 中的 **In-app purchase API** 部分已填写 **Issuer ID**、**Key ID** 及 **Private key**(.P8 文件)。详细操作说明请参阅[提供 Issuer ID 和 Key ID](app-store-connection-configuration#step-2-provide-issuer-id-and-key-id) 及[上传 In-App Purchase Key 文件](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file)。
5. 通过[电子邮件](mailto:support@adapty.io)或 Adapty 看板内的在线客服将链接分享给我们的团队。
放心,导入历史数据不会产生重复记录,即使数据与 Adapty 中已有条目存在重叠。
## Android 已知限制 \{#known-limitations-for-android\}
1. 只会恢复有效订阅,已过期的交易记录不会被恢复。
2. 只会恢复订阅中最近一次续订记录,完整的购买链不会被恢复。
3. 如果产品价格在购买后发生了变化,将使用当前价格,可能导致定价不准确。
:::note
如果你有大量 Android 交易记录,在开始导入前可能需要[申请提高 Google Play Developer API 配额](google-play-quota-increase),以避免超出默认 API 限制。
:::
## 导入文件格式 \{#import-file-format\}
:::tip
如果你正在从 RevenueCat 迁移,可以直接发送 RevenueCat 导出文件,无需转换。导出说明请参阅 [RevenueCat 文档](https://www.revenuecat.com/docs/integrations/scheduled-data-exports)。
:::
请按照以下规则准备数据文件:
- [ ] 文件格式为 .CSV。
- [ ] Android、iOS 和 Stripe 导入需使用独立文件。
- [ ] 每个导入文件包含所有[必填列](importing-historical-data-to-adapty#required-fields)。
- [ ] 导入文件中的列需有标题行。
- [ ] 列标题须与下表 **Column name** 列中的内容完全一致,请仔细检查是否有拼写错误。
- [ ] 不需要的列可以不出现在文件中,不要为没有数据的字段添加空列。
- [ ] 导入文件不应包含表中未提及的额外列,如有请删除。
- [ ] 值之间用逗号分隔。
- [ ] 值不需要用引号括起来。
- [ ] 如果一个用户有多个 **apple_original_transaction_id**,请为每个 **apple_original_transaction_id** 单独添加一行,否则可能无法恢复消耗型商品的购买记录。
iOS 和 Android 的示例文件请参考:[iOS](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/adapty_import_ios_sample.csv) 和 [Android](https://raw.githubusercontent.com/adaptyteam/adapty-docs/refs/heads/main/Downloads/adapty_import_android_sample.csv)。
### 可用的导入文件列 \{#available-import-file-columns\}
| 列名 | 是否必填 | 说明 |
|-----------|--------|-----------|
| **user_id** | 必填 | 你的用户 ID |
| **apple_original_transaction_id** | iOS 必填 | 原始交易 ID(OTID,[了解更多](https://developer.apple.com/documentation/appstoreserverapi/originaltransactionid)),用于 StoreKit 2 导入机制。由于一个用户可能有多个 OTID,只需提供至少一个即可成功导入。
**注意:** 此导入需要在 Adapty 看板中配置 In-app purchase API 凭据。操作说明请参阅[此处](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file)。
| | **google_product_id** | Google 必填 | Google Play Store 中的产品 ID。 | | **google_purchase_token** | Google 必填 | 唯一标识符,代表用户及其购买的应用内产品 ID | | **google_is_subscription** | Google 必填 | 可选值为 `1` \| `0` | | **stripe_token** | Stripe 必填 | 代表唯一购买记录的 Stripe 对象 token,可以是 Stripe 订阅的 token(`sub_...`)或 Payment Intent 的 token(`pi_...`)。 | | **subscription_expiration_date** | 可选 | 订阅到期日期,即下次扣费日期,包含时区的日期时间格式(2020-12-31T23:59:59-06:00) | | **created_at** | 可选 | 用户画像创建的日期时间(2019-12-31 23:59:59-06:00) | | **birthday** | 可选 | 用户生日,格式为 2000-12-31 | | **email** | 可选 | 用户的电子邮件地址 | | **gender** | 可选 | 用户性别 | | **phone_number** | 可选 | 用户的电话号码 | | **country** | 可选 | 格式为 [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) | | **first_name** | 可选 | 用户名字 | | **last_name** | 可选 | 用户姓氏 | | **last_seen** | 可选 | 包含时区的日期时间(2020-12-31T23:59:59-06:00) | | **idfa** | 可选 | 广告标识符(IDFA)是 Apple 为用户设备随机分配的设备标识符,仅适用于 iOS 应用 | | **idfv** | 可选 | 供应商标识符(IDFV)是分配给同一开发者旗下所有应用的唯一代码,仅适用于 iOS 应用 | | **advertising_id** | 可选 | 广告 ID 是由 Android 操作系统分配的唯一代码,广告商可用其唯一标识用户设备 | | **amplitude_user_id** | 可选 | Amplitude 中的用户 ID | | **amplitude_device_id** | 可选 | Amplitude 中的设备 ID | | **mixpanel_user_id** | 可选 | Mixpanel 中的用户 ID | | **appmetrica_profile_id** | 可选 | AppMetrica 中的用户画像 ID | | **appmetrica_device_id** | 可选 | AppMetrica 中的设备 ID | | **appsflyer_id** | 可选 | AppsFlyer 的唯一标识符 | | **adjust_device_id** | 可选 | Adjust 中的设备 ID | | **facebook_anonymous_id** | 可选 | Facebook 为匿名与你的应用或网站互动(即未登录 Facebook)的用户生成的唯一标识符 | | **branch_id** | 可选 | Branch 的唯一标识符 | | **attribution_source** | 可选 | 归因来源集成,例如 appsflyer | | **attribution_status** | 可选 | organic | | **attribution_channel** | 可选 | 带来该交易的归因渠道 | | **attribution_campaign** | 可选 | 带来该交易的归因活动 | | **attribution_ad_group** | 可选 | 带来该交易的归因广告组 | | **attribution_ad_set** | 可选 | 带来该交易的归因广告集 | | **attribution_creative** | 可选 | 广告或营销活动中用于追踪效果的具体视觉或文字素材,用于衡量其在推动点击、转化或安装等目标行为方面的效果 | | **custom_attributes** | 可选 | 以 JSON 字典的键值格式定义最多 30 个自定义属性:格式:`"{'string_value': 'some_value', 'float_value': 123.0, 'int_value': 456}"`。
注意格式中双引号和单引号的使用,布尔值和整数将被转换为浮点数。
| ### 必填字段 \{#required-fields\} 每个平台有两组必填字段:**user_id** 以及用于识别对应平台购买记录的数据。各平台的必填字段请参见下表。 | 平台 | 必填字段 | |--------|---------------| | iOS |user_id
apple_original_transaction_id
| | Android |user_id
google_product_id
google_purchase_token
google_is_subscription
| | Stripe |user_id
stripe_token
| 缺少这些字段,Adapty 将无法获取交易记录。 为了获得准确的同期群分析,请填写 `created_at`。若未提供,我们将以首次购买日期作为安装日期。 ### 将数据导入 Adapty \{#import-data-to-adapty\} 请通过 [support@adapty.io](mailto:support@adapty.io) 或 [Adapty 看板](https://app.adapty.io/overview) 内的在线客服联系我们并分享导入文件。 --- # File: migrate-integrations-to-adapty --- --- title: "将集成迁移到 Adapty" description: "将分析和归因集成从旧版解决方案切换到 Adapty,同时避免重复事件或中断广告系列。" --- 迁移到 Adapty 不仅仅是切换 SDK。您的第三方分析和归因集成——如 Amplitude 和 Adjust 等工具——也需要进行协调切换。操作得当,过渡过程中几乎不会产生重复或丢失的事件,也不会影响您的推广活动。 ## 映射您的事件 \{#map-your-events\} 在大多数 Adapty 集成中,事件名称是可自定义的。您可以将其配置为与看板和广告活动中已使用的名称匹配。切换后,您的分析和广告活动报告将继续使用相同的事件名称。要查看 Adapty 中所有可用事件的完整列表,请参阅[事件](events)。 对于 Adjust,集成使用事件 ID 而非自定义事件名称。请将您现有的事件 ID 从 Adjust 看板迁移到 Adapty 集成配置中。详情请参阅 [Adjust 集成指南](adjust)。 ## Adapty 如何创建集成事件 \{#how-adapty-creates-integration-events\} 要将事件发送到集成,Adapty 必须拥有用户画像。用户画像通过以下两种方式之一创建: - **历史数据导入**:在 SDK 上线之前,当您[导入历史交易数据](importing-historical-data-to-adapty)时创建用户画像。 - **SDK 交互**:当用户首次使用带有 Adapty SDK 的应用时,系统自动创建用户画像。 Adapty 可实时获取在旧系统中发生的购买记录。但只有在买家的用户画像存在时,才能发送集成事件。用户画像在用户使用集成了 Adapty SDK 的应用版本首次打开应用时创建。未升级到新版本的用户将不会产生集成事件。 ## 迁移日之前的准备工作 \{#prepare-before-migration-day\} ### 排除历史事件 \{#exclude-historical-events\} 在您的[集成设置](configuration)中启用 **Exclude Historical Events**。这可以防止早于用户首次 Adapty SDK 会话的事件被发送到集成中。 此设置在[历史数据导入](importing-historical-data-to-adapty)期间尤为重要,届时 Adapty 会一次性处理大量过去的交易记录。如果不启用此设置,这些交易将在您的分析工具中生成大量事件。 ### 提前配置集成 \{#set-up-the-integration-in-advance\} Adapty 允许您在保持集成禁用状态的同时进行配置和测试。您可以设置凭据、事件映射和过滤器,而无需在准备好之前激活集成。启用集成时,所有配置都会被保留,因此在迁移日之前保持关闭状态不会造成任何数据丢失。 要查找您的集成,请参阅[归因集成](attribution-integration)、[分析集成](analytics-integration)、[消息服务集成](messaging)或 [Webhook 和 ETL 集成](webhook-and-etl)。 ## 迁移当天切换 \{#switch-on-migration-day\} 在迁移当天,同时禁用旧方案中的集成并在 Adapty 中启用该集成。同时运行两者将产生重复事件。 在迁移当天暂停大型获客活动。这可以降低由重叠窗口期内的事件导致活动优化出错的风险。 ## 预期结果 \{#what-to-expect\} 迁移过程中出现少量缺失或重复的集成事件是不可避免的。只要切换操作正确,受影响的事件数量可以忽略不计。 产生数据缺口的主要原因在于上述时序问题:Adapty 只有在用户画像存在之后,才能为某笔购买发送集成事件。在旧系统中产生的购买记录,只有当买家使用集成了 Adapty SDK 的应用打开 App 后,才会生成对应的 Adapty 集成事件。 ## 集成与服务器到服务器通知 \{#integrations-vs-server-to-server-notifications\} Adapty 建议使用集成,而非将原始服务器到服务器商店通知直接转发给您的分析或归因工具。 使用集成的优势: - **统一格式**:来自所有商店(App Store、Google Play、Stripe)的事件均采用相同的事件格式。 - **数据增强**:事件包含 Adapty 收集的数据,例如订阅状态和用户属性,而原始通知则不包含这些信息。 --- # File: whats-new --- --- title: "最新动态" description: "随时了解 Adapty 的最新功能和改进" --- 探索最新功能、改进、SDK 更新以及文档增强内容,助您优化应用的变现策略。本页面每月重点介绍最重要的版本发布。 :::note 对新功能有反馈意见? 欢迎告诉我们!请通过 [产品反馈板](https://adapty.featurebase.app/en?b=69831ba5e82e7a3391632ec2) 联系我们。 ::: ## 2026年7月 \{#july-2026\} - **虚拟货币**:在应用内定义代币、金币或宝石等虚拟货币,为每位用户发放并追踪余额,并通过服务端 API 从服务器读取这些余额。[了解更多](virtual-currencies) - **Apple Ads Manager 中的 AI 助手**:通过对话式 AI 助手查询 Apple Ads 的投放表现,直接获取基于广告活动数据的分析结果,无需手动构建报告。[了解更多](ads-manager-ai-agent) - **Apple Ads Manager 中的新自动化功能**:通过两种新规则类型,在广告系列和广告组级别自动执行更改,与现有的关键词和搜索词自动化功能并列使用。[广告系列规则](ads-manager-automations-campaign-rules) | [广告组规则](ads-manager-automations-ad-group-rules) - **Adapty Mail 中的用户画像**:以单个订阅者为维度的视图,在同一页面展示每位用户的操作历程、当前订阅状态及退订状态。[了解更多](mail-profiles) - **React Native、Flutter、Capacitor 和 Kotlin Multiplatform 的 SDK v4**:支持 Flows 的 v4 SDK 正式发布。React Native、Flutter 和 Capacitor 已全面上线,Kotlin Multiplatform v4 也已发布——每个平台均附有独立的迁移指南。[React Native](migration-to-react-native-sdk-v4) | [Flutter](migration-to-flutter-sdk-v4) | [Capacitor](migration-to-capacitor-sdk-v4) | [Kotlin Multiplatform](migration-to-kmp-sdk-v4) - **新增 Webhook 字段**:Webhook 推送内容现在包含每笔交易的原始价格和折扣信息,方便你在下游追踪促销活动和新用户优惠的定价情况。这些字段仅在 Webhook 中提供。[了解更多](webhook-event-types-and-fields) - **流程中的删除线价格**:直接在付费墙编辑工具中展示带删除线的原始价格及折扣徽章,与折后价并排显示。[了解更多](strikethrough-price) - **流程模板库**:从专业设计的模板开始创建新流程,而无需从空白画布起步,然后根据您的应用进行自定义。[了解更多](paywall-builder-templates) - **安装工具按钮**:每篇文档文章的页眉处现在都有一个 **Install tools** 按钮。点击后会弹出一个模态框,其中包含可直接复制的命令,用于在 Claude Code、Copilot CLI、Gemini CLI、Codex 及其他 AI 编程助手中安装 Adapty SDK 集成技能。[了解更多](adapty-sdk-integration-skill) - **全新 Unity SDK 安装方式**:现在可通过 Swift Package Manager 安装 Unity SDK,并新增了常见配置问题的故障排查指南。[了解更多](sdk-installation-unity) - **Flow Builder 中的底部容器**:一个固定在底部的面板,在页面其余内容滚动时始终保持置顶——非常适合用于 CTA 按钮、法律文本和链接。[了解更多](builder-containers#footer) - **全新流程编辑器视频教程**:YouTube 上持续更新的分步视频播放列表,帮助你从零开始构建流程,现已嵌入各流程编辑器指南中。[了解更多](adapty-flow-builder) ## 2026年6月 \{#june-2026\} - **Flows 现已支持 Android**:用于付费墙和用户引导的可视化无代码构建工具现已支持 Android SDK v4 及以上版本,与 iOS 并行运行。界面原生渲染,无需 Web 视图。[了解更多](adapty-flow-builder) - **Apple Ads Manager 中的 CPP A/B 测试**:在 Apple Ads 中对比不同的自定义产品页面。选择 2 到 4 个页面(包括当前默认页面),Apple Ads 将在这些页面之间轮流分配流量,并报告哪个页面的转化效果最佳。[了解更多](ads-manager-cpp-ab-tests) - **Adapty Mail API**:直接从您的服务器将用户画像和交易数据发送到 Adapty Mail,无需通过 Adapty SDK 传输数据。可用于填充订阅者数据库、复用其他应用中的订阅者,或将您的后端作为数据的唯一可信来源。[了解更多](mail-send-data-via-api) - **在首次启动时展示 Apple Ads 定向付费墙**:Apple Ads 归因数据在 SDK 激活后才会到达,因此过早请求付费墙会导致错过 Apple Ads 目标受众。使用 `AdaptyProfile.appliedAttributionSources` 可在归因数据到达后立即展示 Apple Ads 定向付费墙。[iOS](ios-show-aa-targeted-paywall) | [React Native](react-native-show-aa-targeted-paywall) | [Capacitor](capacitor-show-aa-targeted-paywall) - **Flow 编辑工具自动保存功能**:Flow 编辑工具现在每分钟自动保存一次您的进度,离开页面时不再丢失未保存的内容。您仍可以使用 **Cmd/Ctrl + S** 手动保存草稿。[了解更多](builder-save-publish) - **Flow 编辑工具新视频教程**:新增两个演示视频,分别介绍如何在流程页面间构建导航,以及如何设计选中、激活和禁用等元素状态。[流程中的导航](onboarding-navigation-branching) | [元素状态](builder-element-states) - **日语和越南语文档**:Adapty 文档现已支持日语(日本語)和越南语(Tiếng Việt)。使用顶部导航栏中的语言选择器切换语言。 ## 2026 年 5 月 \{#may-2026\} - **流程(Beta)**:在可视化无代码编辑器中创建完整的页面序列——单屏付费墙、多步骤用户引导以及介于两者之间的任意组合,全部在一个流程中完成。页面以原生方式渲染,无需 Web 视图,且无需发布应用更新即可修改文案、设计和逻辑。目前支持 iOS、Android、React Native、Flutter 以及 Capacitor SDK v4 及以上版本。[了解更多](adapty-flow-builder) - **Autopilot 现在能根据测试结果自动调整**:它作为 AI 增长经理,在每轮测试完成后更新增长计划。下一个假设会基于你已运行的实验、哪些实验胜出、以及哪些方向仍值得探索来制定——而不是按照固定顺序推进。[了解更多](autopilot-how-it-works#how-ai-growth-advisor-decides-what-to-recommend) - **Autopilot 市场洞察中的激活 ARPU**:新增数据图表,将您应用的每次新安装平均收入与品类平均水平进行对比。结合转化漏斗一起使用——高转化率配合低激活 ARPU,可能意味着定价偏低。[了解更多](autopilot-analysis#activation-arpu) - **Adapty Mail 中的数据分析**:在同一视图中对比每个营销活动的投递指标和邮件归因收入。可按营销活动、市场细分、A/B 实验变体、消息或触发器进行分组、细分和筛选,并可下钻至任意行查看详情。[了解更多](mail-analytics) - **Adapty Mail 中的品牌档案**:一个统一的档案,用于驱动邮件文案、语调、视觉效果及网页付费墙内容。Adapty 会从您应用的商店列表、落地页、法律页面和社交主页中构建该档案,您可以在线查看或逐项修改。[了解更多](mail-brand) - **Adapty UA 中的趋势预测**:为每个同期群提供预测收入、ROAS、广告利润、ARPU 和 ARPPU,让你在广告系列成熟之前就能进行对比。趋势预测基于你应用自身的历史同期群数据构建,每日更新,支持从 D0 到 D360 或自定义天数的同期群周期。[了解更多](ua-predicted-metrics) - **Adapty UA 自定义 S3 导出新增字段**:自定义 S3 导出现已包含 `bundle_id`、`device_brand`、`device_model`、`os_version`、`app_version` 和 `sdk_version` 字段。可在下游按设备和应用版本对归因数据进行切片和关联分析。[了解更多](ua-custom-s3) - **CLI 中的版位目标受众**:`adapty placements create` 和 `adapty placements update` 命令现已支持 `--audiences` 参数——一个由 `{segment_ids, paywall_id, priority}` 条目组成的 JSON 数组——让你可以直接在终端为不同市场细分指定不同付费墙。新增的 `adapty paywalls placements` 命令可列出使用指定付费墙的所有版位,方便你在替换前预览影响范围。[了解更多](developer-cli-reference#placements) - **西班牙语文档上线**:Adapty 文档现已提供西班牙语(Español)版本。可通过顶部导航栏的语言切换器进行切换。 ## 2026 年 4 月 \{#april-2026\} - **Adapty Mail**:AI 生成的电子邮件营销活动,帮助将试用用户转化为付费订阅者。在 Adapty 项目中直接构建、发送并追踪活动归因,无需额外的电子邮件平台。[了解更多](adapty-mail) - **付费墙诊断(Autopilot)**:在搭建测试前,先了解付费墙需要优化的地方。上传截图后,Autopilot 会根据同类头部应用的基准数据给出优化建议,并提供 AI 生成的布局和文案方案。其中基于基准数据的建议会作为 A/B 测试轮次加入你的增长计划。[了解更多](autopilot-analysis#paywall-analysis) - **每条 Autopilot 建议都更清晰易懂**:每个假设现在都会说明它为何重要(基于数据的解释,说明您的付费墙与既有模式的偏差)、需要更改什么以及如何设置 A/B 测试,以及在全新的"如何解读结果"部分中需要关注哪些数据图表。[了解更多](autopilot-execute-plan#step-1-view-the-hypothesis) - **保持 Autopilot 增长计划最新状态**:刷新分析以获取最新市场数据和新建议,如果新建议不符合预期,可从版本历史记录中查看过往建议。假设按以下标签分组:Top priority(最高优先级)、All(全部)、Pricing(定价)、Visual(视觉)、Geo-pricing(地区定价)和 Archived(已归档)。[了解更多](autopilot-growth-plan) - **Autopilot 中按时长划分的收入分布**:查看您的收入是否过度集中于某一订阅时长。全新的市场洞察数据图表会显示您按时长划分的收入构成,并附带您所在品类和国家的行业平均值。[了解更多](autopilot-analysis#revenue-distribution-by-duration) - **更新后的 LTV 与收入趋势预测**:预测 LTV 和收入现在会优先使用您应用自身的同期群留存数据(历史数据充足时),不足时则回退到跨应用平均值——这样即使是较新的应用,也能在数据分析和 A/B 测试中获得可用的预测结果。[了解更多](predicted-ltv-and-revenue) - **在 Adapty UA 中发送所有事件**:为 Meta 和 TikTok 提供更完整的转化数据,从而优化受众建模。Adapty 现在支持将来自自然流量和未归因用户的安装及交易数据转发到你的像素,而不仅限于匹配到广告系列的用户。[Meta](ua-facebook#send-all-events) | [TikTok](ua-tiktok#send-all-events) - **文档支持俄语和土耳其语**:Adapty 文档现已提供俄语(Русский)和土耳其语(Türkçe)版本。请使用顶部导航栏中的语言切换器来切换语言。 ## 2026 年 3 月 \{#march-2026\} - **开发者 CLI**:无需打开看板,直接在终端管理 Adapty 账号。CLI 支持创建应用、定义访问等级、配置产品、创建付费墙和配置版位——全程可脚本化,适合自动化环境使用。此外还提供 [Adapty CLI skill](https://github.com/adaptyteam/adapty-cli/tree/main/skills/adapty-cli),可帮助 AI 编程助手使用该 CLI。[了解更多](developer-cli) - **Apple Ads Manager 概览页面**:在同一页面查看所有关键 Apple Ads 数据图表,每项指标均附有趋势数据图表。通过页头下拉菜单按应用筛选,自定义显示的指标,并调整图表类型和收入展示方式。[了解更多](ads-manager-overview) - **Apple Ads Manager 中的市场情报**:查看竞争对手在 50 多个国家/地区投放广告的关键词,并将表现最佳的关键词直接添加到您的广告系列中。[了解更多](ads-manager-market-intelligence) - **Apple Ads Manager 中的全周期关键词自动化**:根据您定义的效果规则,自动调整出价、暂停或激活关键词,并在广告组之间移动关键词。[了解更多](ads-manager-automations-keyword-rules) - **Apple Ads Manager 中的出价历史记录**:查看任意关键词 CPT 出价的完整变更日志——包括每次变更的时间、变更前后的值,以及触发变更的自动化规则。[了解更多](ads-manager-manage-keywords#bid-history) - **Autopilot 中的视觉轮次**:付费墙设计建议现在已成为增长计划中的一等轮次——与变现轮次并列显示在侧边栏中。每个视觉轮次包含设计原型、最佳应用场景说明以及所针对的关键数据图表。[了解更多](autopilot-growth-plan#view-the-growth-plan) - **向 Autopilot 添加自定义假设**:通过自定义轮次扩展您的增长计划。添加标题、描述、轮次类型(货币化或视觉),设置目标指标——对于货币化轮次,还需指定所涉及的产品。[了解更多](autopilot-growth-plan#add-your-own-hypothesis) - **重新排列 Autopilot 轮次**:拖拽重新排列增长计划各阶段的顺序,按照最适合您策略的顺序运行实验。[了解更多](autopilot) - **Autopilot 中的地域定价轮次**:在增长计划中将特定国家的价格调整作为一种新型轮次进行测试。Autopilot 基于市场洞察数据,推荐每个国家应提价、降价还是维持现价。将推荐方案添加为地域定价轮次,即可以 A/B 测试形式运行——最多可同时运行 5 个。[了解更多](autopilot-growth-plan#geo-pricing-hypotheses) - **Apple Ads Manager 中的搜索词自动化**:自动将表现优异的搜索词提升为精确匹配关键词,并在来源处将其排除——无需手动下载报告。规则可以从模板创建,也可以通过自定义条件和计划从头构建。[了解更多](ads-manager-automations-search-terms) - **Apple Ads Manager 中的最大化转化出价策略**:创建广告系列时,您现在可以选择最大化转化作为出价策略。Apple 的算法会在您的预算范围内最大化下载量,并可设置可选的目标 CPA 进行指导。[了解更多](ads-manager-create-campaign) - **Adapty UA 中的 FunnelFox 集成**:Adapty UA 现已支持与 FunnelFox 的全新集成。[FunnelFox](ua-funnelfox) - **中文文档**:Adapty 文档现已提供中文版本。使用顶部导航栏中的语言选择器切换语言。 ## 2026 年 2 月 \{#february-2026\} - **按国家设置产品定价**:直接在 Adapty 看板中为各国设置不同的价格 —— Adapty 会自动将更改同步至 App Store Connect 和 Google Play。每次定价更新都会记录在审计日志中,确保每一次变更都有迹可查。[了解更多](edit-product) - **Autopilot 中的国家级竞品定价**:在主要市场中将您的订阅价格与竞品进行对比分析。[了解更多](autopilot-analysis#market-and-competitor-analysis) - **用户引导版本控制**:通过完整的版本历史记录追踪和管理用户引导的版本,随时查看变更并在需要时回滚。 - **分析中的付费墙转化数据图表**:两个新的转化数据图表——付费墙浏览 → 试用和付费墙浏览 → 付费——直观展示付费墙将浏览者转化为订阅用户的效果。[了解更多](analytics-conversion) - **复制市场细分**:复制现有市场细分及其所有筛选条件,无需从头重建类似的细分。适合同时运行多个营销活动或目标受众有重叠的 A/B 测试时使用。[了解更多](segments#duplicate-segments) - **Adapty 移动应用中的推送通知**:直接在 Adapty iOS 应用中为 14 种事件类型配置推送通知,无需打开看板即可随时掌握订阅动态。[了解更多](push-notifications) - **Kotlin Multiplatform SDK 3.15**:新增用户引导支持、网页付费墙及 API 改进。[了解更多](migration-to-kmp-315) - **Capacitor SDK 3.16**:新增 Capacitor 8 支持。使用 Capacitor 7 的项目请继续使用 SDK v3.15。[了解更多](migration-to-capacitor-316) - **LLM 辅助 SDK 集成指南**:借助 AI 编码助手完成 Adapty 集成的分步指南。每份指南引导你的 LLM 完成从看板配置到购买的完整实现流程。[iOS](adapty-cursor) | [Android](adapty-cursor-android) | [React Native](adapty-cursor-react-native) | [Flutter](adapty-cursor-flutter) | [Unity](adapty-cursor-unity) | [Kotlin Multiplatform](adapty-cursor-kmp) | [Capacitor](adapty-cursor-capacitor)。如需一键自动化流程,欢迎体验全新 **adapty-sdk-integration skill**(测试版):[iOS](adapty-sdk-integration-skill) | [Android](adapty-sdk-integration-skill-android) | [React Native](adapty-sdk-integration-skill-react-native) | [Flutter](adapty-sdk-integration-skill-flutter) | [Unity](adapty-sdk-integration-skill-unity) | [Kotlin Multiplatform](adapty-sdk-integration-skill-kmp) | [Capacitor](adapty-sdk-integration-skill-capacitor) ## 2026 年 1 月 \{#january-2026\} - **Capacitor SDK 正式发布**:经过大量测试,Capacitor SDK 现已可用于生产环境。借助完整的 Adapty 集成支持,使用 Capacitor 为 iOS 和 Android 构建订阅类应用。[了解更多](capacitor-sdk-overview) - **新应用的 Autopilot 功能**:即使你的应用尚无大量交易记录,现在也可以使用 Autopilot 分析功能。从第一天起就获取数据驱动的价格优化建议,制定增长计划。[了解更多](autopilot) - **Autopilot 全球定价机会**:通过针对特定国家的定价建议,挖掘核心市场的收入潜力。Autopilot 会分析您下一批前 5 个国家的转化率和购买力,基于 Adapty 定价指数提供数据驱动的建议,帮助您判断是否应提高、降低或维持现有价格。[了解更多](autopilot) - **账单恢复转化数据图表**:新增数据图表,用于追踪从账单问题和宽限期中恢复的收入。通过监控"Billing issue converted"、"Billing issue converted revenue"、"Grace period converted"和"Grace period converted revenue",衡量您的留存恢复效果。 - **在 Apple Ads Manager 中直接管理广告**:无需在平台之间切换,直接在 Adapty 中创建和管理 Apple Ads 广告活动。[了解更多](ads-manager-manage-ads) - **Apple Ads Manager 数据分析**:在 Adapty 中查看详细的广告级别效果指标和归因数据,在统一看板中浏览推广活动表现、广告组数据分析及归因洞察。[了解更多](adapty-ads-manager-analytics) - **Apple Ads 归因数据图表**:将多个归因指标组合成可自定义的数据图表,结合订阅数据全面分析 Apple Ads 的投放效果。[了解更多](adapty-ads-manager-analytics#charts) - **Apple Ads 归因市场细分**:通过简化的两步操作,基于 Apple Ads 归因数据创建用户市场细分。按广告系列、广告组或关键词定向用户,实现更精准的分析与实验。[了解更多](ads-manager-create-segments) - **全新文档平台**:文档站点已迁移至全新平台,带来更快的功能迭代,并通过增强的搜索、导航和内容组织提升用户体验。 ## 2025年12月 \{#december-2025\} - **Apple Ads Manager 文档**:在统一的分析看板中整合 Apple Search Ads 广告活动数据与收入指标。新文档涵盖广告活动创建、广告组管理,以及如何结合订阅表现追踪广告支出的投资回报率。[了解更多](ads-manager) - **应用内网页付费墙**:通过应用内浏览器在应用中展示基于网页的付费墙,无需外部跳转,带来流畅的用户体验。[iOS](ios-web-paywall#open-web-paywalls-in-an-in-app-browser) | [Android](android-web-paywall#open-web-paywalls-in-an-in-app-browser) | [React Native](react-native-web-paywall#open-web-paywalls-in-an-in-app-browser) | [Flutter](flutter-web-paywall#open-web-paywalls-in-an-in-app-browser) - **滚动市场细分**:创建动态目标受众市场细分,根据移动时间窗口自动更新。例如,创建一个"过去 7 天内安装应用的用户"细分,持续刷新以始终显示最新客户。[了解更多](segments#available-attributes) - **Meta 和 TikTok 广告系列设置指南**:在 Meta(Facebook & Instagram)和 TikTok 上创建和追踪广告系列的分步文档,包含转化追踪和数据分析集成。[Meta](meta-create-campaign) | [TikTok](tiktok-create-campaign) - **手动付费墙实现快速入门指南**:通过分步快速入门指南,了解如何将 Adapty SDK 集成到自定义付费墙 UI 中,从而更快地实现应用内购买。[iOS](ios-implement-paywalls-manually) | [Android](android-implement-paywalls-manually) | [React Native](react-native-implement-paywalls-manually) | [Flutter](flutter-implement-paywalls-manually) | [Unity](unity-implement-paywalls-manually) | [Kotlin Multiplatform](kmp-quickstart-manual) | [Capacitor](capacitor-quickstart-manual) - **用户引导链接的应用内浏览器**:用户引导中的外部链接现在默认在应用内浏览器中打开,让用户留在您的应用中。如有需要,您也可以自定义此行为,改用外部浏览器。[iOS](ios-present-onboardings#customize-how-links-open-in-onboardings) | [Android](android-present-onboardings#customize-how-links-open-in-onboardings) | [React Native](react-native-present-onboardings#customize-how-links-open-in-onboardings) - **改进的 Autopilot 建议**:Autopilot 现在基于对订阅数据的深入分析,提供更优质的价格优化建议。[立即体验 Autopilot](autopilot) - **文档深色模式**:文档现已支持深色模式,可自动检测系统偏好设置,也可通过右上角的手动开关切换。 --- # File: adapty-ecosystem --- --- title: "Adapty 生态系统" description: "Adapty 是面向移动应用的应用内购买平台。了解各产品的功能及其相互关联。" --- Adapty 是面向移动应用的应用内购买平台,围绕一个核心使命而生:让应用创造更多收益。它为你提供增长营收所需的一切:获取用户、促成转化、留住订阅者,并赢回流失用户。 注册后即可立即访问完整的 Adapty 生态系统。只需点击 Adapty 徽标即可在各产品之间切换: - **Core** — 无需直接操作 StoreKit 或 Google Play Billing 即可处理购买,使用无代码工具设计付费墙,并实时追踪收入。其他产品均以此为基础构建。 - **Adapty Ads Manager** — 投放和优化 Apple Ads 广告,并以真实的订阅收入数据衡量效果。 - **Adapty Attribution** — 直观了解哪些广告渠道真正带来了收入,无需 MMP。 - **Adapty Mail** — 通过自动化邮件转化试用用户,并赢回流失用户。 还有两款产品与核心四款并列:**FunnelFox**(网页到应用的转化漏斗及托管结账)和 **Adapty Finance**(基于未来订阅收入的预付融资)。 ## 产品如何协同工作 \{#how-the-products-fit-together\} 每个产品在用户生命周期的不同节点发挥作用。将鼠标悬停在任意链接功能上可查看简要定义,或点击跳转至对应文档。
3. 在打开的 **Generate In-App Purchase Key** 窗口中,输入密钥名称以便日后参考。该名称不会在 Adapty 中使用。
4. 点击 **Generate** 按钮。**Generate in-App Purchase Key** 窗口关闭后,您将在 **Active** 列表中看到已创建的密钥。
5. 生成 API 密钥后,点击 **Download In-App Purchase Key** 按钮,将密钥以文件形式下载。
6. 在 **Download in-App Purchase Key** 窗口中,点击 **Download** 按钮。文件将保存到您的计算机。
请务必妥善保管此文件,以便日后上传到 Adapty 看板。请注意,生成的文件只能下载一次,因此在上传之前请确保安全存储。从 **In-App Purchase section** 生成的 .p8 密钥将在[配置 Adapty 与 App Store 的初始集成](app-store-connection-configuration#step-3-upload-in-app-purchase-key-file)时使用。
**下一步:**
- [配置 App Store 集成](app-store-connection-configuration)
---
# File: app-store-connection-configuration
---
---
title: "配置 App Store 集成"
description: "配置您的 App Store 连接,实现无缝的订阅追踪。"
---
3. 复制 **Issuer ID**,并将其粘贴到 Adapty 看板中的 **In-app purchase Issuer ID** 字段。
4. 复制 **Key ID**,并将其粘贴到 Adapty 看板的 **In-app purchase Key ID** 字段中。
## 步骤 3. 上传应用内购买密钥文件 \{#step-3-upload-in-app-purchase-key-file\}
将你在[在 App Store Connect 中生成应用内购买密钥](generate-in-app-purchase-key)章节中下载的 **In-App Purchase Key** 文件
上传到 Adapty 看板中的 **Private key (.p8 file)** 字段。
## 第四步:针对试用期和特殊优惠——配置促销活动 \{#step-4-for-trials-and-special-offers--set-up-promotional-offers\}
:::important
如果你的应用包含[试用期或其他促销活动](offers),此步骤为必填项。
:::
1. 将你在[第二步](#step-2-provide-issuer-id-and-key-id)中使用的同一个 Key ID 复制到 **App Store promotional offers** 部分的 **Subscription key ID** 字段中。
2. 将你在[第三步](#step-3-upload-in-app-purchase-key-file)中使用的同一个 **In-App Purchase Key** 文件上传到 **App Store promotional offers** 部分的 **Subscription key (.p8 file)** 区域。
## 第 5 步:输入 App Store 共享密钥 \{#step-5-enter-app-store-shared-secret\}
**App Store shared secret**(即 App Store Connect Shared Secret)是一个 32 位十六进制字符串,用于应用内购买和订阅收据验证。
1. 打开 [App Store Connect](https://appstoreconnect.apple.com/apps),选择您的应用,进入 **General** → **App Information** 页面。
2. 向下滚动,找到 **App-Specific Shared Secret** 子板块。
:::info
如果 **App-Specific Shared Secret** 子部分未显示,请确认您拥有 Account Holder 或 Admin 角色。如果您已具有 Admin 角色但仍看不到 **App-Specific Shared Secret** 子部分,请联系该应用的 Account Holder(即在 App Store Connect 中创建该应用的人),让其为该应用生成 App Store shared secret。生成后,Admin 也可以看到该子部分。
:::
3. 点击 **Manage** 按钮。
4. 在打开的 **App-Specific Shared Secret** 窗口中,复制 **Shared Secret**。如果没有看到共享密钥,请先点击可用的 **Manage** 或 **Generate** 按钮,然后再复制 **Shared Secret**。
5. 将复制的 **Shared Secret** 粘贴到 Adapty 看板中的 **App Store shared secret** 字段。
6. 点击 Adapty 看板中的 **Save** 按钮确认更改。
## 第六步:添加 App Store Connect API 密钥 \{#step-6-add-app-store-connect-api-key\}
生成 App Store Connect API 密钥并添加到 Adapty,即可[在 Adapty 看板中管理 App Store 产品](create-product#create-product-and-push-to-store):
1. 在 App Store Connect 中,前往 [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api),点击 **+**。
2. 在 **Generate API key window** 中,为密钥输入名称并授予其 **Admin** 权限。
3. 点击密钥旁边的 **Download**。请注意,该密钥只能下载一次。
4. 在 Adapty 看板中,前往 [**App settings > iOS SDK**](https://app.adapty.io/settings/ios-sdk),然后点击 **Connect API key**。
5. 在弹窗中填写以下字段:
- **Issuer ID**:从 [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api) 复制。它位于 **API keys** 表格上方。
- **Key ID**:从 [**Users and Access > Integrations > Team keys**](https://appstoreconnect.apple.com/access/integrations/api) 复制。它在 **API keys** 表格中,位于您的密钥旁边。
- **API key**:上传你从 App Store Connect 下载的 API 密钥文件。
6. 点击 **Connect**。
**下一步**
- [启用 App Store 服务器通知](enable-app-store-server-notifications)
---
# File: enable-app-store-server-notifications
---
---
title: "启用 App Store 服务器通知"
description: "启用 App Store 服务器通知,实时追踪订阅事件。"
---
设置 App Store 服务器通知对于确保数据准确性至关重要,它能让你即时接收来自 App Store 的更新,包括退款及其他事件信息。
:::important
完整支持 App Store Server Notifications V2 需要 Adapty iOS SDK 2.10.0 或更高版本。
:::
1. 在 Adapty 看板中复制 **URL for App Store server notification**。
2. 打开 [App Store Connect](https://appstoreconnect.apple.com/apps)。选择您的应用,进入 **General** → **App Information** 部分下的 **App Store Server Notifications** 子部分。
3. 将复制的 **URL for App Store server notification** 粘贴到 **Production Server URL** 和 **Sandbox Server URL** 字段中。
## 原始事件转发 \{#raw-events-forwarding\}
有时,您可能仍希望直接接收来自 Apple 的原始 S2S 事件。若要在使用 Adapty 的同时继续接收这些事件,只需将您的端点添加到 **URL for forwarding raw Apple events** 字段中,我们将原封不动地转发来自 Apple 的原始事件。
**下一步**
为以下平台配置 Adapty SDK:
- [iOS](sdk-installation-ios)
- [React Native](sdk-installation-reactnative)
- [Flutter](sdk-installation-flutter)
- [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)
- [Unity](sdk-installation-unity)
---
# File: troubleshoot-app-store-integration
---
---
title: "排查 App Store 集成问题"
description: "解决常见的 Apple App Store 配置问题——协议待处理、服务器通知延迟以及价格不匹配等情况。"
---
本文介绍常见的 App Store 集成问题,每个部分均包含症状描述、根本原因及解决方案。
## 产品未显示 \{#products-dont-appear\}
以下两种表现通常指向同一个根本原因:
- App Store Connect API 密钥配置正确,但 Adapty 无法获取任何产品。
- 产品已在 App Store Connect 中创建,但未出现在 Adapty 中,或显示数量少于预期。SDK 在尝试购买时报告 "Product Id not found"。
最常见的根本原因是 **Apple 协议未签署** — 付款协议、税务表格或银行表格处于待处理或未签署状态。当协议处于待处理状态时,App Store Connect API 会在产品相关端点静默返回 403 错误。Adapty 不会收到任何明确的报错提示,产品会被静默过滤掉。
请前往 **App Store Connect → Agreements, Tax, and Banking**,签署所有待处理的协议。然后在 Adapty 的 **App settings → iOS SDK** 中重新同步。
## App Store 服务器通知显示"Delayed" \{#app-store-server-notifications-show-delayed\}
在 App Store Connect 中,App Store 服务器通知的状态可能会显示为 **Delayed**。这意味着 Apple 在发送订阅事件通知方面出现了延迟——续订、取消和账单问题等通知会排队等待,并延迟到达。
安装统计数据不受影响。Adapty 从应用首次启动开始统计安装量,而非依赖服务器端通知。
如果续订或取消数据出现滞后,**Delayed** 状态是最可能的原因。随着 Apple 处理积压的通知,该状态通常会自动恢复正常。
## Adapty 中的价格与 App Store 不匹配 \{#prices-in-adapty-dont-match-app-store\}
Adapty 产品编辑页面上的**价格**字段的行为方式取决于产品的添加方式。
如果你在 Adapty 中创建产品并从看板推送到商店,该价格将作为商店的初始价格使用。
如果你添加的产品在商店中已存在,此价格仅作为占位符使用。Adapty 的分析、集成和 SDK 均以从 App Store 实际获取的价格为准,而非该占位符。App Store 价格发生变更后不会同步更新占位符,且目前无法在看板中手动编辑占位符。
## CSV 价格导出为空 \{#csv-price-export-is-empty\}
如果你导出的 CSV 价格文件只有列标题,说明 App Store Connect API 密钥未完成配置。请参阅[第 6 步 — 添加 App Store Connect API 密钥](app-store-connection-configuration#step-6-add-app-store-connect-api-key)。
## 无法将新产品推送至 App Store \{#cant-push-new-products-to-app-store\}
当你在看板中创建产品时,Adapty 可以将新产品推送至 App Store Connect。如果你的 App Store 集成尚未完整配置,推送选项将被禁用。以下两项设置为必填项:
- **Apple app ID**:在 [第 1 步 — 提供 Bundle ID 和 Apple app ID](app-store-connection-configuration#step-1-provide-bundle-id-and-apple-app-id) 中进行配置。
- **App Store Connect API 密钥**:在 [第 6 步 — 添加 App Store Connect API 密钥](app-store-connection-configuration#step-6-add-app-store-connect-api-key) 中进行配置。
---
# File: enabling-of-devepoler-api
---
---
title: "在 Google Play Console 中启用开发者 API"
description: "启用 Adapty 的开发者 API,以便在您的应用中自动化并简化订阅管理。"
---
3. 打开 [**Google Play Android Developer API**](https://console.cloud.google.com/apis/library/androidpublisher.googleapis.com) 页面。
4. 点击 **Enable** 按钮,等待状态显示为 **Enabled**。这表示 Google Android Developer API 已启用。
5. 打开 [**Google Play Developer Reporting API**](https://console.cloud.google.com/apis/library/playdeveloperreporting.googleapis.com) 页面。
6. 点击 **Enable** 按钮,等待状态显示为 **Enabled**。
7. 打开 [**Cloud Pub/Sub API**](https://console.cloud.google.com/marketplace/product/google/pubsub.googleapis.com) 页面。
8. 点击 **Enable** 按钮,等待状态显示为 **Enabled**。
开发者 API 已启用。
您可以在 Google Cloud Console 的 [**APIs & Services**](https://console.cloud.google.com/apis/dashboard) 页面上重新确认。向下滚动页面,验证页面底部的表格中包含以下所有 3 个 API:
- Google Play Android Developer API
- Google Play Developer Reporting API
- Cloud Pub/Sub API
**下一步**
- [在 Google Cloud Console 中创建服务账号](create-service-account)
---
# File: create-service-account
---
---
title: "在 Google Cloud Console 中创建服务账号"
description: "了解如何在 Adapty 中为安全 API 访问创建服务账号。"
---
为了让 Adapty 自动化数据访问,需要在 Google Play Console 中创建一个服务账号。
1. 打开 Google Cloud Console 的 [**IAM & Admin** -> **Service accounts**](https://console.cloud.google.com/iam-admin/serviceaccounts) 部分。请确保您使用的是正确的项目。
2. 在 **Service accounts** 窗口中,点击 **Create service account** 按钮。
3. 在 **Create service account** 窗口的 **Service account details** 子部分中,输入您想要的 **Service Account Name**。我们建议在名称中包含"Adapty",以说明该账号的用途。**Service account ID** 将自动生成。
4. 复制服务账号的电子邮件地址并保存,以备将来使用。
5. 点击 **Create and continue** 按钮。
6. 在 **Grant this service account access to project** 子部分的 **Select a role** 下拉列表中,选择 **Pub/Sub -> Pub/Sub Admin**。启用实时开发者通知需要此角色。
7. 点击 **Add another role** 按钮。
8. 在新的 **Role** 下拉列表中,选择 **Monitoring -> Monitoring Viewer**。允许监控通知队列需要此角色。
9. 点击 **Continue** 按钮。
10. 无需任何更改,直接点击 **Done** 按钮。**Service accounts** 窗口将打开。
**下一步**
- [在 Google Play Console 中为服务账号授予权限](grant-permissions-to-service-account)
---
# File: grant-permissions-to-service-account
---
---
title: "在 Google Play Console 中授予服务账号权限"
description: "为服务账号授予权限,以实现安全高效的 API 访问。"
---
授予 Adapty 将用于管理订阅和验证购买的服务账号所需权限。
1. 在 Google Play Console 中打开 [**Users and permissions**](https://play.google.com/console/u/0/developers/8970033217728091060/users-and-permissions) 页面,然后点击 **Invite new users** 按钮。
2. 在 **Invite user** 页面中,输入您已创建的服务用户的电子邮件地址。
3. 切换到 **Account permissions** 选项卡。
4. 选择以下权限:
- View app information and download bulk reports (read-only)
- View financial data, orders, and cancellation survey responses
- Manage orders and subscriptions
- Manage store presence
5. 点击 **Invite user** 按钮。
6. 在 **Send invite?** 窗口中,点击 **Send invite** 按钮。服务账号将显示在用户列表中。
**下一步**
- [在 Google Play Console 中生成服务账号密钥文件](create-service-account-key-file)
---
# File: create-service-account-key-file
---
---
title: "在 Google Play Console 中生成服务账号密钥文件"
description: "了解如何创建服务账号密钥文件,以便与 Adapty 无缝集成。"
---
要将您在 Play Store 上的移动应用与 Adapty 关联,您需要在 Google Play Console 中生成专用服务账号密钥文件,并将其上传到 Adapty。这些文件有助于保护您的应用并防止未经授权的访问。
:::warning
新服务账号通常需要至少 24 小时才能激活。不过,有一个[技巧](https://stackoverflow.com/a/60691844)可以加速此过程。在 [Google Play Console](https://play.google.com/apps/publish/) 中创建服务账号后,打开任意一个应用,导航至 **Monetize** -> **Products** -> **Subscriptions/In-app products**。编辑任意产品的描述并保存更改。这样应该可以立即激活服务账号,之后您可以将更改恢复原状。
:::
1. 在 Google Play Console 中打开 [**Service accounts**](https://console.cloud.google.com/iam-admin/serviceaccounts) 部分。请确保您已选择了正确的项目。
2. 在弹出的窗口中,点击 **Add key** 并从下拉菜单中选择 **Create new key**。
3. 在 **Create private key for [Your_project_name]** 窗口中,点击 **Create**。您的私钥将以 JSON 文件的形式保存到您的计算机。您可以通过 **Private key saved to your computer** 窗口中提供的文件名来找到该文件。
4. 在 **Create private key for Your_project_name** 窗口中,点击 **Create** 按钮。此操作会将您的私钥以 JSON 文件的形式保存到您的计算机。如有需要,您可以使用弹出的 **Private key saved to your computer** 窗口中提供的文件名来定位该文件。
在[配置 Google Play Store 集成](google-play-store-connection-configuration)时,您将需要使用此文件。
:::warning
新服务账号通常需要至少 24 小时才能激活。不过,有一个[技巧](https://stackoverflow.com/a/60691844)可以加速此过程。在 [Google Play Console](https://play.google.com/apps/publish/) 中创建服务账号后,打开任意一个应用,导航至 **Monetize** -> **Products** -> **Subscriptions/In-app products**。编辑任意产品的描述并保存更改。这样应该可以立即激活服务账号,之后您可以将更改恢复原状。
:::
**下一步**
- [配置 Google Play Store 集成](google-play-store-connection-configuration)
---
# File: google-play-store-connection-configuration
---
---
title: "配置 Google Play 商店集成"
description: "在 Adapty 中配置 Google Play 商店连接,以顺畅处理应用内购买。"
---
本节介绍通过 Google Play 销售的移动应用与 Adapty 的集成流程。您需要将应用在 Play 商店中的配置数据填写到 Adapty 看板中。此步骤对于在 Adapty 中验证购买及接收来自 Play 商店的订阅更新至关重要。
您可以在初始用户引导期间完成此流程,也可以稍后在 Adapty 看板的 **App Settings** 中进行修改。
:::danger
配置更改仅应在您发布集成了 Adapty 付费墙的移动应用之前进行。发布后进行更改将导致集成中断,付费墙将无法在您的移动应用中显示。
:::
## 步骤 1. 提供包名 \{#step-1-provide-package-name\}
包名是您的应用在 Google Play 商店中的唯一标识符。这是 Adapty 基本功能(如订阅处理)所必需的。
1. 打开 [Google Play 开发者控制台](https://play.google.com/console/u/0/developers)。
2. 选择您需要获取 ID 的应用,**Dashboard** 窗口将会打开。
3. 在应用名称下方找到产品 ID 并复制。
4. 从 Adapty 顶部菜单打开 [**App settings**](https://app.adapty.io/settings/android-sdk)。
5. 在 **App settings** 窗口的 **Android SDK** 标签页中,粘贴已复制的 **Package name**。
## 步骤 2. 上传账号密钥文件 \{#step-2-upload-the-account-key-file\}
1. 将您在[创建服务账号密钥文件](create-service-account)步骤中创建的 JSON 格式服务账号私钥文件上传到 **Service account key file** 区域。
请不要忘记点击 **Save** 按钮以确认更改。
**下一步**
- [在 Google Play 控制台中启用实时开发者通知(RTDN)](enable-real-time-developer-notifications-rtdn)
---
# File: enable-real-time-developer-notifications-rtdn
---
---
title: "在 Google Play Console 中启用实时开发者通知 (RTDN)"
description: "通过在 Google Play Console 中为 Adapty 启用实时开发者通知 (RTDN),及时了解关键事件并保持数据准确性。了解如何设置 RTDN 以接收来自 Play Store 的退款及其他重要事件的即时更新"
---
设置实时开发者通知 (RTDN) 对于确保数据准确性至关重要,它能让您即时接收来自 Play Store 的更新,包括退款及其他事件的信息。
## 启用通知 \{#enable-notifications\}
1. 确保已启用 **Google Cloud Pub/Sub**。打开[此链接](https://console.cloud.google.com/flows/enableapi?apiid=pubsub)并选择您的应用项目。如果尚未启用 **Google Cloud Pub/Sub**,请在此处启用。
2. 从 Adapty 顶部菜单进入 [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk),复制 **Google Play RTDN topic name** 标题旁 **Enable Pub/Sub API** 字段的内容。
:::note 如果 **Enable Pub/Sub API** 字段的内容格式不正确(正确格式以 `projects/...` 开头),请参阅[修复 Enable Pub/Sub API 字段格式错误](enable-real-time-developer-notifications-rtdn#fixing-incorrect-format-in-enable-pubsub-api-field)部分获取帮助。 ::: 3. 打开 [Google Play Console](https://play.google.com/console/),选择您的应用,然后前往 **Monetize with Play** -> **Monetization setup**。在 **Google Play Billing** 部分,勾选 **Enable real-time notifications** 复选框。 4. 将您在 Adapty **App Settings** 中复制的 **Enable Pub/Sub API** 字段内容粘贴到 **Topic name** 字段中。 5. 在 Google Play Console 中点击 **Save changes**。
## 测试通知 \{#test-notifications\}
要验证您是否已成功订阅实时开发者通知:
1. 在 Google Play Console 设置中保存更改。
2. 在 Google Play Console 的 **Topic name** 下方,点击 **Send test notification**。
3. 在 Adapty 中进入 [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk)。如果测试通知已发送,您将在主题名称上方看到其状态。
## 修复 Enable Pub/Sub API 字段格式错误 \{#fixing-incorrect-format-in-enable-pubsub-api-field\}
如果 **Enable Pub/Sub API** 字段的内容格式不正确(正确格式以 `projects/...` 开头),请按以下步骤排查并解决问题:
### 1. 验证 API 启用状态与权限 \{#1-verify-api-enablement-and-permissions\}
请仔细确认所有必需的 API 已启用,且权限已正确授予服务账号。即使您已完成这些步骤,也请再次逐一核查,确保没有遗漏任何子步骤。请重复以下各节中的步骤:
1. [在 Google Play Console 中启用开发者 API](enabling-of-devepoler-api)
2. [在 Google Cloud Console 中创建服务账号](create-service-account)
3. [在 Google Play Console 中授予服务账号权限](grant-permissions-to-service-account)
4. [在 Google Play Console 中生成服务账号密钥文件](create-service-account-key-file)
5. [配置 Google Play Store 集成](google-play-store-connection-configuration)
### 2. 调整域策略 \{#2-adjust-domain-policies\}
更改 **Domain restricted contacts** 和 **Domain restricted sharing** 策略:
1. 打开 [Google Cloud Console](https://console.cloud.google.com/),选择您用于管理应用的服务账号所在的项目。
2. 在 **Quick Access** 部分,选择 **IAM & Admin**。
3. 在左侧面板中,选择 **Organization Policies**。
4. 找到 **Domain restricted contacts** 策略。
5. 点击 **Actions** 列中的省略号按钮,选择 **Edit policy**。
6. 在策略编辑窗口中:
1. 在 **Policy source** 下,选择 **Override parent's policy** 单选按钮。
2. 在 **Policy enforcement** 下,选择 **Replace** 单选按钮。
3. 在 **Rules** 下,点击 **ADD A RULE** 按钮。
4. 在 **New rule** -> **Policy values** 下,选择 **Allow All**。
5. 点击 **SET POLICY**。
7. 对 **Domain restricted sharing** 策略重复步骤 4-6。
最后,重新生成 **Google Play RTDN topic name** 标题旁 **Enable Pub/Sub API** 字段的内容。该字段现在将显示正确的格式。
成功启用实时开发者通知 (RTDN) 后,请务必将已更新策略的 **Policy source** 切换回 **Inherit parent's policy**。
## 原始事件转发 \{#raw-events-forwarding\}
有时,您可能仍希望接收来自 Google 的原始 S2S 事件。如需在使用 Adapty 的同时继续接收这些事件,只需将您的端点添加到 **URL for forwarding raw Google events** 字段,我们将原样转发来自 Google 的原始事件。
---
**下一步**
为以下平台配置 Adapty SDK:
- [Android](sdk-installation-android)
- [React Native](sdk-installation-reactnative)
- [Flutter](sdk-installation-flutter)
- [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)
- [Unity](sdk-installation-unity)
---
# File: stripe
---
---
title: "与 Stripe 的初始集成"
description: "将 Stripe 与 Adapty 集成,实现无缝的订阅支付处理。"
---
Adapty 通过追踪通过 [Stripe](https://stripe.com/) 完成的网页支付和订阅,支持 web2app 订阅流程。
此集成涵盖网页端发起的购买(Stripe Checkout、托管支付页面或自定义网页流程),并将其与移动应用的访问权限和数据分析进行同步。
适用于以下场景:
- 为在网页端完成购买、之后安装应用并登录账户的用户自动开通付费功能
- 在单一 Adapty 看板中查看所有订阅分析数据(包括同期群、趋势预测及其他分析工具)
尽管网页端购买在应用中越来越普遍,但 Apple App Store 目前仅允许美国地区对数字商品采用应用内购买以外的支付方式。请确保不要在其他国家的应用内推广您的网页订阅,否则应用可能会被拒审或下架。
以下步骤介绍如何配置 Stripe 集成。
:::important
本集成的重点是追踪和同步 Stripe 网页端购买。如果您需要将用户从应用引导至网页结算页面,请参阅[网页付费墙](web-paywall)。
:::
## 1\. 将 Stripe 连接到 Adapty \{#1-connect-stripe-to-adapty\}
此集成主要依靠 Adapty 通过 webhook 从 Stripe 拉取订阅数据。因此,您需要提供 API 密钥,并在 Stripe 中使用 Adapty 的 webhook URL,将您的 Adapty 账户与 Stripe 账户关联起来。为自动配置 webhook,请在 Stripe 中安装 Adapty 应用:
:::note
以下步骤对 Stripe 的生产模式和测试模式均适用,但每种模式需要使用不同的 API 密钥。
:::
0. 确认您是以测试模式还是正式模式连接 Stripe。如果您最初在测试模式下操作,之后还需要对正式模式重复以下步骤。
1. 前往 [Stripe 应用市场](https://marketplace.stripe.com/apps/adapty) 安装 Adapty 应用。请注意,沙盒模式不支持安装应用,只能在生产模式或测试模式下安装。
2. 授予应用所需权限,这将允许 Adapty 访问订阅数据和历史记录。然后点击 **Continue to app settings** 继续。
在权限弹窗底部,您可以选择以正式模式还是测试模式安装应用。
3. 在弹窗中生成一个新的受限密钥。您需要通过邮件、Touch ID 或安全密钥验证身份。密钥生成后将无法再次查看,请将其安全存储在密码管理器或密钥存储中。
4. 从弹窗中复制生成的密钥,然后前往 Adapty 的 [App Settings → Stripe](https://app.adapty.io/settings/stripe)。根据您的模式,将密钥粘贴到 **Stripe App Restricted API Key** 对应区域。请注意,测试模式和正式模式需要生成不同的密钥。
大功告成!接下来,在 Stripe 中创建产品并将其添加到 Adapty。
2. 点击 **Secret key** 旁边的 **Reveal live (test) key button**,复制密钥后前往 Adapty 的 [App Settings → Stripe](https://app.adapty.io/settings/stripe),将密钥粘贴到此处:
3. 接下来,从 Adapty 同一页面底部复制 Webhook URL。在 Stripe 中前往 [**Developers** → **Webhooks**](https://dashboard.stripe.com/webhooks),点击 **Add endpoint** 按钮:
4. 将 Adapty 的 webhook URL 粘贴到 **Endpoint URL** 字段中。然后在 webhook 的 **Version** 字段中选择 **Latest API version**,并选择以下事件:
- charge.refunded
- customer.subscription.created
- customer.subscription.deleted
- customer.subscription.paused
- customer.subscription.resumed
- customer.subscription.updated
- invoice.created
- invoice.updated
- payment_intent.succeeded
5. 点击"Add endpoint",然后在"Signing secret"下点击"Reveal"。这是用于在 Adapty 端解码 webhook 数据的密钥,显示后请复制:
6. 最后,将此密钥粘贴到 Adapty 的 App Settings → Stripe 中的"Stripe Webhook Secret"字段:
:::warning
目前 Adapty 仅支持**固定价格**($9.99/月)或**打包定价**($9.99/10 个单位),因为这两种方式与应用商店的行为类似。**阶梯定价**、**基于用量的收费**和**客户自定义价格**选项目前不受支持。
:::
## 3\. 将 Stripe 产品添加到 Adapty \{#3-add-stripe-products-to-adapty\}
:::warning
产品是必须配置的!请务必在 Adapty 看板中创建您的 Stripe 产品。Adapty 仅追踪与这些产品关联的交易事件,请不要跳过此步骤——否则交易事件将无法创建。
:::
我们对待 Stripe 的方式与 App Store 和 Google Play 相同:它只是您销售数字产品的另一个渠道,配置方式也类似。只需将 Stripe 产品(即其 `product_id` 和 `price_id`)添加到 Adapty 的产品区域即可:
Stripe 中的产品 ID 格式为 `prod_...`,价格 ID 格式为 `price_...`。在 Stripe 的[产品目录](https://dashboard.stripe.com/products?active=true)中打开任意产品,即可轻松找到这些信息:
添加完所有必要产品后,下一步是告知 Stripe 哪位用户正在完成购买,以便 Adapty 能够识别!
## 4\. 在网页端购买中附加用户 ID \{#4-enrich-purchases-made-on-the-web-with-your-user-id\}
Adapty 依赖来自 Stripe 的 webhook 作为唯一数据来源,用于为用户提供和更新访问等级。但在使用 Stripe 时,您需要从您这端提供额外信息,才能确保集成正常运行。
为了让访问等级在各平台(网页或移动端)保持一致,您需要确保使用单一用户 ID,让 Adapty 能够通过 webhook 识别该用户。这可以是用户的邮箱、手机号,或您所使用的授权系统中的任意其他 ID。
确定您希望用于识别用户的 ID。然后在代码中找到通过 Stripe 初始化支付的部分,并将该用户 ID 以 `customer_user_id` 为键添加到 [Stripe Subscription](https://docs.stripe.com/api/subscriptions/object#subscription_object-metadata)(`sub_...`)或 [Checkout Session](https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-metadata)(`ses_...`)对象的 `metadata` 字段中,如下所示:
```json showLineNumbers title="Stripe Metadata contents"
{'customer_user_id': "YOUR_USER_ID"}
```
这一简单的改动是您在代码层面唯一需要做的事情。之后,Adapty 会解析从 Stripe 接收到的所有 webhook,提取该 `metadata`,并将订阅正确关联到您的客户。
:::warning
用户 ID 是必填项
否则,我们将无法匹配该用户并在移动端为其授予访问等级。
如果您没有在 `metadata` 中提供 `customer_user_id`,可以选择让 Adapty 在其他位置查找 `customer_user_id`:要么使用 Stripe 客户对象中的 `email`,要么使用 Stripe Session 中的 `client_reference_id`。
了解更多关于配置用户画像创建行为的信息,请参阅[下方内容](stripe#profile-creation-behavior)。
:::
:::note
Stripe 中的 Customer 也是必需的
如果您使用 Checkout Sessions,请[确保创建了 Stripe Customer](https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-customer_creation),将 `customer_creation` 设置为 `always`。
:::
## 5\. 为移动端用户开通访问权限 \{#5-provide-access-to-users-on-the-mobile\}
为确保从网页端进入的移动用户能够访问付费功能,只需使用与上一步相同的 `customer_user_id` 调用 `Adapty.activate()` 或 `Adapty.identify()`(详情请参阅
2. 为密钥命名并设置过期日期。要让 API 密钥与 Adapty 配合使用,需要为所有实体授予 **Read** 权限。点击 **Save**。
3. 点击 **Copy key**。
4. 在 Adapty 中,前往 [App Settings → Paddle](https://app.adapty.io/settings/paddle),将密钥粘贴到 **Paddle API key** 部分。
:::warning
如果你为 Paddle API 密钥设置了过期日期,必须在到期前手动生成新密钥并在 Adapty 中更新。密钥过期后,集成将无任何警告地停止工作,用户将无法完成购买。
:::
### 1.2. 添加将发送到 Adapty 的事件 \{#add-events-that-will-be-sent-to-adapty\}
1. 从 Adapty 中同一个 **Paddle** 页面复制 **Webhook URL**。
2. 在 Paddle 中,前往 [**Developer Tools → Notifications**](https://vendors.paddle.com/notifications-v2),然后点击 **New destination** 添加 webhook。
3. 为该 webhook 输入一个描述性名称。建议在名称中包含"Adapty",方便日后查找。
4. 将 Adapty 中的 **Webhook URL** 粘贴到 **URL** 字段。请确保使用的是正确环境的 webhook。
5. 将 **Notification type** 设置为 **Webhook**。
6. 选择以下事件:
- `subscription.created`
- `subscription.updated`
- `transaction.created`
- `transaction.updated`
- `adjustment.created`
- `adjustment.updated`
7. 点击 **Save destination** 完成 Webhook 设置。
### 1.3. 获取并添加 Webhook 密钥 \{#retrieve-and-add-the-webhook-secret-key\}
1. 在 **Notifications** 窗口中,点击刚刚创建的 Webhook 旁边的三个点,选择 **Edit destination**。
2. **Edit destination** 面板中会出现一个名为 **Secret key** 的新字段,复制它。
3. 在 Adapty 中,前往 [App Settings → Paddle](https://app.adapty.io/settings/paddle),将密钥粘贴到 **Notification secret key** 字段中。Adapty 将使用该密钥验证 webhook 数据。
### 1.4. 将 Paddle 客户与 Adapty 用户画像关联 \{#match-paddle-customers-with-adapty-profiles\}
Adapty 需要将每笔购买与[用户画像](profiles-crm)关联,这样才能在你的应用中使用。默认情况下,当 Adapty 收到 Paddle 的 webhook 时,会自动创建用户画像。你可以选择将哪个值用作 Adapty 中的 `customer_user_id`:
1. **默认且推荐:** 您在 `custom_data` 字段中传递的 `customer_user_id`(参见 [Paddle 文档](https://developer.paddle.com/build/transactions/custom-data))
2. Paddle Customer 对象中的 `email`(参见 [Paddle 文档](https://developer.paddle.com/paddle-js/methods/paddle-checkout-open/#parameters))
3. `ctm-...` 格式的 Paddle Customer ID(参见 [Paddle 文档](https://developer.paddle.com/paddle-js/methods/paddle-checkout-open/#parameters))
4. 不创建用户画像。如果您希望自行管理客户的用户画像,请选择此选项。
您可以在 [App Settings → Paddle](https://app.adapty.io/settings/paddle) 的 **Profile creation behavior** 字段中配置使用哪个值。
## 2. 将 Paddle 产品添加到 Adapty \{#2-add-paddle-products-to-adapty\}
:::warning
请务必将您的 Paddle 产品添加到 Adapty 看板,或将 Paddle 产品 ID 添加到现有产品中。Adapty 仅跟踪与这些产品绑定的交易事件。如果跳过此步骤,将不会创建任何交易事件。
:::
Paddle 在 Adapty 中的使用方式与 App Store 和 Google Play 完全相同——它是您销售数字产品的另一个平台。要完成配置,请在 Adapty 的 [Products](https://app.adapty.io/products) 页面中,填入相应的 `product_id` 和 `price_id` 值。
在 Paddle 中,产品 ID 格式为 `pro_...`,价格 ID 格式为 `pri_...`。打开某个具体产品后,你可以在 [Paddle 产品目录](https://vendors.paddle.com/products-v2)中找到它们:
产品添加完成后,下一步是确保 Adapty 能将购买行为关联到正确的用户。
## 3\. 为移动端用户提供访问权限 \{#3-provide-access-to-users-on-the-mobile\}
为确保在网页购买的用户能够在移动端获得访问权限,请使用购买时传入的相同 `customer_user_id` 调用 `Adapty.activate()` 或 `Adapty.identify()`。详情请参见[用户识别](identifying-users)。
## 4\. 测试您的集成 \{#4-test-your-integration\}
完成所有设置后,您可以测试您的集成。在 Paddle 测试环境中进行的交易将在 Adapty 中显示为 **Test**。来自生产环境的交易将显示为 **Production**。
您的集成现已完成。用户可以在您的网站上购买订阅,并自动在您的移动应用中获得高级功能访问权限,同时您可以在统一的 Adapty 看板中跟踪所有订阅分析数据。
## 重要注意事项 \{#important-considerations\}
- 在 Adapty 的分析中,交易金额包含税费和 Paddle 手续费,这与 Paddle 看板中显示税后及手续费后金额的方式不同。因此,您在 Adapty 中看到的数字会高于 Paddle 看板中的数字。
- 与其他商店不同,Paddle 中的退款仅影响被退款的特定交易,不会自动取消订阅。除非明确取消,否则订阅将继续保持活跃状态。
- 您还可以在 `custom_data` 字段中包含 `variation_id`,以将购买归因到特定的付费墙实例。Adapty 将从 webhook 中处理这些数据,并将其纳入分析统计。
### 付费试用 \{#paid-trials\}
在 Paddle 中使用付费试用时,需要在 Adapty 中创建两个产品:
1. 创建一个一次性购买产品,并将其关联到负责收取试用期费用的 Paddle 价格。
2. 然后创建一个订阅产品(月度/周度等),并将其关联到包含免费试用组件的 Paddle 价格。
从 Paddle 的角度来看,这是一个包含两个价格的单笔交易——一个价格用于收取试用费(例如 $0.99),另一个价格用于免费试用($0.00)。
从 Adapty 的角度来看,这会产生两个独立的事件:一个是针对试用付款的一次性购买事件,另一个是针对订阅产品的试用开始事件。
例如,当用户以 $0.99 开始付费试用一个 $9.99/月的订阅时,Paddle 会创建一笔包含两个价格的交易,而 Adapty 则将其处理为一笔 $0.99 的一次性购买(即时付款)和一个 $0.00 的试用开始事件(对应未来 $9.99/月的订阅)。
:::note
当用户取消付费试用时,你会收到 **Trial expired** 和 **Trial renewal canceled** 事件。
:::
## 充分利用您的 Paddle 数据 \{#get-more-from-your-paddle-data\}
:::important
要使您的 Paddle 事件能够与集成配合使用,您的用户必须至少使用其 App Store/Google Play 账户登录过一次应用。
:::
完成 Paddle 集成后,Adapty 即可立即提供数据洞察。为了充分利用您的 Paddle 数据,您可以设置额外的 Adapty 集成来转发 Paddle 事件——将所有订阅分析数据汇聚到同一个 Adapty 看板中。
您可以使用以下集成来转发和分析 Paddle 事件:
- [AppsFlyer](appsflyer)
- [Webhook](webhook)
- [Posthog](posthog)
## 当前限制 \{#current-limitations\}
- **取消订阅**:Paddle 提供两种取消订阅的方式:
1. 立即取消:订阅立即终止。
2. 在当前周期结束时取消:订阅在当前计费周期结束后终止(类似于应用商店中的应用内订阅)。
- **退款**:Adapty 支持追踪全额退款和部分退款。
- **宽限期**:默认情况下,Paddle 为账单问题设置固定的 30 天宽限期,在此期间订阅保持有效。你可以[自定义宽限期时长及到期后的处理方式(暂停或取消订阅)](https://developer.paddle.com/build/retain/configure-payment-recovery-dunning#prerequisites)。
**试用期**:如果试用期结束后收款失败,订阅状态将变为 `past_due`。在生产环境中,Paddle 的 Retain 功能会应用催款窗口,在订阅被取消或暂停之前尝试恢复付款。在沙盒环境中,Retain 不可用,因此不会重试付款,订阅将无限期保持 `past_due` 状态。
---
**另请参阅:**
- [通过服务端 API 验证 Paddle 购买、获取访问等级并从 Paddle 导入交易历史](api-adapty/operations/validatePaddlePurchase)
---
# File: custom-store
---
---
title: "与其他商店的初始集成"
description: "Adapty 与 App Store 的初始集成:快速指南"
---
欢迎加入 Adapty!我们的首要任务是帮助您快速上手,为您的应用取得最佳成果。
初始集成仅适用于 [App Store](initial_ios)、[Google Play](initial-android)、[Stripe](stripe) 和 [Paddle](paddle),因为 Adapty 会与这些商店验证您的应用、产品和优惠。
Adapty 不会与其他应用商店验证数据,也不处理通过它们完成的购买。但是,您仍然可以标记通过其他商店销售的产品,以便 Adapty 在购买成功后授予付费内容的访问权限、在分析中记录交易,并通过集成进行共享。
:::important 请确保您的后端处理购买并使用 [Adapty 服务端 API](getting-started-with-server-side-api) 将交易发送给 Adapty。只有在收到交易后,Adapty 才会提供访问权限、触发交易事件、将其发送至集成,并在分析中反映。 ::: 要将产品标记为通过自定义应用商店销售,请在创建产品时选择对应的应用商店。如果所需商店未在列表中,以下是创建商店的方法: 1. 在 **Products** 页面,打开您希望通过自定义应用商店销售的产品。 2. 选择您要通过其销售的应用商店。如果未列出,请点击 **Create Custom Store** 按钮。
3. 输入商店的 **Title** 和 **Store ID**。
4. 点击 **Create store** 按钮。
如果您的后端配置正确,Adapty 将接收来自该自定义商店的产品交易,在分析、[**Event Feed**](event-feed) 和[集成](https://app.adapty.io/integrations)中反映这些交易,并相应地授予访问权限。
## 从自定义商店数据中获取更多价值 \{#get-more-from-your-custom-store-data\}
:::important
要使自定义商店事件与集成配合使用,您的用户必须至少使用其 App Store/Google Play 账户登录过应用一次。
:::
设置自定义商店集成后,Adapty 即可立即提供洞察。为充分利用您的数据,您可以设置额外的 Adapty 集成来转发自定义商店事件——将所有订阅分析汇聚到单一的 Adapty 看板中。
可用于转发和分析自定义商店事件的集成:
- [AppsFlyer](appsflyer)
- [Webhook](webhook)
- [Posthog](posthog)
---
# File: transfer-apps
---
---
title: "将应用转移到其他账户"
description: "在 Adapty 中更换应用所有者"
---
当您的公司被收购、出售应用或重组业务实体时,需要将应用转移给其他所有者。转移过程涉及协调 Adapty、App Store Connect 和 Google Play Console 中的变更,以确保服务不中断。
## 转移应用所有权 \{#transfer-app-ownership\}
请先完成应用商店的转移,然后再在 Adapty 中转移应用。此顺序可确保在整个转移过程中购买功能持续正常运行。
:::note
在转移过程中,请勿删除或重新创建产品。在验证转移成功完成之前,请勿更改产品 ID。
:::
### App Store (iOS) 迁移 \{#app-store-ios-transfer\}
:::important
App Store Connect API 密钥(Issuer ID、Key ID、.p8 文件)属于账户级别,而非应用级别。迁移完成后,你需要从新所有者的账户生成新的 API 密钥,并在 Adapty 中更新。
应用专属共享密钥在迁移过程中仍可用于验证收据,但迁移完成后,新所有者同样需要重新生成并在 Adapty 中更新。
:::
1. **新所有者:** 如果还没有账号,请在 [app.adapty.io](https://app.adapty.io) 注册 Adapty 账号。
2. **原所有者:** 按照 Apple 的[转让指南](https://developer.apple.com/help/app-store-connect/transfer-an-app/overview-of-app-transfer)在 App Store Connect 中发起应用转让。
3. **新所有者:** 在 App Store Connect 中接受转让。
4. **原所有者:** 发送邮件至 [support@adapty.io](mailto:support@adapty.io),申请在 Adapty 中转让应用。请提供应用名称和新所有者的邮箱地址。
5. **新所有者:** 在 Adapty 中接收应用后,按照 [App Store 集成指南](initial_ios)在你的账号下生成并配置所有凭据。
### Google Play(Android)迁移 \{#google-play-android-transfer\}
1. **新所有者:** 如果还没有 Adapty 账户,请在 [app.adapty.io](https://app.adapty.io) 注册一个。
2. **双方所有者:** 确保两个 Google Play 开发者账户均已完成注册。
3. **原所有者:** 通过 Google Play 管理中心或 Google Play 开发者支持提交转让申请。Google 可能会要求提供额外文件,例如 DUNS 编号、合同或销售证明。
4. **新所有者:** 审核并批准转让申请。
5. **Google:** Google 支持团队处理转让请求,通常需要几个工作日,但具体时间可能因账户验证、订阅复杂程度和支付设置而有所不同。
6. **原所有者:** Google 完成转让后,发送邮件至 [support@adapty.io](mailto:support@adapty.io),申请在 Adapty 中转移应用。请提供应用名称和新所有者的电子邮件地址。
7. **新所有者:** 在 Adapty 中接收应用后,按照 [Google Play 集成指南](initial-android) 在您的账户下生成并配置所有凭据。
转让内容包括用户、订阅、统计数据、评分和商店列表。现有订阅者的账单连续性将得到保障,但付款将在转让完成后才切换到新所有者的商户账户。转让前的付款报告和订单仍保留在原账户中。详细要求请参阅 Google 的[转让指南](https://support.google.com/googleplay/android-developer/answer/6230247)。
## 风险规避与时机选择 \{#risk-mitigation-and-timing\}
**转移期间持续正常运行的功能:**
- 购买与续订(专属共享密钥在转移窗口期间持续验证收据)
- 现有订阅者的访问权限
- SDK 持续正常运行
**暂时停止运行的功能:**
- App Store Connect API 调用(需配置新密钥后恢复)
- 服务器通知(需重新配置端点后恢复)
- 凭据过渡期间分析数据可能出现缺口
**推荐时间安排:**
- 在低流量时段完成迁移(用户主要时区的凌晨 3 点至 6 点)
- 接受商店迁移后,新所有者需立即配置凭据
- 在接受迁移与完成 Adapty 集成之间,预留 15–30 分钟
**完成迁移后:**
- 立即测试收据验证
- 监控自动续订成功率,持续 48 小时
- 确认服务器通知已正常到达您的系统
- 检查新购买记录是否被正确追踪
## 验证转移是否成功完成 \{#verify-transfer-completed-successfully\}
在完成 Adapty 和应用商店的转移后:
1. **检查看板访问权限:** 新所有者应能在其 Adapty 看板中看到该应用。
2. **验证 API 密钥连接:** 检查新的 App Store Connect API 密钥或 Google Play 服务账户是否在 Adapty 中成功连接。
3. **测试 SDK 连接:** 运行您的应用,验证 Adapty SDK 初始化时是否无报错。
---
# File: installation-of-adapty-sdks
---
---
title: "安装 Adapty SDK"
description: "为 iOS、Android 及跨平台应用安装 Adapty SDK。"
---
根据您的偏好,您有三种方式可以开始使用:
- **遵循平台专属快速入门指南**:指南包含可直接用于生产环境的代码片段,因此实施起来不会花费太长时间。
- [iOS](ios-sdk-overview)
- [Android](android-sdk-overview)
- [React Native](react-native-sdk-overview)
- [Flutter](flutter-sdk-overview)
- [Unity](unity-sdk-overview)
- [Kotlin Multiplatform](kmp-sdk-overview)
- [Capacitor](capacitor-sdk-overview)
- **使用大语言模型(LLM)**:我们的文档对 LLM 友好。阅读我们的[指南](adapty-cursor),了解如何充分利用 LLM 与 Adapty 文档结合使用。
- **探索示例应用**:
- [iOS (Swift)](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples)
- [Android (Kotlin)](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app)
- [React Native(基础示例 - 纯 RN)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/BasicExample)
- [React Native(高级示例 - 适用于开发,可处理更复杂的场景)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/AdaptyDevtools)
- [React Native(Expo 开发版本)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo)
- [React Native(Expo Go & Web)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock)
- [Flutter (Dart)](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example)
- [Unity (C#)](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets)
- [Kotlin Multiplatform](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example)
- [Capacitor](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples)
---
# File: sample-apps
---
---
title: "示例应用"
description: ""
---
为了帮助你快速上手 Adapty SDK,我们准备了示例应用,展示如何集成和使用其核心功能。这些应用提供了付费墙、购买流程和数据分析追踪的现成实现。
## 为什么使用示例应用? \{#why-use-sample-apps\}
- **快速集成:** 在真实应用中了解 Adapty SDK 的工作方式。
- **最佳实践:** 遵循推荐的实现模式。
- **调试与测试:** 在将 Adapty 集成到自己的项目之前,使用示例应用进行排查和实验。
## 可用示例应用 \{#available-sample-apps\}
- [iOS (Swift)](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples)
- [Android (Kotlin)](https://github.com/adaptyteam/AdaptySDK-Android/tree/master/app)
- [React Native(纯 RN 基础示例)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/BasicExample)
- [React Native(高级示例——适合开发使用,可处理更复杂的场景)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/AdaptyDevtools)
- [React Native(Expo 开发构建)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo)
- [React Native(Expo Go 及 Web)](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock)
- [Flutter (Dart)](https://github.com/adaptyteam/AdaptySDK-Flutter/tree/master/example)
- [Unity (C#)](https://github.com/adaptyteam/AdaptySDK-Unity/tree/main/Assets)
- [Kotlin Multiplatform](https://github.com/adaptyteam/AdaptySDK-KMP/tree/main/example)
- [Capacitor (React)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-react-example)
- [Capacitor (Vue.js)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-vue-example)
- [Capacitor (Angular)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/basic-angular-example)
- [Capacitor(高级开发工具)](https://github.com/adaptyteam/AdaptySDK-Capacitor/tree/master/examples/adapty-devtools)
---
# File: paywall-builder-templates
---
---
title: "创建流程"
description: "从自定义设计的模板库或最简启动项开始新建流程。"
---
您可以从模板创建流程,也可以从头开始创建。
:::link
想了解更多关于构建流程的内容?请观看我们 [YouTube 播放列表](https://www.youtube.com/playlist?list=PLMksWqaZiWtM)中的分步视频教程。
:::
## 创建流程 \{#create-flow\}
1. 打开 **Flows** 页面。
2. 点击 **Create flow**。
3. 选择一个选项:
- **Browse templates**(打开模板库)
- **Start from scratch**(创建空白流程)
4. 在编辑器中重命名流程。点击顶部标题栏中的流程名称,然后输入新名称。
:::warning
Adapty 允许流程重名。请为每个新建的流程重命名,否则会创建多个难以区分的 **Untitled** 流程。
:::
### 使用模板 \{#use-a-template\}
模板库包含多个模板,可作为你构建流程的起点。每个模板都是一个完整的流程,包含多个页面、交互元素和可用的导航功能。你可以编辑任意元素来进行自定义。
使用模板的步骤:
1. 在模板库中浏览模板卡片。每张卡片会展示该流程的预览截图。
2. 点击你想要的卡片上的 **Use as template**。
模板加载完成后,您可以在编辑工具中修改任何元素、页面或属性。
### 从零开始 \{#start-from-scratch\}
从零开始会创建一个包含单个空白屏幕的流程。你可以使用[元素库](builder-elements)中的元素来设计该屏幕。
## 更换模板 \{#change-the-template\}
您可以在编辑工具内切换模板。打开 Screens 面板,点击 **Templates** Templates 按钮重新打开模板库,然后选择一个新模板。
:::warning
应用新模板会替换当前的流程草稿。Adapty 会提示您确认——点击 **Use template** 继续操作,或点击 **Cancel** 保留草稿。确认后,之前的草稿将无法恢复。已发布的流程不受影响,仍正常运行。
:::
## 模板中的自定义字体 \{#custom-fonts-in-templates\}
:::link
主要文章:[Flow Builder 中的自定义字体](using-custom-fonts-in-flow-builder)
:::
带有 **Custom font** 标签的模板使用了自定义字体。这些字体不随移动端 SDK 一起提供。将鼠标悬停在标签上可查看该模板所使用的字体。
若要在设备上呈现预期的排版效果,请将字体文件添加到您的应用包中。未内置该字体的旧版应用将回退使用系统字体。
如需在不影响旧版本的情况下替换字体,请复制该流程,在副本中修改字体,并将副本限制为[包含该字体的应用版本的用户](segments)。
---
# File: builder-ui
---
---
title: "流程编辑工具界面"
description: "流程编辑工具界面与工作区概览。"
---
流程编辑工具的主界面包含添加视觉元素、编辑属性以及修改用户流程逻辑所需的全部工具。本文将介绍界面的各个区域:各区域的功能及其位置。
:::link
想了解更多关于构建流程的内容?请观看我们 [YouTube 播放列表](https://www.youtube.com/playlist?list=PLMksWqaZiWtM)中的分步视频教程。
:::
## 项目控件与常用快捷键(顶部工具栏)\{#project-controls-and-useful-shortcuts-top-toolbar\}
* **Close** Close:退出流程编辑器并返回流程列表页。
* **App name** App:标识该流程所属的应用。
* **All flows** Flows:打开该应用所有流程的列表。
* **Flow status**:流程名称左侧的图标表示当前[流程状态](builder-save-publish#flow-status):
- **Draft** Draft
- **Publishing**(旋转加载中)
- **Failed** Failed
- 或 **Live** Live。
* **重命名流程**:点击流程名称即可重命名。多个流程可以同名——建议[为每个新流程起一个唯一的名称](paywall-builder-templates#create-flow)。
* **视图模式切换**:在设计视图 Cursor 与[远程配置视图](customize-flow-with-remote-config)Remote Config 之间切换。
* **撤销/重做**:点击箭头图标以撤销 Undo 或重做 Redo 流程更改,也可使用 ⌘Z / Ctrl+Z 进行撤销。
* **保存草稿 / 发布**:点击 **Save draft** 可保存进度而不上线(⌘ / Ctrl+S)。展开下拉菜单 Open dropdown 可访问 [**Publish**](builder-save-publish) 按钮。只有发布后,才能将流程添加到[版位](create-placement)中。
## 预览区域(中央) \{#preview-area-center\}
工作区中央区域模拟你的流程在移动设备上的实际显示效果。
* 点击某个元素即可选中并编辑其属性。若要选中容器内的子元素,请先点击容器,再点击子元素。
* 若要编辑页面本身的属性,请点击所有元素以外的空白区域,或在 Screens and Layers 面板中选择该页面。
* 若要调整元素的排列顺序,请在 Screens and Layers 面板中上下拖动对应条目。
:::warning
流程编辑器旨在创建响应式布局。因此,您**无法手动更改元素的位置**——只能更改它们的顺序。每个容器的布局设置决定了其中元素的排列方式。
:::
### 设备预览上方的活动屏幕工具栏 \{#active-screen-bar-above-the-device-preview\}
- **Screen name** — 显示当前屏幕名称的标签。
- **Toggle animations** Toggle animations — 开启或关闭元素动画预览;开启后动画会持续播放,直到手动关闭。仅在当前屏幕包含至少一个[动画](builder-styling#animation)时显示。不影响真机上的动画效果。
- **Add element** Plus — 在当前屏幕打开[元素库](builder-elements)。等同于"屏幕与图层"面板顶部的 **+** 按钮——在面板折叠时尤为实用。
### 查看控件(底部工具栏)\{#view-controls-bottom-toolbar\}
底部工具栏中的工具用于控制预览效果。
* **Device**:从可用的 iPhone 和 Android 手机型号中选择一款,以更改视口尺寸和设备外观。
* **Screen orientation**:在竖屏 Portrait 和横屏 Landscape 模式之间切换,预览不同方向下的流程效果。
* **Color scheme**:在浅色 Light mode 和深色 Dark mode 模式之间切换,查看设计在不同主题下的适配效果。
* **Locale**:选择语言区域,预览本地化内容下的流程效果。
* **View options**:开启或关闭设备边框和安全区域参考线。
## 屏幕与元素属性(右侧面板)\{#screen-and-element-properties-right-panel\}
### 屏幕设置与布局 \{#screen-settings-and-layout\}
:::link
主要文章:[屏幕与图层](paywall-layout-and-products)
:::
未选中任何元素时,右侧面板允许你调整当前[流程屏幕](paywall-layout-and-products)的属性,包括以下内容:
* 与系统 UI 的交互(例如是否显示状态栏)
* 自动布局规则
* 背景(颜色、图片或视频)
* 内边距大小
* 垂直滚动行为
如果界面包含某些元素(例如[互动问卷](onboarding-quizzes)),此列表将扩展并显示相关属性。
### 元素属性 \{#element-properties\}
选中元素后,右侧面板允许您修改其样式和交互属性。
#### 设计属性 \{#design-properties\}
:::link
了解更多:[布局与定位](manage-paywall-ui-elements),[样式与外观](builder-styling)
:::
**Design** 标签页用于配置所选元素的视觉外观和布局:
* **Visibility(可见性)**:显示或隐藏元素。启用 **Conditional** 可见性可设置规则,控制元素何时显示。
* **Position(位置)**:在 Relative、Absolute 或 Fixed 定位方式之间选择。
* **Content(内容)**(仅限文本元素):编辑元素的文本内容、插入[变量](#variables)并管理本地化。
* **Typography(排版)**(仅限文本元素):配置字体、字重、字号、颜色、对齐方式、修饰效果和截断方式。
* **Spacing(间距)**:设置元素的外边距和内边距。
* **Effects(效果)**:添加投影、内阴影、背景模糊或图层模糊。
* **Animation(动画)**:添加动画效果(例如 Pulse),并配置其时长和强度。
* **Appearance(外观)**:调整不透明度和旋转角度。
* **Layout(布局)**:选择布局方向(纵向或横向),并设置子元素的分布方式。
#### 交互属性 \{#interactions-properties\}
:::link
了解更多:[操作](onboarding-actions),[导航与交互](onboarding-navigation-branching)
:::
**Interactions** 选项卡用于定义用户与所选元素交互时会发生什么。每个交互由一个**触发器**和一个或多个**操作**组成:
* **触发器**定义*何时*发生某件事——例如,**On Tap**(用户点击该元素)。
* **动作**定义*发生什么*——例如,跳转到另一个页面或修改某个变量的值。可以为同一个触发器添加多个动作,使它们按顺序依次执行。
可以为同一个元素添加多个触发器,从而按顺序执行多个动作。
## 左侧面板 \{#left-panel\}
左侧面板的功能会根据当前激活的按钮而变化。你可以在以下选项中切换:
* [屏幕与图层](#screens-and-layers)
* [添加元素](#element-selection)
* [产品](#products)
* [样式](#saved-styles)
* [变量](#variables)
* [本地化](#localization)
### 屏幕与图层 \{#screens-and-layers\}
:::link
主要文章:[屏幕与图层](paywall-layout-and-products)
:::
点击图层 Layers 按钮可打开屏幕与图层面板(默认在打开流程编辑器时显示)。
该面板以树形结构展示每个屏幕的图层。屏幕上的每个元素都是一个图层,容器内的子元素会嵌套显示。你可以通过拖放来调整图层顺序。
### 元素选择 \{#element-selection\}
:::link
主要文章:[元素](builder-elements)
:::
点击加号 Plus 按钮后,左侧面板会显示可用 UI 元素及其变体列表。点击某一条目,即可将其作为新图层添加到当前屏幕。
### 产品
:::link
主要文章:[产品](paywall-product-block)
:::
产品 Products 按钮会打开产品列表,显示流程中每个屏幕所分配的产品。
该列表为只读模式。若要为屏幕分配产品,请添加一个产品元素并在右侧面板中进行配置。若要创建或编辑产品,请使用 Adapty 看板中的 **Products** 页面。
### 已保存的样式 \{#saved-styles\}
:::info
了解更多:
- [样式与外观](builder-styling)
- [文字内容](onboarding-text)
- [深色模式](paywall-dark-mode)
:::
点击样式 Styles 按钮可打开已保存的样式。
在这里,你可以编辑和管理全局样式。如果你的流程中有多个元素使用了相同的字体排版或颜色,可以将这些数据保存为全局样式,之后只需单击即可复用。
目前,Flow Builder 支持两种全局样式——字体样式和颜色样式。每种颜色样式都可以为深色模式单独设置一个值。
### 变量 \{#variables\}
:::link
主要文章:[变量](onboarding-variables)
:::
括号 Variables 按钮用于打开变量面板。
在这里,你可以创建和管理流程中的变量。运行时,SDK 会将变量占位符替换为实际值——用户属性、产品价格、本地化字符串等。
变量分为两个标签页:
* **Custom(自定义)**:通过操作创建和控制的变量。
* **Elements(元素)**:由用户交互决定的值——例如测验答案、开关状态或标签页选择。
产品变量(价格、名称及其他产品数据)不会显示在此面板中,请在编辑文本元素时直接引用它们。
变量的用途:
* **绑定文本**:显示动态内容,而非静态字符串。
* **控制可见性**:根据条件显示或隐藏元素(例如,为高级用户隐藏升级按钮)。
* **与用户交互**:访问用户输入字段中的数据,例如表单或测验。
### 本地化 \{#localization\}
:::link
主要文章:[本地化](add-flow-remote-config-locale)
:::
本地化视图让你集中管理流程中所有可翻译的内容。它以表格形式展示每个文本字符串和图片,按屏幕分组排列,并为每种语言提供单独的列。在此视图中,你可以:
* 添加新语言区域并直接编辑本地化字符串。
* 跟踪翻译状态——每一行都会标记为 **Done** 或 **Missing**。
* 按屏幕筛选,或仅显示缺少翻译的内容。
* 使用 **AI Translate** 自动翻译内容,或通过 **Import/Export** 批量导入/导出翻译。
---
# File: flow-builder-recipes
---
---
title: "常见流程方案"
description: "在流程编辑工具中构建常见屏幕模板的分步指南。"
---
本节介绍如何在流程编辑工具中逐步构建最常见的屏幕模板——从布局选择到交互操作,逐个元素详细说明。每篇指南均独立成文,使用标准的流程编辑工具元素。
3. 购买按钮、链接和关闭按钮均已预配置操作。对于链接,[配置用于导航用户的 URL](#links)。对于其他按钮类型,请前往 **Interactions** 面板。在 **Button triggers** 部分,设置按钮需要执行的[操作](onboarding-actions)。
4. 在 **Design** 面板中配置[按钮设计](builder-styling)。
## 按钮类型 \{#button-types\}
### 购买按钮 \{#purchase-buttons\}
:::link
若要让购买按钮正常工作,请将产品绑定到屏幕并添加 **Products** 元素。请参阅[指南](paywall-product-block)。
:::
购买按钮会触发用户在屏幕上选中产品的应用内购买流程。SDK 会自动处理交易,因此无需在应用代码中手动处理购买逻辑。
添加购买按钮的步骤:
1. 点击 **+** 并选择 **Button**,然后选择一个按钮预设。
2. 选中按钮后,在右侧面板中打开 **Interactions** 标签页。
3. 点击 **Add trigger** > **On tap**,然后点击 **Add action**。
4. 将 **Action** 设置为 **Purchase**,将 **Product** 设置为 `products.selectedProduct`。`products.selectedProduct` 变量始终解析为当前屏幕上已选中的产品。
:::tip
你可以通过添加动画效果让购买按钮更加醒目。付费墙编辑工具目前支持 **Pulse** 动画类型。
在 **Design** 面板中配置动画样式。
:::
### 链接 \{#links\}
:::important
**Terms of Use** 和 **Privacy Policy** 按钮内置了 **Open URL** 操作。请在该操作中设置目标 URL。空的 Open URL 以及[内联链接](onboarding-text#inline-link)会阻止预览和发布。
:::
为满足部分应用商店的要求,您可以添加以下链接:
- 服务条款
- 隐私政策
- 购买恢复
添加链接的方法:
1. 点击 **+**,选择 **Button > Links**。这会添加一行内联按钮,包含预设操作:恢复购买或打开 URL。如果不需要其中某些按钮,可在图层面板中删除多余的按钮。
2. 接下来,设置按钮操作:
- **Restore purchases** 按钮已自动处理购买恢复功能。
- 对于其余每个链接:
1. 点击按钮将其选中,然后切换到右侧的 **Interactions** 标签页。
2. 将 URL 粘贴到输入框中。
3. 默认情况下,URL 会在应用内浏览器中打开,以提供流畅的用户体验。如果希望在外部浏览器中打开,请勾选 **Open in external browser** 复选框。
### 关闭流程 \{#close-flow\}
**关闭** 按钮可自动关闭流程。
要添加关闭按钮,点击 **+** 并选择 **Button > Close flow**。
:::tip
使用 **Absolute** 定位将关闭按钮放置在屏幕角落。
:::
您也可以通过[操作](onboarding-actions)将任意其他按钮配置为关闭流程。
### 自定义按钮 \{#custom-buttons\}
你添加的每个按钮都可以配置点击后执行的操作:
- 跳转到下一个页面
- 显示弹窗提示
- 设置[变量](onboarding-variables)
- [显示或隐藏页面元素](onboarding-element-visibility)
- 打开 URL
- 恢复购买
- 执行条件操作
---
# File: builder-tabs
---
---
title: "标签页"
description: "在流程中添加可切换内容面板的标签页导航。"
---
**标签页**将屏幕的某个区域拆分为可切换的内容面板——用户点击标签页标头后,下方面板会随之更新。
{/* TODO: on-device GIF */}
## 添加、删除和选择标签页 \{#add-remove-and-select-tabs\}
每个标签页由两部分组成:
- **标签页头部** — 可点击的标签名称(Tab 1、Tab 2 等)。
- **标签页内容** — 每个标签页对应一个内容容器,在该容器中添加的内容会在相应标签页被选中时显示。
点击 **Add tab** 可添加新标签页,每个新标签页都会自动创建一个对应的内容容器。
若要设置某个标签页在页面首次显示时默认处于激活状态,请开启 **Selected by default**。
## 设置标签页样式 \{#style-the-tabs\}
### 模板 \{#templates\}
Flow Builder 提供三种开箱即用的标签页模板:
- **Segment control** — 胶囊形切换器,选中标签带有圆角边框。
- **Button Tabs** — 独立的按钮式标签页。
- **Underline** — 文字标签,选中标签下方带有下划线标记。
### 选项卡状态 \{#tab-states\}
每个选项卡都有一个状态切换器(**Default / Selected**),可以分别为激活和未激活状态设置样式——包括字体、颜色、填充色和边框。
## 可选组 \{#selectable-group\}
标签页是一种**单选可选组**——同一时间只有一个标签处于激活状态。在 **Screen settings** 面板的 [Selectable groups](paywall-layout-and-products#selectable-groups) 部分管理该组。
该组提供两个变量:
- `tabs.selectedOptionId` — 当前选中标签页的 ID,可用于条件判断。
- `tabs.selectedOptionTitle` — 当前选中标签页的标签文本,可用于动态文本。
如果你重命名了该组,请将 `tabs` 替换为你自定义的 **Group ID**。
详细说明请参阅[可选元素与组](flow-selectable-elements)。
---
# File: builder-toggles
---
---
title: "切换开关"
description: "为您的支付流程添加切换开关。"
---
:::warning
Apple 可能会拒绝使用预选试用切换开关的应用。默认设置为"开启"的切换开关可能被认定为违反 App Store 审核指南的暗黑设计模式——它在用户未明确选择的情况下暗示其同意免费试用。
为避免被拒绝,请将切换开关默认设置为 **off**,让用户自行选择是否开启试用。
:::
试用切换开关是一个二元开关,允许用户在付费墙上选择标准产品或试用型产品。当用户改变其状态时,可以立即触发某个操作——例如切换产品组、更新变量,或显示/隐藏元素。
要添加试用切换开关,点击目标屏幕上的 **+**,然后选择 **Trial toggle**。
每个试用切换开关都是 **Toggle** 类型的可选元素。每个可选元素都分配有一个变量来反映其状态——例如,名为 `trial` 的切换开关会获得一个值为 `True` 或 `False` 的 `trial.is_selected` 变量。
要让其他元素依赖切换开关的状态,请基于此变量设置条件[动作](onboarding-actions)或[条件可见性](onboarding-element-visibility)。
---
# File: builder-reviews-and-testimonials
---
---
title: "评论与用户推荐"
description: "在付费墙中添加评论、评分和社交证明。"
---
**User Engagement** 元素分类提供了四个模板,用于在付费墙上展示评论、评分和社交证明。每个模板都是完全可编辑的组合——替换占位文本,并应用你的[颜色样式](builder-styling)和[排版设置](onboarding-text)以与整个流程保持一致。
## 评论 \{#review\}
带有评分、引言和作者署名的卡片。适合展示一条令人印象深刻的用户评价。
## 评分 \{#rating\}
计数与星级行,例如"17000+ 评分"。用于突出显示评分数量。
## 应用评分 \{#app-rating\}
带有样本量的突出评分,例如"4.9 / 基于 1000+ 条评价"。适合用来展示整体高评分。
## 社交证明 \{#social-proof\}
带有头像组和成员数量的展示区,例如"加入 50,000+ 用户"。用于强调社区规模。
---
# File: flow-timer
---
---
title: "倒计时器"
description: "在付费墙中添加倒计时器。"
---
**倒计时器**从固定时长开始倒数至零——归零后画面将停止。
## 模板 \{#templates\}
该分类提供四种视觉样式:
- **Blocks** — 将天、时、分、秒分别显示在带标签的独立格子中。
- **Inline Units** — 带单位后缀的单行文本。
- **Inline** — 纯数字显示。
- **Badge** — 胶囊形数字展示。
## 设置 \{#settings\}
### 设置时长 \{#set-the-duration\}
在右侧面板的 **Countdown** 部分,输入倒计时的起始时长(天、小时、分钟、秒)。
### 配置行为 \{#configure-the-behavior\}
**Behavior** 下拉菜单用于控制计时器的启动时机:
- **Every appear** — 每次用户打开该页面时重新开始计时。默认选项。
- **First appear** — 在当前 App 会话中用户首次查看该页面时开始计时。若用户在同一会话内返回该页面,计时继续;重新启动 App 后重置。
- **First appear (persisted)** — 在用户首次打开该页面时开始计时,并在 App 重启后持续计时。
### 计时器结束时触发动作 \{#trigger-an-action-when-the-timer-ends\}
:::link
主要文章:[动作](onboarding-actions)
:::
添加 **On timer end** 触发器,在倒计时归零时执行动作——例如跳转到另一屏幕或隐藏折扣标签。
---
# File: onboarding-quizzes
---
---
title: "流程中的测验"
description: "在您的 Adapty 流程中添加互动测验,以收集用户偏好并驱动个性化流程——无需编写代码。"
---
使用测验向用户呈现预定义的选项。与输入框不同,测验没有文字输入字段——用户从您定义的选项中进行选择。可用于收集用户偏好、进行市场细分,或根据用户的回答对流程进行分支跳转。
### 添加测验 \{#add-a-quiz\}
1. 点击左上角的 **+**。
2. 选择 **Quiz**。
3. 选择测验类型:
- **Icon/image/emoji options:** 纵向排列的可选选项列表,每个选项包含图标、图片或表情符号以及文字标签。
- **Icon/image/emoji grid:** 网格形式的可选选项,每个选项包含图标、图片或表情符号。
- **Rating:** 供用户表达评分的量表——支持数字或星级形式。
### 设置条件导航 \{#set-up-conditional-navigation\}
如需根据用户的选择将其引导至不同页面,请在**导航按钮**上设置条件动作,而不是在测验选项上设置:
1. 选择导航按钮。
2. 在 **Interactions** 面板中,添加一个 **On Tap** 触发器,并选择 **Conditional** 动作。
3. 在 **Edit Action** 对话框中,构建 **if** 行:
- 在左侧,点击 `{}` 并选择 **Elements → Screen → `
2. 点击右上角的 **Create product**。Adapty 支持所有类型的产品:订阅、非消耗型商品(包括永久授权)以及消耗型商品。
3. 选择 **Create a new product and push to stores**。
4. 填写以下信息:
- **Product name**:输入产品名称,该名称将显示在 Adapty 看板中。名称主要供你自己参考,请选择最便于在 Adapty 看板中使用的名称。
- **Access Level**:选择该产品所属的[访问等级](access-level)。访问等级用于确定购买产品后解锁的功能。请注意,此列表仅包含已创建的访问等级。Adapty 默认创建了 `premium` 访问等级,你也可以[添加更多访问等级](access-level)。
- **Subscription duration**:从列表中选择订阅时长。
- **Weekly/Monthly/2 Months/3 Months/6 Months/Annual**:订阅时长。
- **Lifetime**:对于永久解锁应用高级功能的产品,请使用 Lifetime(永久)周期。
- **Non-Subscriptions**:对于非订阅类产品(即没有时长的产品),请使用 Non-Subscriptions。这类产品可用于解锁额外功能、消耗型商品等。
- **Consumables**:消耗型商品可多次购买,在应用使用过程中会被消耗,例如游戏货币和道具。请注意,消耗型商品不影响访问等级。如需通过一次性购买授予访问等级,请改用 **Non-Subscriptions**。
- **Price (USD)**:产品的美元定价。该价格将作为基准价,自动计算并设置各国/地区的价格。你可以在之后[为不同国家和地区自定义价格](edit-product#set-country-specific-prices)。
5. 点击 **Save & Continue**。
6. 如果你计划在 App Store 上架,请填写对应的产品信息:
- **Product ID**:为该产品创建一个永久唯一的 ID。
- **Product group**:选择你在 App Store Connect 中已创建的产品组,或点击 **Create new Product Group** 并设置名称。Adapty 创建完成后,你可以从下拉菜单中选择它。
- **Screenshot**:上传一张应用内购买的截图,清晰展示所提供的商品或服务。该截图仅用于 App Store 审核,不会在 App Store 上公开显示。截图尺寸和格式要求请参阅[此处](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications/)。
7. 点击 **Push data to App Store**。
:::warning
如果这是您该应用的第一个产品,您必须在 App Store Connect 中手动提交审核。之后无需再次操作。审核完成后,Adapty 中的产品状态将自动更新。
:::
8. 如果计划在 Google Play 发布,请配置 Google Play 的产品信息:
- **Base Product ID**:为该产品创建一个永久唯一的 ID。
- **Subscription**:从下拉列表中选择您已在 Google Play Console 中创建的订阅组,或点击 **Create new Product Group** 并设置其名称和 ID。Adapty 创建完成后,您即可从下拉列表中选择它。
:::note
Grace Period 和 Account Hold Period 将按照 Play Store 规则自动设置为默认值。您可以稍后在 Google Play Console 中进行修改。
:::
9. 点击 **Push data to Play Store**。
10. 对于 iOS,通过从下拉菜单中选择 **Free duration** 来配置新用户优惠(免费试用)。在初始设置阶段,您可以添加一个免费试用的新用户优惠。主产品经商店审核通过后,您可以通过关联商店控制台中已有的 ID 来[添加更多优惠](offers)(例如促销活动、赢回优惠)。
:::important
新用户优惠不会自动与 Google Play 同步。与 App Store 不同,Google Play 没有单独的"新用户优惠"类型——免费试用和折扣优惠都以**优惠**的形式配置在基础方案上。[在 Google Play Console 中创建优惠并将其关联到你的 Adapty 产品](google-play-offers)。
:::
11. 最后,点击 **Save** 确认创建产品。
## 创建产品并关联已有应用商店产品 \{#create-product-and-connect-existing-store-products\}
:::warning
开始之前,请确保你已完成以下操作:
- 配置了所需应用商店的集成:
- [App Store](initial_ios)
- [Google Play](initial-android)
- 在所需应用商店中创建了产品:
- [App Store](app-store-products)
- [Google Play](android-products)
**如果你尚未创建任何产品**,建议参考[推送至应用商店](#create-product-and-push-to-store)指南,同时在 Adapty 和应用商店中创建产品。
:::
2. 点击右上角的 **Create product**。Adapty 支持所有类型的产品:订阅、非消耗型商品(包括永久授权)和消耗型商品。
3. 选择 **Connect an existing store product**。
4. 填写以下信息:
- **Product name**:输入产品名称,该名称将在 Adapty 看板中显示。此名称主要供你自己参考,可以选择任何方便在 Adapty 看板中使用的名称。
- **Access Level ID**:选择该产品所属的[访问等级](access-level)。访问等级用于确定购买产品后可解锁的功能。请注意,此列表仅显示已创建的访问等级。`premium` 访问等级在 Adapty 中默认创建,您也可以[添加更多访问等级](access-level)。
- **订阅时长**:从列表中选择订阅的时长。
- **每周/每月/2个月/3个月/6个月/每年**:订阅的具体时长。
- **永久授权**:适用于永久解锁应用高级功能的产品。
- **非订阅**:对于非订阅类产品(即没有时长的产品),请使用非订阅类型。可用于解锁附加功能、消耗型商品等。
- **消耗型商品**:消耗型商品可多次购买,在应用使用过程中会被消耗掉,常见示例包括游戏内货币和道具。请注意,消耗型商品不会影响访问等级。如需通过一次性购买授予访问等级,请使用**非订阅**类型。
- **价格(USD)**:产品的美元定价。如果您的产品已在商店上架,此处的值不会影响其实际售价,您可以从列表中选择任意值。之后,您可以直接在 Adapty 看板中[为不同地区自定义价格](edit-product#set-country-specific-prices)。
5. 点击 **Continue**。
6. 配置每个应用商店的产品信息:
- **App Store:**
- **App Store Product ID:** 该唯一标识符用于在设备上访问您的产品。请从列表中选择。如果列表中未显示,请在 App Store Connect 中检查其配置,确保配置正确且归属于此应用。
- **Play Store:**
- **Google Play Product ID:** 这是 Play Store 中的产品标识符。请从列表中选择。如果列表中未显示,请在 Google Play Console 中检查其配置,确保配置正确且归属于此应用。
- **Base Plan ID:** 该 ID 用于定义产品在 Play Store 中的基础方案。在 Play Store 上添加订阅的 Product ID 时,必须提供 Base Plan ID。基础方案定义了订阅的核心信息,包括账单周期、续订类型(自动续订或预付费)以及对应价格。请注意,在 Adapty 中,同一订阅与不同基础方案的每种组合均被视为独立产品。
- **Legacy fallback product**:备用产品仅适用于使用旧版 Adapty SDK(2.5 及以下版本)的应用。通过在 Google Play Console 中将产品标记为向后兼容,Adapty 可以识别旧版 SDK 是否可以购买该产品。此字段请按以下格式填写:`
## 设置国家/地区特定价格 \{#set-country-specific-prices\}
您可以直接在 Adapty 看板中为不同地区设置不同的价格,这些国家/地区特定价格将自动应用到 App Store Connect 和/或 Google Play Console 中的产品。
要设置国家/地区特定价格:
1. [打开产品进行编辑](#edit-product)。
2. 点击 **Download**,以正确格式导出当前应用商店价格,或创建一个新的 CSV 文件。
3. 在 CSV 文件中更新价格。请遵循[格式要求](#csv-file-format)。如果某个国家/地区的价格保持不变或未包含在文件中,则不会有任何变化。上传 CSV 时,Adapty 会比较价格并仅更新有差异的价格。
4. 在 **Edit** 窗口中,点击 **Upload** 并选择 CSV 文件。
5. 如果您希望更改也对现有订阅者生效,请选择 **Apply to existing subscribers**。
6. 检查将要应用的更改,然后点击 **Save changes**。
### CSV 文件格式 \{#csv-file-format\}
:::tip
如果您在同一应用中有类似产品,或希望在不同应用中设置相同的价格,可以复用同一个 CSV 文件。
:::
编辑 CSV 中价格的最简便方法是[下载包含当前价格的文件并直接编辑](#set-country-specific-prices)。
但如果您自行创建文件,文件中必须包含以下列:
- `region_name`
- `region_code`
- `app_store_currency`
- `app_store_requested_price`
- `play_store_currency`
- `play_store_requested_price`
示例:
```
region_name,region_code,app_store_currency,app_store_requested_price,play_store_currency,play_store_requested_price
United States,US,,8.99,,8.99
United Arab Emirates,AE,USD,8.99,AED,39.99
Germany,DE,USD,8.99,USD,8.99
```
## 查看审计日志 \{#view-audit-log\}
Adapty 会记录每个产品的所有定价变更,以便您追踪更改人员及时间。要查看审计日志:
1. 从 Adapty 主菜单进入 **[Products](https://app.adapty.io/products)**。
2. 点击产品旁边的三点菜单,然后选择 **Audit log**。
审计日志表格显示每次定价变更的日期、团队成员姓名与角色,以及更改次数。
要下载某次事件的详细 CSV 说明,请点击该行的下载图标。
---
# File: delete-product
---
---
title: "删除产品"
description: "了解如何在 Adapty 中删除订阅产品,同时不影响应用的收入流。"
---
您只能删除未在付费墙中使用的产品。
要删除产品,请执行以下步骤:
1. 从 Adapty 主菜单进入 **[Products](https://app.adapty.io/products)**。
2. 点击产品旁边的 **3-dot** 按钮,然后选择 **Delete**。
2. 输入您要删除的产品名称。
3. 点击 **Delete forever**。
---
# File: add-product-to-paywall
---
---
title: "向付费墙添加产品"
description: "了解如何在 Adapty 中向付费墙添加和管理产品。"
---
要使产品在应用程序用户的[付费墙](paywalls)中可见且可选择,请按照以下步骤操作:
1. 在[配置付费墙](create-paywall)时,点击 **Products** 标题下方的 **Add product**。
2. 从打开的下拉列表中,选择将向客户展示的产品。该列表仅包含之前已创建的产品。产品的排列顺序会在 SDK 端保留,因此在配置付费墙时,请务必考虑所需的排列顺序。此外,您还可以根据需要为产品指定优惠。
3. 根据付费墙的状态,点击 **Create as draft** 或 **Save and publish**。
请注意,付费墙创建后,不建议对付费墙中的产品进行编辑、添加或删除操作,因为这可能会影响付费墙的数据图表。
---
# File: virtual-currencies
---
---
title: "虚拟货币"
description: "在 Adapty 中定义应用内货币,将其与产品关联以自动发放积分,并跟踪每位用户的余额。"
---
要在 Google Play Console 中创建优惠活动:
1. 点击 **Add offer** 并从列表中选择基础方案。
2. 输入优惠活动 ID。该 ID 后续将用于分析和 Adapty 看板,请为其设置一个有意义的名称。
3. 选择资格标准:
1. **New customer acquisition**:该优惠活动仅对新订阅者开放,且这些用户此前未使用过该优惠。这是最常见的选项,建议默认使用。
2. **Upgrade**:该优惠活动面向从其他订阅升级的用户。当您希望向现有订阅者推广更高价位的方案时使用,例如从订阅青铜等级升级至黄金等级的用户。
3. **Developer determined**:您可以通过应用代码控制哪些用户可以使用该优惠活动。在生产环境中使用时请谨慎,以避免潜在欺诈行为:用户可能反复激活免费或折扣订阅。此类优惠活动的一个典型使用场景是赢回已流失的订阅者。
4. 为您的优惠活动最多添加两个定价阶段。共有三种可用阶段类型:
1. **Free trial**:订阅可在配置的时间内(最少 3 天)免费使用。这是最常见的优惠类型。
2. **Single payment**:如果用户预付费用,订阅价格更低。例如,通常月度方案售价 $9.99,但使用此优惠类型后,前三个月合计 $19.99,享受 30% 的折扣。
3. **Discounted recurring payment**:订阅在前 `n` 个周期内享受优惠价格。例如,通常月度方案售价 $9.99,但使用此优惠类型后,前三个月每月仅需 $4.99,享受 50% 的折扣。
一个优惠活动可以包含两个阶段。在这种情况下,第一个阶段必须是免费试用(Free trial),第二个阶段为单次付款(Single payment)或折扣周期性付款(Discounted recurring payment)。这两个阶段将按此顺序依次生效。
:::important
请注意,使用 Adapty 付费墙编辑工具创建的付费墙仅会显示多阶段 Google 订阅优惠的第一个阶段。但请放心,当用户购买产品时,所有优惠阶段将按照 Google Play 中的配置依次生效。
:::
5. 激活优惠活动以在应用中使用。
6. 继续[将优惠活动添加到 Adapty](create-offer)。
:::note
不同基础方案的优惠活动 ID 可以相同。
:::
## 后续步骤 \{#next-steps\}
添加优惠活动后,请继续完成以下设置:
- 如果您**同时在 App Store 拥有应用**,请参阅 [App Store 指南](app-store-offers)。
- 如果您**仅在 Google Play 拥有应用**,请参照[此指南](create-offer)将优惠活动添加到 Adapty。
---
# File: create-offer
---
---
title: "将优惠添加至 Adapty"
description: "使用 Adapty 的工具创建和管理特殊订阅优惠。"
---
Adapty 允许你为新用户、现有用户或流失用户提供试用或折扣优惠。
在 App Store Connect 或 Google Play Console 中完成设置后,你需要通过以下两个步骤将其添加到 Adapty:
1. [在 Adapty 中使用商店的优惠 ID 将优惠添加到产品。](#1-create-offer)
2. [在流程或付费墙中展示优惠。](#2-display-offer)
:::warning
新用户优惠(App Store)会在用户符合条件时自动生效,无需在 Adapty 中手动添加到产品。
本指南介绍如何配置促销活动(App Store)、赢回优惠(App Store)以及所有 Google Play 优惠。
:::
## 0. 开始之前 \{#0-before-you-start\}
在 Adapty 中设置优惠之前,请确保以下事项:
1. 您已在商店中创建了所需的所有优惠:
- [App Store](app-store-offers)
- [Google Play](google-play-offers)
2. 您已在 Adapty 中创建了[产品](create-product)并添加了其 ID。
3. 对于 App Store:您已上传[用于促销活动的应用内购买密钥](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers)。
## 1. 在 Adapty 中为产品添加优惠 \{#1-add-offer-to-product-in-adapty\}
无论是 Play Store 和 App Store 的促销活动,还是 App Store 的赢回优惠,在应用商店完成配置后,将其添加到 Adapty 非常简单:
1. 在 Adapty 主菜单中打开 [**Products**](https://app.adapty.io/products),找到要添加优惠的产品。
2. 找到目标产品后,在 **Actions** 列中点击产品旁边的 **3-dot** 按钮,然后选择 **Edit**。
3. 在 **Edit product** 窗口中,点击 **+** 并选择 **Add offers**。
4. 点击 **Add offer**。
5. 然后填写产品的优惠详情。
以下是优惠的相关字段:
- **Offer name**:为优惠命名,便于在 Adapty 中识别。使用任何方便你的名称即可。
- **App Store Offer type**:选择你要添加的 App Store 优惠类型:促销活动或赢回优惠。(新用户优惠无需手动添加——如果有,系统会自动应用。)
- **App Store Offer ID**:这是你[在 App Store 中设置的](app-store-products)优惠唯一 ID。
- **Play Store Offer ID**:同样,这是你[在 Play Store 中设置的](android-products)优惠唯一 ID。
:::tip
如果 **App Store Offer ID** 或 **Play Store Offer ID** 字段未激活,请切换到 **Products** 标签页并选择一个产品 ID。
:::
6. (可选)如有需要,点击 **Add offer** 继续添加优惠。
7. 点击 **Save**,将优惠添加到产品中。
## 2. 展示优惠 \{#2-display-offer\}
将优惠关联到产品后,需要在用户看到该产品的地方展示它——可以在流程中,也可以在付费墙中。
### 在流程中添加优惠 \{#add-offer-to-flow\}
在 [流程编辑工具](adapty-flow-builder) 中,优惠通过产品元素绑定到具体产品上。请先添加产品元素并为其分配产品——详见[设置购买](paywall-product-block)。
绑定优惠的步骤:
1. 在画布上,选择要显示优惠的产品卡片。
2. 在右侧面板的 **Product** 下,选择对应产品,然后在 **Select offer (optional)** 下拉菜单中选择优惠。
### 将优惠添加到付费墙 \{#add-offer-to-paywall\}
:::info
你无法向处于 **live** 状态的付费墙添加优惠。如果想为已有付费墙添加优惠,请先[复制](duplicate-paywalls)它,然后在新付费墙中配置产品。
:::
要让优惠在应用的[付费墙](paywalls)中对用户可见且可供选择,请按以下步骤操作:
1. 创建或编辑付费墙时,在 **General** 标签页中,添加您刚才为其创建了优惠的产品。
2. 从 **Offer** 列表中为该产品选择您之前创建的优惠。该列表仅对已添加优惠的产品可用。
3. 如有需要,可以继续添加更多产品和优惠,但每个产品只能添加一个优惠。
## Adapty 如何处理优惠活动 \{#how-adapty-works-with-offers\}
请注意以下关于 Adapty 中优惠活动的工作方式:
- 当用户符合某项优惠的条件时,Adapty 会在用户购买时自动应用您配置的优惠。
- 如果某个产品在 App Store 中同时配置了新用户优惠和促销活动,符合条件的用户将优先享受新用户优惠。新用户优惠期结束后,如果用户仍符合促销活动的条件,且您在 Adapty 中配置了该促销活动,则在用户再次尝试购买该产品时,促销活动将自动生效。
- 如果您希望更精细地控制优惠的应用方式,或在某些情况下需要不附带优惠地销售产品,可以通过以下几种方式实现:
- 在 App Store 或 Google Play Console 中配置资格条件
- 在 App Store 或 Google Play Console 中创建一个不含优惠的独立产品
- 在 Adapty 中创建一个不含优惠的独立产品,将包含两种产品版本的付费墙添加到某个[版位](placements),并使用目标受众[市场细分](segments)来控制向不同用户展示哪个付费墙。例如,您可以根据**订阅产品**或**付费访问等级**创建市场细分,或使用[自定义属性](profiles-crm)来实现自己的业务逻辑。
---
# File: create-access-level
---
---
title: "创建访问等级"
description: "在 Adapty 中创建并分配访问等级,以实现更好的用户细分。"
---
访问等级让您无需硬编码特定产品 ID,即可控制应用用户在移动应用中的操作权限。每个产品定义了用户获得某一访问等级的时长。因此,每当用户完成购买时,Adapty 会为其授予特定时段(订阅)或永久(永久授权购买)的应用访问权限。
当您在 Adapty 看板中创建应用时,系统会自动生成 `premium` 访问等级。该访问等级作为默认访问等级,无法被删除。
:::tip
您也可以通过 [Developer CLI](developer-cli-reference#adapty-access-levels-create) 以编程方式创建访问等级。
:::
创建新访问等级的步骤:
1. 在 Adapty 主菜单中进入 **[Products](https://app.adapty.io/access-levels)**,然后选择 **Access levels** 标签页。
2. 点击 **Create access level**。
3. 在 **Create access level** 窗口中,为其分配一个 ID。该 ID 将作为移动应用内部的标识符,在用户购买后用于开启额外功能的访问权限。此外,该标识符有助于在应用中区分不同的访问等级。请确保其清晰易懂,以便于您的使用。
4. 点击 **Create access level** 确认创建访问等级。
---
# File: assigning-access-level-to-a-product
---
---
title: "为产品分配访问等级"
description: "为产品分配访问等级,以优化订阅管理。"
---
每个[产品](product)都需要关联一个访问等级,以确保用户在购买后能够获得相应的专属内容。Adapty 会自动确定订阅时长,并将其作为访问等级的到期日期。对于永久授权产品,若用户完成购买,访问等级将永久有效,不设任何到期日期。
将访问等级关联至产品:
1. 在[配置产品](create-product)时,从 **Access Level ID** 列表中选择访问等级。
2. 点击 **Save**。
---
# File: give-access-level-to-specific-customer
---
---
title: "为特定用户授予访问等级"
description: "使用 Adapty 的高级工具为用户分配特定的访问等级。"
---
您可以直接在 Adapty 看板中手动调整特定用户的访问等级。这在客户支持场景中尤为实用。例如,您可以为某位用户额外延长一周的高级功能使用时间,以感谢其留下精彩评价。
## 在 Adapty 看板中为特定用户授予访问等级 \{#give-access-level-to-a-specific-customer-in-the-adapty-dashboard\}
1. 从 Adapty 主菜单进入 **[Profiles and Segments](https://app.adapty.io/placements)**。
2. 点击您想要授予访问权限的用户。
3. 点击 **Add access level**。
4. 选择要授予的访问等级以及该访问等级对此用户的到期时间。
5. 点击 **Apply**。
## 通过 API 为特定用户授予访问等级 \{#give-access-level-to-a-specific-customer-via-api\}
您也可以选择通过 Adapty API 从服务器端为用户授予访问等级。如果您为推荐用户或其他与产品相关的事件设置了奖励机制,这将非常方便。详细信息请参阅[通过服务端 API 授予访问等级](api-adapty/operations/grantAccessLevel)页面。
---
# File: local-access-levels
---
---
title: "本地访问等级"
description: "在临时服务中断的情况下管理访问等级。"
---
:::important
请注意以下几点:
- 本地访问等级从 Adapty SDK 3.12 版本开始支持。
- 出于安全考虑,Android 上的本地访问等级默认处于禁用状态。如有需要,请在 SDK 激活时启用:[Android](sdk-installation-android#enable-local-access-levels)、[React Native](sdk-installation-reactnative)、[Flutter](sdk-installation-flutter#enable-local-access-levels-android)。
:::
您配置的每个产品都关联了一个[**访问等级**](access-level)。当用户完成购买后,Adapty SDK 会将访问等级分配给该用户的[用户画像](profiles-crm),因此您需要使用此访问等级来判断用户是否可以访问应用内的付费内容。
Adapty SDK 非常可靠,其服务器不可用的情况极为罕见。即使发生这种情况,您的用户也不会察觉到任何影响。
如果用户完成了购买,但 Adapty 无法收到响应,SDK 将切换为直接在应用商店验证购买。因此,访问等级会在应用本地授予,无需进行任何额外配置即可启用此功能。SDK 会在后台自动处理这一切,用户将像正常情况一样访问其已付费的内容。
关于本地访问等级的工作方式,请注意以下几点:
- 当用户重新联网后,交易信息将自动推送至 Adapty 服务器,服务器随后会将交易应用到用户画像,并将更新后的用户画像返回给 SDK。
- 在数据推送完成之前,更新后的数据不会显示在 Adapty 数据分析中。
- 本地访问等级仅在 Adapty 服务器不可用时生效,否则 SDK 将使用已缓存的数据。
- 本地访问等级不适用于消耗型商品,但如果消耗型商品在 Adapty 看板中被分配了订阅类型(月付、年付、周付等),则不受此限制。
---
# File: choose-meaningful-placements
---
---
title: "选择有意义的版位"
description: "使用 Adapty 优化流程和付费墙版位,提升用户参与度和收益。"
---
在[创建版位](create-placement)时,务必考虑应用的逻辑流程以及您希望为用户打造的体验。大多数应用拥有不超过 5 个[版位](placements)即可,同时不影响进行实验的能力。以下是一个版位结构示例:
1. **用户引导流程:** 这是用户与你的应用首次互动的阶段。通过在此处结合流程、用户引导和付费墙版位,是向用户展示应用价值的绝佳机会。超过 80% 的订阅在用户引导旅程中完成激活,因此专注于在此阶段推销最高利润的订阅至关重要。借助 Adapty,你可以轻松为不同目标受众设置不同的[流程](adapty-flow-builder)、[用户引导](onboardings)和[付费墙](paywalls),并通过运行 A/B 测试找到最适合你应用的方案。例如,你可以针对美国用户运行 A/B 测试,以 50% 的概率展示价格更高的订阅。
2. **应用设置:** 如果用户在用户引导旅程中未完成订阅,你可以在应用内创建流程或付费墙版位,例如放在应用设置中,或在用户完成某个特定目标操作后触发。由于应用内的用户往往会更审慎地考虑是否订阅,这里的产品价格可以比用户引导阶段略低一些。
3. **促销活动:** 如果用户多次看到流程或付费墙后仍未订阅,可能意味着价格对他们来说偏高,或者他们对订阅本身有所顾虑。此时,你可以向他们展示一个特别优惠,提供最实惠的订阅方案,甚至是永久授权产品。这有助于吸引对价格敏感或对订阅持观望态度的用户完成购买。
大多数应用都有相似的逻辑和版位设置,遵循用户旅程,并在关键节点展示流程、付费墙、用户引导或 A/B 测试,以提升转化率和营收。你可以在每个版位中进行配置,从而灵活试验并优化变现策略。
---
# File: create-placement
---
---
title: "创建版位"
description: "在 Adapty 中创建和管理版位,以改善流程和付费墙的效果。"
---
[版位](placements)是移动应用中的特定位置,用于展示流程、付费墙、用户引导或 A/B 测试。例如,订阅选择界面可能出现在启动流程中,而消耗型商品(如金币)则可能在游戏玩家金币耗尽时弹出。
你可以在不同版位向不同用户群体展示相同或不同的流程、付费墙、用户引导或 A/B 测试——在 Adapty 中,这些用户群体称为"目标受众"。
请阅读[选择有意义的版位](choose-meaningful-placements)部分,了解如何选择合适的版位。
:::tip
您也可以使用 [Developer CLI](developer-cli-reference#adapty-placements-create) 以编程方式创建版位。
:::
:::info
虽然版位的创建流程对于流程、付费墙和用户引导来说大致相同,但您无法创建一个同时服务于多种类型的版位——每种版位类型处理的数据图表各不相同。
:::
## 创建并配置版位 \{#create-and-configure-a-placement\}
1. 从 Adapty 主菜单进入 **[Placements](https://app.adapty.io/placements)**。根据你想创建的版位类型,切换到 **Flows**、**Paywalls** 或 **Onboardings** 标签页。
2. 点击 **Create placement**。
3. 输入**版位名称**。这是在 Adapty 看板中使用的内部标识符,之后可以随时修改。
4. 输入**版位 ID**。你将在 Adapty SDK 中使用该 ID 来调用版位的[流程](adapty-flow-builder)、[付费墙](paywalls)、[用户引导](onboardings)和 [A/B 测试](ab-tests)。该 ID 是每个版位的唯一标识,创建后无法修改。
接下来,为版位分配流程、付费墙、用户引导或 A/B 测试。Adapty 支持[目标受众](audience)——基于[市场细分](segments)的用户群体——因此你可以向不同用户群体展示不同内容。如果不需要定向投放,默认的 *所有用户* 目标受众可覆盖所有人。
:::note
在开始之前,请确保你已创建好想要运行的流程、付费墙、用户引导或 A/B 测试,以及要指定的目标受众。
:::
1. 在 **Placements/ Your placement** 窗口中,为默认的 *All users* 目标受众添加流程、付费墙、用户引导或 A/B 测试。点击 **Run flow**、**Run paywall** 或 **Run A/B test** 按钮(按钮标签取决于版位类型),然后从下拉列表中选择所需的流程、付费墙、用户引导或 A/B 测试。
2. 如果你希望在版位中使用多个目标受众,为不同用户群体提供个性化内容,请点击 **Add audience** 按钮,并从列表中选择所需的市场细分。
导出的 CSV 文件包含以下版位信息:
- 版位 ID
- 版位名称
- 目标受众名称
- 市场细分名称
- 跨版位 A/B 测试名称
- A/B 测试名称
- 流程名称、付费墙名称或用户引导名称(取决于导出时所在的标签页)
:::note
流程版位不支持跨版位 A/B 测试,因此该列在流程导出中将为空。
:::
---
# File: delete-placement
---
---
title: "删除版位"
description: "了解如何在 Adapty 中删除版位,同时不影响您的流程或付费墙效果。"
---
[版位](placements)是指您移动应用中的特定位置,可在该位置展示流程、付费墙、用户引导或 A/B 测试。
:::danger
尽管你可以删除任何版位,但请务必确认不要删除移动应用中正在使用的版位。删除一个活跃的流程或付费墙版位后,如果你已[配置了备用付费墙](fallback-paywalls),该备用付费墙将永久显示,且你将无法在已发布的应用版本中将其替换为动态流程或付费墙。
:::
要删除已有版位:
1. 从 Adapty 主菜单进入 **[Placements](https://app.adapty.io/placements)**。根据要删除的版位类型,切换到 **Flows**、**Paywalls** 或 **Onboardings** 标签页。
2. 点击版位旁边的 **3-dot** 按钮,选择 **Delete** 选项。
3. 在弹出的 **Delete placement** 窗口中,输入你即将删除的版位名称。
4. 点击 **Delete forever** 按钮确认删除。
---
# File: add-audience-paywall-ab-test
---
---
title: "为版位添加目标受众与流程、付费墙或 A/B 测试"
description: "在 Adapty 中对不同目标受众细分的流程和付费墙运行 A/B 测试。"
---
Adapty 中的**目标受众**是由[市场细分](segments)定义的用户群体,可让你向特定用户展示流程、付费墙、用户引导和 A/B 测试。通过筛选条件构建市场细分,确保每个用户群体看到适合自己的内容。
将目标受众添加到[版位](placements)后,你可以将流程、付费墙、用户引导或 A/B 测试定向投放给特定用户群体。将目标受众与版位关联,能确保合适的用户在其使用旅程中的恰当时机看到正确的内容。
打开您想要添加流程、付费墙、用户引导或 A/B 测试的版位,或在 [版位](https://app.adapty.io/placements) 菜单中新建一个。
:::note
在开始之前,请确保你已创建好想要运行的流程、付费墙、用户引导或 A/B 测试,以及要指定的目标受众。
:::
1. 在 **Placements/ Your placement** 窗口中,为默认的 *All users* 目标受众添加流程、付费墙、用户引导或 A/B 测试。点击 **Run flow**、**Run paywall** 或 **Run A/B test** 按钮(按钮标签取决于版位类型),然后从下拉列表中选择所需的流程、付费墙、用户引导或 A/B 测试。
2. 如果你希望在版位中使用多个目标受众,为不同用户群体提供个性化内容,请点击 **Add audience** 按钮,并从列表中选择所需的市场细分。
在这种情况下,我们依赖目标受众优先级。目标受众优先级是一个数字顺序,其中 #1 优先级最高。它指导检查目标受众的顺序。简单来说,目标受众优先级帮助 Adapty 决定在选择要展示的付费墙、用户引导或 A/B 测试时,首先应用哪个目标受众。如果目标受众的优先级较低,可能符合条件的用户会被跳过,转而被导向另一个优先级更高的目标受众。
跨版位目标受众(即为[跨版位 A/B 测试](ab-tests#ab-test-types)创建的目标受众)始终优先于常规目标受众。
"所有用户"目标受众始终具有最低优先级,因为它是一个备用选项,包含所有不符合其他目标受众条件的用户。
要调整版位的目标受众优先级:
1. 在创建新版位或编辑现有版位时,点击 **Edit priority**。只有在版位中添加了至少三个目标受众("所有用户"加上其他两个)时,该按钮才可见。如果少于三个,顺序是显而易见的——"所有用户"目标受众排在最后。
2. 在打开的 **Edit audience priorities** 窗口中,通过拖放方式重新排列目标受众以正确排序。
3. 点击 **Save** 按钮。
---
# File: placement-metrics
---
---
title: "版位数据图表"
description: "在 Adapty 中分析版位数据图表,提升付费墙表现。"
---
借助 Adapty,你可以在应用中灵活创建和管理多个版位,每个版位都可以关联不同的付费墙或 A/B 测试。这种灵活性让你能够针对特定的市场细分人群,尝试不同的优惠或定价模型,从而优化应用的变现策略。
为了深入了解版位的表现及用户与您的优惠之间的互动情况,Adapty 会追踪与已展示付费墙相关的各类用户交互和交易行为。其强大的分析系统可捕获浏览量、独立浏览量、购买量、试用量、退款量、转化率和收入等数据图表。
所收集的数据图表会实时持续更新,并可通过 Adapty 友好的看板方便地访问和分析。您可以自由自定义分析时间范围、按不同参数应用筛选器,以及跨版位、用户市场细分或产品比较数据图表。
版位数据图表可在版位列表中查看,您可在此获取所有版位的整体表现概览。该高级视图为每个版位提供汇总数据图表,便于您比较其表现并识别趋势。
如需对每个版位进行更详细的分析,可导航至版位详细数据图表页面。在该页面上,您将看到所选版位的全面专项数据图表。这些数据图表能更深入地揭示特定版位的表现,帮助您评估其有效性并做出数据驱动的决策。
### 按安装日期筛选数据图表 \{#filter-metrics-by-install-date\}
付费墙、试用和购买的数据图表可以按两种不同的日期维度进行分组:
- **事件日期** — 付费墙被查看、试用开始或购买发生的时间。
- **安装日期** — 用户首次打开应用的时间。
对于同一日期范围,两种视图呈现的数据可能差异显著。**按安装日期筛选数据图表** 复选框用于控制看板采用哪种分组方式:
- **未勾选(默认)**:数据图表按事件日期分组。
- **已勾选**:数据图表按安装日期分组。
**示例。** 将日期范围设置为 4 月 1 日至 30 日,查看试用数据。
- **未勾选**:显示 4 月内*开始*的试用,无论这些用户何时安装应用。
- **已勾选**:显示 4 月内*安装*应用的用户所产生的试用,无论其试用何时开始。
使用安装日期视图可衡量特定同期群的用户获取效果;使用事件日期视图可衡量特定时段内付费墙或用户引导的活跃情况。
### 数据图表控件 \{#metrics-controls\}
系统根据所选时间段展示数据图表,并按左侧列参数以四级缩进进行组织。
#### 数据图表的视图选项 \{#view-options-for-metrics-data\}
版位数据图表页面提供两种数据视图选项:基于付费墙的视图和基于目标受众的视图。
在基于付费墙的视图中,数据图表按与付费墙关联的版位进行分组,便于用户按不同版位分析数据图表。
在基于目标受众的视图中,数据图表按付费墙的目标受众进行分组,用户可评估特定目标受众市场细分的数据图表。
#### 时间范围 \{#time-ranges\}
你可以从多种时间段中进行选择,以分析数据图表,聚焦于特定的天数、周数、月数或自定义日期范围。
#### 可用筛选器与分组 \{#available-filters-and-grouping\}
:::link
主要文章:[分析控件](controls-filters-grouping-compare-proceeds)
:::
Adapty 提供了强大的工具,帮助你按需筛选和自定义数据图表分析。在 Adapty 的数据图表页面,你可以使用多种时间范围、分组选项和筛选功能。
- ✅ 筛选方式:目标受众、付费墙、付费墙分组、版位、国家、商店。
- ✅ 分组方式:市场细分、商店、产品
#### 单项数据图表 \{#single-metrics-chart\}
版位数据图表页面的核心组成部分之一是数据图表区域,它以可视化方式呈现所选数据图表,便于分析。
版位数据图表页面的图表区域包含一个水平条形图,直观展示所选数据图表的值。图表中的每个条形对应一个数据值,大小按比例呈现,一目了然。水平轴表示所分析的时间范围,垂直列显示数据图表的数值。所有数据图表值的总计显示在图表旁边。
此外,点击图表区域右上角的箭头图标可展开视图,在图表完整折线上显示所选数据图表。
#### 数据图表总计摘要 \{#total-metrics-summary\}
在单项数据图表旁边,还有一个数据图表总计摘要区域,显示特定时间点所选数据图表的累计值,您可以通过下拉菜单更改所显示的数据图表。
### 数据图表定义 \{#metrics-definitions\}
借助我们全面的定义,充分发挥版位数据图表的价值。从收入到转化率,获取有价值的洞察,为您的变现策略提供强力支撑,助力应用走向成功。
:::note
Adapty 使用 [currencylayer.com](https://currencylayer.com/) 的汇率(每 8 小时更新一次)将其他货币换算为美元。汇率**在交易发生时锁定**——后续汇率变动不会影响已换算的结果。
:::
#### 收入 \{#revenue\}
该数据图表表示在特定版位内,来自购买和续订所产生的以美元计的总收入。请注意,收入计算不包括 Apple App Store 或 Google Play Store 的佣金,且为扣除任何费用前的金额。
#### 实际收益 \{#proceeds\}
该数据图表表示应用所有者在特定版位内,扣除 Apple App Store 或 Google Play Store 佣金后,实际从购买和续订中获得的以美元计的收入。它反映了直接贡献于应用收益的净收入。有关实际收益计算方式的更多信息,请参阅 Adapty [文档](analytics-cohorts#revenue-vs-proceeds)。
#### ARPPU \{#arppu\}
ARPPU 即每付费用户平均收入,衡量特定版位内每位付费用户所产生的平均收入。计算方式为总收入除以独立付费用户数量。例如,若总收入为 15,000 美元,付费用户数为 1,000,则 ARPPU 为 15 美元。
#### ARPAS \{#arpas\}
ARPAS 即每活跃订阅者平均收入,用于衡量特定版位内每位活跃订阅者所产生的平均收入。计算方式为总收入除以已激活试用或订阅的用户数量。例如,若总收入为 5,000 美元,订阅者数量为 1,000,则 ARPAS 为 5 美元。该数据图表有助于评估每位订阅者的平均变现潜力。
#### ARPU \{#arpu\}
仅适用于用户引导版位。ARPU 是查看用户引导的每位用户的平均收入,计算方式为总收入除以独立浏览用户数量。
#### 独立购买转化率 \{#unique-cr-to-purchases\}
独立购买转化率的计算方式为特定版位内的购买数量除以独立浏览量。它侧重于购买量与独立浏览量之比,从而洞察特定版位内将独立访客转化为付费用户的效果。
#### 购买转化率 \{#cr-to-purchases\}
购买转化率的计算方式为特定版位内的购买数量除以付费墙的总浏览次数。它表示特定版位内产生购买的浏览比例,从而洞察您的付费墙将用户转化为付费用户的效果。
#### 独立试用转化率 \{#unique-cr-to-trials\}
独立试用转化率的计算方式为特定版位内启动的试用数量除以独立浏览量。它衡量特定版位内导致试用激活的独立浏览比例,从而洞察您的付费墙将独立访客转化为试用用户的效果。
#### 购买量 \{#purchases\}
购买量代表特定版位内付费墙上各类交易的累计总数。该数据图表包含以下类型的交易(不含续订):
- 在特定版位内直接进行的新购买。
- 最初在特定版位内激活的试用转化。
- 在特定版位内进行的订阅降级、升级和跨级操作。
- 在特定版位内的订阅恢复,例如在自动续订到期后重新激活订阅。
通过综合考量这些不同类型的交易,购买量数据图表可全面呈现特定版位内的整体获客和变现活动情况。
#### 试用量 \{#trials\}
试用量数据图表表示在特定版位内已激活的试用总数,反映了通过您的付费墙在这些版位内启动试用期的用户数量。该数据图表有助于追踪试用优惠的有效性,并提供关于用户参与度以及从试用转化为付费订阅的洞察。
#### 已取消试用量 \{#trials-canceled\}
已取消试用量数据图表表示特定版位内已关闭自动续订功能的试用数量。当用户手动取消订阅试用时即会产生此情况,表明用户决定在试用期结束后不继续订阅。追踪已取消试用量可提供关于用户行为的宝贵信息,帮助您了解特定版位内用户退出试用的比率。
#### 退款量 \{#refunds\}
退款量数据图表表示特定版位内退款的购买和订阅数量,包括因各种原因(如用户申请、支付问题或其他适用退款政策)而被撤销或退款的交易。
#### 退款率 \{#refund-rate\}
退款率的计算方式为特定版位内的退款数量除以首次购买数量(不含续订)。例如,若有 5 笔退款和 1,000 笔首次购买,则退款率为 0.5%。
#### 浏览量 \{#views\}
浏览量数据图表表示特定版位内用户浏览付费墙的总次数。用户每次访问该版位内的付费墙均计为一次独立浏览。追踪浏览量有助于了解用户与付费墙的互动程度,提供关于用户行为以及付费墙在应用特定区域内的版位和设计效果的洞察。
#### 独立浏览量 \{#unique-views\}
独立浏览量数据图表表示特定版位内用户浏览付费墙的独立实例数量。与将每次访问计为一次浏览的总浏览量不同,独立浏览量无论用户访问多少次,均只计算该用户对特定版位内付费墙的一次访问。追踪独立浏览量有助于更准确地衡量用户参与度以及付费墙在特定版位内的覆盖范围,因为它关注的是独立用户而非总访问次数。
#### 完成量与独立完成量 \{#completions--unique-completions\}
仅适用于用户引导版位。完成量统计用户完成用户引导版位的次数,即从第一屏浏览到最后一屏。若用户完成两次,则计为两次**完成量**,但只有一次**独立完成量**。
#### 独立完成率 \{#unique-completions-rate\}
仅适用于用户引导版位。独立完成量除以独立浏览量的结果。该数据图表有助于了解用户与用户引导版位的互动情况,并在发现用户忽略该引导时进行相应调整。
---
# File: create-paywall
---
---
title: "创建付费墙"
description: "了解如何使用 Adapty 的付费墙编辑工具创建高转化率付费墙。"
---
[付费墙](paywalls)是 Adapty 中定义要提供哪些产品的配置。在 Adapty 中,付费墙是在应用中获取产品的唯一方式。
无论以何种方式展示,您都需要一个付费墙:
- [**付费墙编辑工具**](adapty-paywall-builder):在无代码编辑器中设计页面。Adapty 负责渲染并处理购买逻辑。
- **自定义付费墙**:自行实现 UI,并使用付费墙配置获取产品。
创建后,将付费墙分配到[版位](placements)——版位控制用户看到哪个付费墙。已上线的付费墙产品是固定的,因此其数据图表始终反映相同的产品组合,让您可以比较不同产品和定价方案之间的表现。
:::tip
您也可以使用 [Developer CLI](developer-cli-reference#adapty-paywalls-create) 以编程方式创建付费墙。
:::
## 后续步骤 \{#next-steps\}
创建第一个付费墙后:
1. 将其添加到[版位](placements)。版位 ID 将是唯一需要硬编码的实体,您将使用它们来获取要销售的产品。
2. 后续使用付费墙的方式取决于您的实现方案:
- 如果您想使用 [Adapty 付费墙编辑工具](adapty-paywall-builder),请在无代码编辑器中设计付费墙。Adapty 将负责渲染付费墙并处理购买逻辑,您只需在应用代码中展示付费墙即可。
- 如果您使用自定义付费墙,请参阅适用于您平台的 Adapty 应用内购买实现指南:
- [iOS](ios-implement-paywalls-manually)
- [Android](android-implement-paywalls-manually)
- [React Native](react-native-implement-paywalls-manually)
- [Flutter](flutter-implement-paywalls-manually)
- [Unity](unity-implement-paywalls-manually)
- [Kotlin Multiplatform](kmp-implement-paywalls-manually)
---
# File: customize-paywall-with-remote-config
---
---
title: "使用远程配置设计付费墙"
description: "在 Adapty 中使用远程配置自定义付费墙,实现更精准的用户定向。"
---
:::important
本指南介绍经典付费墙的远程配置。如需了解 Flow Builder,请参阅[使用远程配置自定义流程](customize-flow-with-remote-config)。
:::
付费墙远程配置是一个强大的工具,提供灵活的配置选项。它允许使用自定义 JSON 数据来精确定制你的付费墙。你可以通过它定义标题、图片、字体、颜色等各种参数。
3. 切换到 **Remote config** 选项卡。
远程配置有 2 种视图:
- [表格视图](customize-paywall-with-remote-config#table-view-of-the-remote-config)
- [JSON 视图](customize-paywall-with-remote-config#json-view-of-the-remote-config)
**表格**视图和 **JSON** 视图包含相同的配置元素。两者只是使用偏好上的差异,唯一的区别在于表格视图提供了右键菜单,在修正本地化错误时非常实用。
如需切换视图,随时点击 **Table** 或 **JSON** 标签即可。
无论您选择哪种视图来自定义付费墙,之后都可以通过 SDK 使用 `AdaptyPaywall` 的 `remoteConfig` 或 `remoteConfigString` 属性访问这些数据,并对付费墙进行相应调整。您也可以通过[服务端 API](api-adapty/operations/updatePaywall) 以编程方式更新远程配置的值,从而无需手动在看板上操作即可动态修改付费墙配置。以下是一些远程配置的使用示例。
### 远程配置的表格视图 \{#table-view-of-the-remote-config\}
如果你不太习惯直接编写代码,但又需要修改 JSON 中的某些值,Adapty 为你提供了**表格**视图。
这是您 JSON 的表格版副本,便于阅读和理解。颜色编码有助于区分不同的数据类型。
要添加键,请点击 **Add row** 按钮。我们会自动检查值与类型的映射关系,如果您的修改可能导致无效的 JSON,系统会显示警告。
其他行选项主要适用于[付费墙本地化](add-remote-config-locale):
现在是时候[创建版位](create-placement)并将付费墙添加到其中了。完成后,你可以在移动应用中
4. 点击 **Locales** 并选择您想要支持的语言。保存更改以将这些语言区域添加到付费墙。
现在,您可以手动翻译内容、使用 AI,或导出本地化文件供外部翻译人员使用。
## 使用 AI 翻译付费墙 \{#translate-paywalls-with-ai\}
AI 驱动的翻译是本地化付费墙的快捷高效方式。
您可以翻译 **String** 和 **List** 类型的值。默认情况下,所有行都已选中(以紫色高亮显示)。已翻译的行标记为绿色,默认不会包含在新的翻译中。未选中或未翻译的行显示为灰色。
1. 选择要翻译的行。建议取消勾选包含 ID、URL 和变量的行,以防止 AI 对其进行翻译。
2. 选择翻译目标语言。
3. 点击 **AI Translate** 应用翻译。所选行将被翻译并添加到付费墙中,已翻译的行将标记为绿色。
## 导出本地化文件供外部翻译 \{#exporting-localization-files-for-external-translation\}
虽然 AI 驱动的本地化正成为一种流行趋势,但您可能更倾向于使用更可靠的方式,例如专业的人工翻译或经验丰富的翻译机构。如果是这种情况,您可以导出本地化文件分享给翻译人员,然后将翻译结果重新导入 Adapty。
通过 **Export** 按钮导出时,会为每种语言创建单独的 `.json` 文件,并打包为一个压缩包。如果您只需要一个文件,可以直接从特定语言的菜单中导出。
收到翻译文件后,使用 **Import** 按钮一次性或逐个上传。Adapty 将自动验证文件,确保其符合正确格式。
### 导入文件格式 \{#import-file-format\}
为确保导入成功,导入文件必须满足以下要求:
- **文件名和扩展名:**
文件名必须与其所代表的语言区域一致,并以 `.json` 为扩展名。您可以在 Adapty 看板中验证并复制语言区域名称。如果名称无法识别,导入将失败。
- **有效的 JSON:**
文件必须是有效的 JSON 格式。否则,导入将失败。
## 手动本地化 \{#manual-localization\}
有时,您可能需要微调翻译内容、为特定语言区域添加不同的图片,或直接调整远程配置。
1. 选择要翻译的元素并输入新值。您可以更新 **String** 和 **List** 类型的值,或替换更适合该语言区域的图片。
2. 利用英语语言区域中的上下文菜单高效解决本地化问题:
- **Copy this value to all locales**:将所选行的英语值覆盖到所有非英语语言区域,替换其中已做的更改。
- **Revert all row changes to original values**:放弃当前会话中所做的所有更改,将值恢复到上次保存的状态。
在为付费墙添加语言区域后,请确保在应用代码中正确实现语言区域代码。请参阅
### 设置 Apple Pay 域名验证 \{#set-up-apple-pay-domain-verification\}
在 **Settings > Domains** 中,选择用于域名验证的主要支付服务商。然后,向相应服务商验证你的付费墙域名:
**Stripe**:
1. 前往 [Payment method domain settings](https://dashboard.stripe.com/settings/payment_method_domains),点击 **Add a new domain**。
2. 添加 `app.funnelfox.com` 以及你的个人付费墙子域名(格式类似 `paywalls-....fnlfx.com`)。如需查找你的子域名,请前往 **Settings > Domains**,复制 **Hosted subdomain** 的值。
**Paddle**:
1. 在 Paddle 控制台中,前往 **Checkout > Website approval**,点击 **Add a new domain**。
2. 添加 `app.funnelfox.com` 以及你的个人付费墙子域名(格式类似 `paywalls-....fnlfx.com`)。要查找你的子域名,请前往 **Settings > Domains**,复制 **Hosted subdomain** 的值。
Paddle 的审批流程为人工审核,你需要等待域名状态从 `Pending` 变为 `Approved`。
**FunnelFox Billing**:
请按照 [FunnelFox Billing 集成说明](https://funnelfox.com/docs/billing/integration-billing-funnelfox)进行操作。
**SolidGate**:
1. 在 Solidgate 看板中,前往 **Developers > Apple Pay Domains**。
2. 点击 **+ Add new domain**,粘贴您的项目域名(来自 FunnelFox 的 **Settings > Domains**)。如有自定义域名,也一并添加。
3. 若要在预览模式下使用 Apple Pay,还需添加 `http://app.funnelfox.com/`。
## 创建并配置网页付费墙 \{#create-and-configure-a-web-paywall\}
1. 在网页付费墙列表页面,点击 **Create a paywall**。
2. 输入付费墙名称,然后点击 **Create**。
3. 系统将自动跳转到一个基础模板,其中包含两个订阅选项和 Apple Pay 购买按钮。
The first screen lists the subscription plans. The second and third screens are checkout screens. Each screen corresponds to one plan you offer. If you have only one plan, delete the extra screen. If you have more, you need to duplicate the checkout screens.
The last screen users see after a successful purchase is where you need to clearly indicate that they can return to your app.
4. 设置方案列表:添加或删除方案和价格。屏幕上显示的所有价格和方案均不会动态添加,因此需要手动配置。
5. 为每个方案添加或配置结账页面。建议在每个结账页面添加总金额,让用户在点击购买按钮之前了解需要支付的费用。
6. 在结账页面中,Apple Pay 按钮已默认存在。若要使其正常工作,请在每个页面上配置以下内容:
1. **Product type**:选择是否要添加试用期或折扣。
2. **Trial period**:输入试用期时长。
3. **Product**:从您的支付提供商中选择产品。
:::important
请确保该产品已添加到 Adapty。否则,购买结果将被设置为默认值。
:::
4. **Subscription discount**:可选,从您的支付提供商中选择优惠券。
7. 现在,您需要将方案与结账页面关联。在方案选择页面,点击 **Continue** 按钮,然后为每个方案选择目标页面。
当付费墙准备好后,你需要获取其链接以在 Adapty 中激活该付费墙。获取方式取决于你是在测试还是在生产环境中发布:
1. **沙盒测试**:点击右上角的 **Preview**,复制链接。
2. **生产环境**:点击右上角的 **Publish**,然后点击 **Home**,从 **URL** 列中复制链接。
就这些!使用此链接[继续完成设置](web-paywall#step-2-trigger-the-paywall)。
---
# File: fallback-paywalls
---
---
title: "备用付费墙"
description: "使用备用付费墙确保 Adapty 中流畅的用户体验。"
---
为了保持流畅的用户体验,请务必为你的[付费墙](paywalls)和[用户引导](onboardings)设置**备用版本**。
当应用加载付费墙时,Adapty SDK 会向服务器请求付费墙配置数据。但如果设备因网络问题或服务器故障无法连接到 Adapty,会发生什么呢?
* 如果用户之前访问过该付费墙,且设备已缓存其数据,应用将**从缓存**加载付费墙数据。
* 如果设备未缓存付费墙,应用会查找本地存储的配置文件,从而在不报错的情况下展示付费墙。
Adapty 会自动生成备用配置文件供你下载使用。每个文件包含*所有*版位的平台专属配置。
## 快速开始 \{#get-started\}
1. 从 Adapty [下载备用配置文件](/local-fallback-paywalls)。
2. 使用 Adapty SDK 配置备用付费墙:
* [iOS](ios-use-fallback-paywalls)
* [Android](android-use-fallback-paywalls)
* [React Native](react-native-use-fallback-paywalls)
* [Flutter](flutter-use-fallback-paywalls)
* [Unity](unity-use-fallback-paywalls)
* [Kotlin Multiplatform](kmp-use-fallback-paywalls)
* [Capacitor](capacitor-use-fallback-paywalls)
## 限制说明 \{#limitations\}
备用付费墙是硬编码并本地存储的,因此不具备常规 Adapty 付费墙的动态能力。
* 备用付费墙不支持[国际化](paywall-localization)。Adapty 生成配置文件时,默认使用 `en` 语言区域。
* 每个版位只能有一个备用付费墙。如果你的配置中针对不同[目标受众](audience)设置了不同的付费墙,Adapty 会使用面向"所有用户"的配置。
* 备用付费墙不支持 [A/B 测试](ab-tests)。如果付费墙参与了 A/B 测试,其备用配置文件将包含权重最高的实验变体。
* 备用付费墙不支持[远程管理](customize-paywall-with-remote-config)。如需更新配置文件,必须在 App Store / Google Play 上发布新版本的应用。
---
# File: local-fallback-paywalls
---
---
title: "下载备用付费墙"
description: "在 Adapty 中使用本地备用付费墙,确保订阅流程顺畅无阻。"
---
Adapty 会自动为你的[备用付费墙](/fallback-paywalls)生成 JSON 配置文件,每个平台对应一个文件。这些文件同时包含用户引导的备用数据。
如果某个版位下有多个付费墙或用户引导,备用版本将包含权重最高或受众范围最广的那个变体。每当你修改付费墙或用户引导时,Adapty 都会更新这些文件。
请按照以下步骤下载备用配置:
1. 打开 **[Placements](https://app.adapty.io/placements)** 页面。
2. 点击 **Fallbacks** 按钮。
3. 从下拉菜单中选择目标平台(*iOS* 或 *Android*)。
4. 选择你的 SDK 版本以开始下载。
## 下载后的操作 \{#after-the-download\}
请根据你的具体平台参考相应的配置指南:
* [iOS](ios-use-fallback-paywalls)
* [Android](android-use-fallback-paywalls)
* [React Native](react-native-use-fallback-paywalls)
* [Flutter](flutter-use-fallback-paywalls)
* [Unity](unity-use-fallback-paywalls)
* [Kotlin Multiplatform](kmp-use-fallback-paywalls)
* [Capacitor](capacitor-use-fallback-paywalls)
---
# File: paywall-metrics
---
---
title: "付费墙数据图表"
description: "跟踪和分析付费墙性能指标,以提升订阅收入。"
---
Adapty 收集一系列数据图表,帮助您更好地衡量付费墙的表现。所有数据图表均实时更新,但浏览量除外(每隔几分钟更新一次)。除浏览量外,所有数据图表均归因于付费墙内的产品。本文档概述了可用的数据图表、其定义及计算方式。
付费墙数据图表在付费墙列表中即可查看,让你一览所有付费墙的整体表现。这个汇总视图展示了每个付费墙的聚合指标,帮助你评估各付费墙的效果并找出可优化的方向。
如需对某个付费墙进行更深入的分析,可以进入该付费墙的详细数据图表页面。在这里,你将看到所选付费墙的全面专项指标,从而获得更深层的性能洞察。
### 按安装日期筛选数据图表 \{#filter-metrics-by-install-date\}
付费墙、试用和购买的数据图表可以按两种不同的日期维度进行分组:
- **事件日期** — 付费墙被查看、试用开始或购买发生的时间。
- **安装日期** — 用户首次打开应用的时间。
对于同一日期范围,两种视图呈现的数据可能差异显著。**按安装日期筛选数据图表** 复选框用于控制看板采用哪种分组方式:
- **未勾选(默认)**:数据图表按事件日期分组。
- **已勾选**:数据图表按安装日期分组。
**示例。** 将日期范围设置为 4 月 1 日至 30 日,查看试用数据。
- **未勾选**:显示 4 月内*开始*的试用,无论这些用户何时安装应用。
- **已勾选**:显示 4 月内*安装*应用的用户所产生的试用,无论其试用何时开始。
使用安装日期视图可衡量特定同期群的用户获取效果;使用事件日期视图可衡量特定时段内付费墙或用户引导的活跃情况。
### 数据图表控件 \{#metrics-controls\}
系统根据所选时间段显示数据图表,并按左侧列参数以三级缩进方式进行组织。
对于正在运行的付费墙,数据图表涵盖从付费墙启动日期至当前日期的时间段。对于已停用的付费墙,数据图表涵盖从启动日期到所选时间段结束日期的完整时间段。草稿和已归档的付费墙也会显示在数据图表表格中,但如果这些付费墙没有可用数据,则仅列出条目而不显示任何数据图表。
#### 数据图表的查看选项 \{#view-options-for-metrics-data\}
付费墙页面提供两种数据图表查看方式:按版位查看和按目标受众查看。
在按版位查看模式下,数据图表按与该付费墙关联的版位进行分组,方便用户按不同版位分析数据。
在目标受众视图中,数据图表按付费墙的目标受众分组,用户可以查看不同目标受众细分下的具体数据。您可以通过付费墙详情页顶部的下拉选项切换视图。
#### 时间范围 \{#time-ranges\}
您可以从多种时间周期中选择来分析数据图表,支持按天、周、月或自定义日期范围进行聚焦查看。
#### 可用筛选条件与分组 \{#available-filters-and-grouping\}
:::link
主要文章:[Analytics controls](controls-filters-grouping-compare-proceeds)
:::
Adapty 提供了强大的工具,帮助你根据需求对数据图表分析进行过滤和自定义。在 Adapty 的数据图表页面,你可以使用多种时间范围、分组选项和过滤条件。
- 过滤条件:目标受众、国家、付费墙、付费墙状态、付费墙分组、版位、国家、商店、产品及产品商店。
- 分组方式:产品和商店。
#### 单项数据图表 \{#single-metrics-chart\}
付费墙数据图表页面的核心组成部分之一是数据图表区域,它以可视化方式呈现所选数据图表,便于分析。
付费墙数据图表页面的图表区域包含一个水平条形图,直观地展示所选数据图表的具体数值。图表中的每条横条对应一个数据图表值,并按比例缩放,让你一眼就能看懂数据。水平轴表示分析的时间范围,垂直列显示各数据图表的数值。图表旁边还会显示所有数据图表值的总和。
此外,点击数据图表区域右上角的箭头图标可展开视图,以完整折线图的形式展示所选数据图表。
#### 数据汇总 \{#total-metrics-summary\}
单项数据图表旁边会显示数据汇总区域,展示特定时间点所选数据图表的累计值,你可以通过下拉菜单切换要显示的数据图表。
### 数据图表定义 \{#metrics-definitions\}
:::note
Adapty 使用 [currencylayer.com](https://currencylayer.com/) 的汇率(每 8 小时更新一次)将其他货币换算为美元。汇率**在交易发生时锁定**——后续汇率变动不会影响已换算的结果。
:::
#### 收入 \{#revenue\}
该数据图表表示通过购买和续订产生的 USD 总金额。请注意,收入计算不包含 App Store / Play Store 的佣金,且为扣除任何费用之前的金额。
#### 实际到账金额 \{#proceeds\}
该数据图表表示应用所有者在扣除适用的 App Store / Play Store 佣金后,从购买和续订中实际收到的 USD 金额。
:::important
如果您的应用已加入佣金减免计划,请告知 Adapty。为确保计算结果准确,请在[应用设置](general)中指定您的 [Small Business Program](app-store-small-business-program) 和 [Reduced Service Fee program](google-reduced-service-fee) 状态。
:::
它反映了直接贡献于应用收益的净收入。有关收益计算方式的更多信息,请参阅 Adapty [文档](analytics-cohorts#revenue-vs-proceeds)。
#### ARPPU
ARPPU 是每位付费用户的平均收入,计算方式为总收入除以唯一付费用户数。例如:$15000 收入 / 1000 位付费用户 = $15 ARPPU。
#### ARPAS \{#arpas\}
每位活跃订阅者的平均收入,用于衡量每位活跃订阅者产生的平均收入。计算方式为总收入除以已激活试用或订阅的订阅者数量。例如,若总收入为 $5,000,订阅者数量为 1,000,则 ARPAS 为 $5。该指标有助于评估每位订阅者的平均变现潜力。
#### 独立访客购买转化率(CR)\{#unique-conversion-rate-cr-to-purchases\}
独立访客购买转化率的计算方式是:购买次数除以独立访客浏览次数。例如,若有 10 次购买和 100 次独立访客浏览,则独立访客购买转化率为 10%。该指标关注购买次数与独立访客浏览次数的比率,有助于了解将独立访客转化为付费用户的效果。
#### 购买转化率(CR)\{#cr-to-purchases\}
购买转化率的计算方式为购买次数除以总浏览量。例如,若有 10 次购买和 100 次浏览,则购买转化率为 10%。此数据图表表示最终产生购买的浏览量占比,有助于了解付费墙将用户转化为付费客户的效果。
#### 独立用户试用转化率(CR) \{#unique-cr-to-trials\}
独立用户试用转化率的计算方式为已开始的试用次数除以独立浏览量。例如,若有 30 次试用开始和 100 次独立浏览量,则独立用户试用转化率为 30%。此数据图表衡量最终激活试用的独立浏览量占比,有助于了解付费墙将独立访客转化为试用用户的效果。
#### 购买 \{#purchases\}
购买代表付费墙上各类交易的累计总量。此数据图表包含以下交易类型(不含续订):
- 直接在付费墙上完成的新购买。
- 最初在付费墙上激活的试用转化。
- 在付费墙上进行的订阅降级、升级和跨级操作。
- 在付费墙上恢复的订阅,例如在无自动续订的情况下到期后重新激活订阅。
通过综合考虑这些不同类型的交易,购买数据图表提供了付费墙上整体获客和变现活动的全面视图。
#### 试用 \{#trials\}
试用数据图表表示已激活的试用总次数,反映通过付费墙发起试用期的用户数量。此数据图表有助于跟踪试用产品的效果,并可提供有关用户参与度以及从试用转化为付费订阅的洞察。
#### 已取消试用 \{#trials-canceled\}
已取消试用数据图表表示已关闭自动续订功能的试用数量。当用户手动取消订阅试用时,即表明其决定在试用期结束后不继续订阅。跟踪已取消试用可提供有关用户行为的宝贵信息,帮助您了解用户退出试用的比率。
#### 退款 \{#refunds\}
退款数据图表表示已退款的购买和订阅数量。这包括因各种原因被撤销或退款的交易,例如客户申请、付款问题或其他适用的退款政策。
#### 退款率 \{#refund-rate\}
退款率的计算方式为:退款数量除以首次购买数量(不含续订)。例如,若有 5 笔退款和 1000 笔首次购买,则退款率为 0.5%。
#### 浏览量 \{#views\}
浏览量数据图表表示用户查看付费墙的总次数。每次用户访问付费墙时,均计为一次单独的浏览。例如,若一个用户访问付费墙两次,则记录为两次浏览。跟踪浏览量有助于了解用户对付费墙的参与程度和互动情况,并为付费墙版位及设计的有效性提供洞察。
#### 独立浏览量 \{#unique-views\}
独立浏览量数据图表表示用户查看付费墙的独立实例数量。与总浏览量将每次访问计为单独一次不同,独立浏览量对每位用户访问付费墙仅计数一次,无论其访问多少次。例如,若一个用户访问付费墙两次,则记录为一次独立浏览。跟踪独立浏览量有助于更准确地衡量用户参与度和付费墙的覆盖范围,因为它侧重于个别用户而非总访问次数。
:::warning
请务必使用 `.logShowFlow()`(iOS SDK v4+)/ `.logShowPaywall()` 方法向 Adapty 上报付费墙的展示事件。否则,付费墙的展示次数将不会计入数据图表,转化率数据也会失去参考价值。
:::
---
# File: migrate-paywalls
---
---
title: "在应用之间迁移付费墙"
description: "了解如何在 Adapty 中从其他应用迁移付费墙。"
---
使用 Adapty,您无需为每个应用从头构建新的付费墙。如果您管理多个应用,可以将任何通过编辑工具创建的付费墙的付费墙编辑工具配置从一个应用迁移到另一个应用。
迁移允许您复制所有视觉配置:
- 付费墙及所有付费墙元素的布局设置
- 媒体资源
- 本地化
迁移仅适用于编辑工具配置,不会复制产品或远程配置。
:::note
如果您迁移的付费墙编辑工具配置包含自定义字体,请在设备上进行测试,因为这些字体可能显示不正确。
:::
## 迁移付费墙 \{#migrate-paywall\}
:::important
您只能迁移在**新版** Adapty 付费墙编辑工具中创建的付费墙。若要迁移**旧版**付费墙编辑工具创建的付费墙,必须先将其迁移至新版付费墙编辑工具。
:::
要迁移付费墙编辑工具配置:
1. **对于新付费墙**:开始[创建付费墙](create-paywall)并添加产品。然后,点击 **Build no-code paywall** 以打开模板库。
**对于已有付费墙**:前往 **Builder & Generator** 标签页的 **Layout settings** 部分,点击 **Change template**。
2. 在编辑付费墙模板时,点击 **Copy a design from your apps** 框内的 **Choose paywall**。
3. 选择您想要复制配置的应用和付费墙。
4. 点击 **Copy Selected Paywall**。
迁移完成后,您可以进行任何所需的编辑,这些更改不会影响原始付费墙。
---
# File: duplicate-paywalls
---
---
title: "复制付费墙"
description: "了解如何在 Adapty 中管理重复的付费墙并优化付费墙性能。"
---
如果您需要对 Adapty 中的现有付费墙进行少量修改,尤其是当该付费墙已在您的移动应用中使用,且您不希望影响分析数据时,您可以直接复制它。您可以根据需要使用这些副本来替换部分或全部版位中的原始付费墙。
复制操作会创建一个包含付费墙所有详细信息的副本,例如名称、产品以及任何促销活动。新付费墙的名称将添加"Copy"后缀,以便您轻松与原始付费墙区分。
在 Adapty 看板中复制付费墙的步骤如下:
1. 在 Adapty 主菜单中打开 [**Paywalls**](https://app.adapty.io/paywalls) 板块。Adapty 看板的付费墙列表页面提供了您账户中所有付费墙的概览。
2. 点击付费墙旁边的 **3-dot** 按钮,然后选择 **Duplicate** 选项。
3. 调整新付费墙的设置,然后点击 **Save** 按钮。
4. 如果原始付费墙当前已在某个版位中使用,Adapty 将提示您是否要在版位中将原始付费墙替换为其副本。如果您选择 **Create and replace original**,新付费墙将立即变为 **Live** 状态。或者,您也可以将它们创建为 **Draft** 状态的新付费墙,稍后再将其添加到版位中。
---
# File: archive-paywalls
---
---
title: "归档付费墙"
description: "了解如何在 Adapty 中归档过时的付费墙,同时不丢失数据。"
---
随着您深入使用 Adapty 并不断调整付费墙设置,可能会积累一些不再符合当前策略或活动的付费墙。这些处于 `Inactive`(未激活)状态的付费墙会使您的工作区变得杂乱,让您难以找到最重要的付费墙。为解决这一问题,Adapty 推出了归档不必要付费墙的功能。
归档可确保这些付费墙被安全保存而不会被永久删除,日后如有需要随时可以访问。此外,已归档的付费墙可以从默认视图中过滤掉,从而整理您的工作区,简化用户界面。在本指南中,我们将带您了解如何在 Adapty 中高效地归档付费墙,让您对付费墙管理流程拥有更强的掌控力。
温馨提示:当前在至少一个版位中处于激活状态的付费墙无法被归档。如果您希望归档此类付费墙,请事先将其从所有版位中移除。
:::note
如果付费墙正在用于未归档的 A/B 测试中,则无法将其归档。这样用户可以查看已完成的 A/B 测试的详细数据图表,而关联的付费墙也是该数据的一部分。
:::
**归档付费墙的步骤:**
1. 在 Adapty 主菜单中打开 [**Paywalls**](https://app.adapty.io/paywalls) 部分。
2. 点击付费墙旁边的 **3-dot** 按钮,然后选择 **Archive** 选项。
3. 在 **Archive paywall** 窗口中,输入您希望归档的付费墙名称,然后点击 **Archive** 按钮。
---
# File: restore-paywall
---
---
title: "从存档中恢复付费墙"
description: "在 Adapty 中恢复付费墙,以确保为用户提供不间断的订阅服务。"
---
存档付费墙的功能对于简化付费墙管理流程非常有帮助。它允许您隐藏不再需要的付费墙,从而减少工作区中的混乱。此外,恢复已存档付费墙的选项提供了灵活性,使您能够在需要时将其重新纳入策略。
已存档的付费墙可能会在默认视图中被过滤掉。要查看它们,请在 **State** 筛选器中选择 **Archived**。
**将付费墙从存档中恢复**
1. 在 Adapty 主菜单中打开 [**Paywalls**](https://app.adapty.io/paywalls) 部分。
2. 确保已存档的付费墙显示在列表中。如果没有,请更新右侧的筛选器。
3. 点击已存档付费墙旁边的 **3-dot** 按钮,然后选择 **Back to active**。
---
# File: profiles-crm
---
---
title: "用户画像/CRM"
description: "在 Adapty 中管理用户画像和 CRM 数据,以增强目标受众细分能力。"
---
用户画像是面向你的用户的 CRM 系统。通过用户画像,你可以:
1. 通过用户画像 ID、Customer User ID、邮箱或交易 ID 查找特定用户。
2. 查看用户的事件时间线,包括账单问题、宽限期及其他[事件](events)。
3. 分析用户属性,如订阅状态、总收入/实际收益等。
4. 为用户授予订阅。
:::note
事件feed中的事件到达看板时会有延迟。新的用户画像和属性变更可能不会立即显示。
:::
:::link
要了解 Adapty 如何创建和关联用户画像,请参阅[用户画像的工作原理](how-profiles-work)。
:::
## 查找用户 \{#finding-users\}
在用户画像列表中,你可以通过以下方式搜索特定用户:
- **Profile ID**:Adapty 对该用户的内部标识符(也称为 Adapty ID)。
- **Customer user ID**:你的应用为该用户设置的标识符(如已设置)。
- **Email**:用户的电子邮件地址(如已作为自定义属性传入)。
- **Transaction ID**:购买时产生的应用商店交易 ID。
点击任意一行即可打开该用户的完整用户画像。
## 订阅状态 \{#subscription-state\}
在用户画像列表中,您可以按订阅状态对用户进行筛选和排序。状态值如下:
| 用户**状态** | 描述 |
| :--------------------- | :----------------------------------------------------------- |
| Subscribed | 用户拥有有效订阅,且自动续订已开启。 |
| Auto-renew off | 用户已关闭自动续订,但在订阅期结束前仍可使用高级功能。 |
| Subscription cancelled | 用户已取消订阅,且订阅已完全终止。 |
| Billing issue | 用户在订阅或试用期到期后因账单问题无法完成扣款。 |
| Grace period | 用户当前处于宽限期,原因是在订阅或试用期到期后尝试扣款时发生了账单问题。 |
| Active trial | 用户拥有有效订阅,当前处于试用期。 |
| Trial cancelled | 用户已取消试用,且没有有效订阅。 |
| Never subscribed | 用户从未订阅或开始试用,仍为免费用户。 |
## 用户属性 \{#user-attributes\}
您可以通过 SDK 向 Adapty 发送额外的用户属性。
默认情况下,Adapty 会设置:
| 属性 | 描述 |
| ------------------ | ------------------------------------------------------------ |
| Customer user ID | 您的系统中终端用户的标识符。 |
| Adapty ID | Adapty 内部的终端用户标识符,即 Profile ID。 |
| IDFA | 广告主标识符,由 Apple 分配给用户设备。在 iOS 14+ 上需要 App Tracking Transparency (ATT) 权限。Android 不可用。 |
| Country | 终端用户所在国家/地区。 |
| OS | 终端用户使用的操作系统。 |
| Device | 终端用户可见的设备型号名称。 |
| Install date | 用户在 Adapty 中首次被记录的日期:
## 授予订阅 \{#granting-a-subscription\}
在用户画像中,你可以延长活跃订阅的有效期,或授予用户某个访问等级的永久授权——无需用户实际购买。
以下场景中此功能最为实用:
- 在账单或支持问题后对用户进行补偿。
- 执行手动促销活动或 Beta 测试计划。
- 无需真实购买即可测试订阅流程。
要授予访问权限,请打开用户的用户画像,进入 **Access levels** 部分,然后点击 **Edit**。设置到期日期并保存。到期日期必须是未来的时间,且一旦设置后不能缩短。调整活跃订阅的到期日期不会影响正在进行中的付款。
:::note
授予访问权限不会创建 App Store 或 Google Play 购买事件。用户的事件记录和分析数据将与真实购买流程有所不同。
:::
您也可以使用 [Grant access level](api-adapty/operations/grantAccessLevel) API 方法以编程方式授予访问权限。
## 在用户账户之间共享付费访问权限 \{#sharing-paid-access-between-user-accounts\}
:::link
主要文章:[在用户账户之间共享付费访问权限](sharing-paid-access-between-user-accounts)
:::
### 访问等级共享历史 \{#access-sharing-history\}
当访问等级被共享或转让时,用户的用户画像会显示一个指向关联用户画像的链接——即共享访问的用户画像或接收访问的用户画像。要查看关联的用户画像,请在用户的 **Profile** 中,点击访问等级旁边的链接。
:::note
虚拟货币余额不像访问等级那样在用户画像之间共享或转移。每个余额仅归属于单个用户画像——详见[余额、用户画像与设备](virtual-currency-balance#balances-profiles-and-devices)。
:::
## 后续步骤 \{#next-steps\}
- 要了解 Adapty 如何创建和关联用户画像,请参阅[用户画像的工作原理](how-profiles-work)。
- 要配置访问共享策略,请参阅[在用户账户之间共享付费访问等级](sharing-paid-access-between-user-accounts)。
- 要以编程方式授予访问权限,请参阅 [授予访问等级](api-adapty/operations/grantAccessLevel) API 方法。
---
# File: how-profiles-work
---
---
title: "用户画像的工作原理"
description: "了解 Adapty 如何创建、跟踪和关联用户画像,包括匿名用户画像、已识别用户及父/继承关系。"
---
您应用中的每位用户都会获得一个 Adapty 用户画像,用于追踪其购买记录、事件和订阅状态。了解用户画像的创建和关联方式,有助于您避免集成错误、防止数据碎片化,并正确解读 [用户画像](profiles-crm) 页面中的数据。
## 创建用户画像 \{#profile-creation\}
Adapty 会在用户首次打开应用时自动创建用户画像。
**未设置 Customer User ID 时**,用户画像为匿名状态。每当以下情况发生时,系统会创建新的匿名用户画像:
- 用户重新安装应用
- 用户退出应用(当应用调用 `Adapty.logout()` 时)
购买记录与应用安装绑定,而非与持久的用户身份关联。
**设置了 Customer User ID 时**,用户画像可在重装和多设备间持久保留。使用 Customer User ID 可以让你:
1. 跨应用重装和多设备追踪用户。
2. 在 [**Profiles**](profiles-crm) 页面通过 customer user ID 查找用户。
3. 在[服务端 API](getting-started-with-server-side-api) 中使用 customer user ID。
4. Adapty 会将 customer user ID 发送给所有集成渠道。
设置 customer user ID 的时机不同,用户画像的行为也会有所差异:
- **在 SDK 激活时**:Adapty 会使用该 customer user ID 对应的现有用户画像(针对已有用户),或创建新的用户画像(针对首次使用的用户)。
- **在 SDK 激活后**:Adapty 在激活时创建一个匿名用户画像。当你稍后识别用户身份时,Adapty 会将 customer user ID 关联到该匿名用户画像(针对首次使用的用户),或切换到已有该 ID 的用户画像(针对已有用户)。
**如何选择:**
- **应用启动时即可获取 Customer user ID**(例如从上次会话中已保存)——在初始化 SDK 时将其传入 `activate()`。
- **用户在应用启动后登录**——在身份验证完成后调用 `identify()`。若该 ID 是新 ID,Adapty 会将其关联到当前用户画像;若该 ID 已存在,则切换到对应的已有用户画像。
- **用户可在登录前购买**——在登录完成后调用 `identify()`。若该 Customer user ID 在 Adapty 中已存在,请在调用后重新获取用户画像,以同步当前的访问等级。
有关实现细节,请参阅 [用户识别](identifying-users) SDK 指南。
:::note
如果某个回访用户此前在没有 customer user ID 的情况下使用过你的应用,当你在 SDK 初始化时开始识别用户后,这些匿名用户画像不会自动合并。若需要为此类用户保留完整历史记录,请在登录后改用 `identify()`。
:::
## 父画像与继承画像 \{#parent-and-inheritor-profiles\}
当同一个商店端订阅与多个 Adapty 用户画像关联时,Adapty 会将这些画像视为一条链:一个**父**画像,以及一个或多个从同一购买中共享访问权限的**继承**画像。
出现这种情况的原因如下:
- [用户账户之间共享付费访问权限](sharing-paid-access-between-user-accounts)已启用,且某用户在一台设备上登录,而该设备上此前已有另一个用户画像完成了购买。
- 用户在未设置 `customer_user_id` 的情况下重新安装应用,新用户画像继承了上一次安装的购买记录。
- 不同的已识别用户在同一台设备上恢复了购买。
- 应用在 Apple Team ID 之间进行了迁移,新应用继承了旧 Team ID 下的购买记录。
**父级用户画像的选择方式。**
**父级**是 **第一个记录购买行为的用户画像** — 由 Adapty 中的购买收据顺序决定,而非用户画像的创建顺序。例如:你安装应用后未进行任何购买,然后重新安装并购买了订阅。第二个用户画像成为父级,因为它完成了购买。第一个用户画像成为继承方,并通过共享获得访问权限。
**事件的分配方式:**
- **事务性事件**(购买、续订、取消、账单问题、宽限期、退款):仅出现在发起购买的**父用户画像**上。所有订阅续订和更新都将继续显示在该用户画像上。
- **`access_level_updated` 事件**:每当访问等级状态发生变化时,**父用户画像和继承用户画像都会**收到该事件。这样可以确保所有关联的用户画像都能及时获知当前的访问状态。
父级用户画像显示完整的交易历史记录。继承者用户画像仅在 **Access level** 部分显示其访问等级更新内容以及指向父级用户画像的链接。
**跨用户画像追踪同一订阅。**
每个继承者用户画像都有独立的 `profile_id`,因此 `profile_id` 在整个链路中并不稳定。若要在多个用户画像之间识别同一笔订阅——例如在核对 webhook 事件或将看板中的用户画像与同一底层用户进行匹配时——请改用渠道侧的标识符。
| 字段 | 用途 |
| --- | --- |
| `store_original_transaction_id` | 跨用户画像识别订阅链。每个 Apple 订阅唯一。 |
| `profiles_sharing_access_level`(webhook 字段) | 启用共享时,当前享有该订阅访问等级的所有用户画像。 |
| `profile_id` | **不**适合跨用户画像追踪——每个继承者都有自己的 `profile_id`。 |
## 没有用户画像的交易 \{#transactions-without-profiles\}
在 Adapty 中,有些交易没有关联任何用户画像——它们会出现在数据分析和导出结果中,但不会显示在用户画像列表里。这类情况发生在**服务器间(S2S)商店通知**中,即这些通知对应的用户从未通过 Adapty SDK 连接过你的应用。已知来源包括:
- App Store S2S 通知(包括退款事件)
- Google Play S2S 通知
- Stripe 和 Paddle Webhook 事件
这些交易:
- **出现在分析数据图表中**(计入整体数据指标)
- **出现在导出数据中**(S3、GCS、BigQuery),其中 `profile_id` 设置为 `null`
- **不出现在用户画像列表中** — 因为没有关联的用户画像
如果你在分析或导出数据中看到的事件数量多于用户画像界面中能找到的数量,差异部分很可能就是这些没有关联用户画像的交易记录。要在导出数据中找到它们,可以筛选 `profile_id IS NULL` 的行。
## 在用户账户之间共享付费访问权限 \{#sharing-paid-access-between-user-accounts\}
:::link
主要文章:[在用户账户之间共享付费访问权限](sharing-paid-access-between-user-accounts)
:::
要设置您的访问等级共享策略,请在 [**General**](general) 设置页面上选择一个共享选项。您可以为[沙盒环境](test-purchases-in-sandbox)单独设置策略。
**已启用(默认)**
已识别用户(即设置了 [Customer User ID](identifying-users#set-customer-user-id-on-configuration) 的用户)在设备登录相同 Apple/Google ID 的情况下,可以共享 Adapty 提供的同一[访问等级](access-level)。这在用户重新安装应用并使用不同邮箱登录时非常有用——他们仍然可以访问之前的购买内容。使用此选项时,多个已识别用户可以共享同一访问等级。
尽管访问等级是共享的,但所有过去和未来的交易都会作为事件记录在原始 Customer User ID 下,以保持数据一致性并完整保留交易历史——包括试用期、订阅购买、续订等,均关联到同一用户画像。
**将访问权限转移给新用户**
已识别用户可以继续访问 Adapty 提供的[访问等级](access-level),即使他们使用不同的 [Customer User ID](identifying-users#set-customer-user-id-on-configuration) 登录或重新安装应用,只要设备登录的是相同的 Apple/Google ID 即可。
与上一选项不同,Adapty 会在已识别用户之间转移购买记录。这确保购买内容始终可用,但同一时间只有一个用户能拥有访问权限。例如,如果 UserA 购买了订阅,而 UserB 在同一设备上登录并恢复了交易,则 UserB 将获得该订阅的访问权限,UserA 的访问权限将被撤销。
如果其中一个用户(无论新用户还是旧用户)未被识别,Adapty 中这些用户画像之间的访问等级仍会共享。
尽管访问等级会被转移,但所有过去和未来的交易都会作为事件记录在原始 Customer User ID 下,以保持数据一致性并完整保留交易历史——包括试用期、订阅购买、续订等,均关联到同一用户画像。
切换到**将访问权限转移给新用户**后,用户画像之间的访问等级不会立即转移。每个特定访问等级的转移流程仅在 Adapty 收到来自商店的事件时触发,例如订阅续订、恢复购买或验证交易时。
**已禁用**
第一个获得访问等级的已识别用户画像将永久保留该访问等级。如果你的业务逻辑要求购买记录必须绑定到单个 Customer User ID,这是最佳选项。
请注意,访问等级在匿名用户之间仍会共享。
你可以通过[删除所有者的用户画像](https://adapty.io/docs/zh/api-adapty/operations/deleteProfile)来"解绑"购买记录。删除后,访问等级将归属于第一个声明它的用户画像,无论是匿名用户还是已识别用户。
禁用共享仅影响新用户。已在用户之间共享的订阅在禁用此选项后仍会继续共享。
:::warning
Apple 和 Google 要求在用户之间共享或转移应用内购买,因为这些购买是依赖 Apple/Google ID 进行关联的。如果不启用共享,用户在重新安装应用后可能无法恢复购买。
禁用共享可能导致用户登录后无法重新获得访问权限。
我们建议仅在用户**必须先登录**才能进行购买的情况下禁用共享。否则,已识别用户可能在购买订阅后登录另一个账号,从而永久失去访问权限。
:::
### 应该选择哪个设置?\{#which-setting-should-i-choose\}
| 我的应用…… | 推荐选项 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 没有登录系统,仅使用 Adapty 的匿名用户画像 ID。 | 使用默认选项,因为对于所有三个选项,匿名用户画像 ID 之间的访问等级始终是共享的。 |
| 有可选登录系统,允许用户在创建账号之前进行购买。 | 选择**将访问权限转移给新用户**,确保未登录账号就完成购买的用户之后仍能恢复交易。 |
| 要求用户在购买前创建账号,但允许购买记录关联到多个 Customer User ID。 | 选择**将访问权限转移给新用户**,确保同一时间只有一个 Customer User ID 拥有访问权限,同时允许用户使用不同 Customer User ID 登录而不丢失已付费的访问权限。 |
| 要求用户在购买前创建账号,并严格规定购买记录只能绑定到单个 Customer User ID。 | 选择**已禁用**,确保交易记录永远不会在账号之间转移。 |
## 事件时间戳显示为未来日期(Apple/iOS)\{#event-timestamps-with-future-dates-appleios\}
此行为仅在 Apple App Store 中存在,Google Play 的通知系统不会提前发送事件。
用户画像和集成中的事件时间戳可能显示为未来日期,这是因为 Apple 会提前发送续订事件。
- **发生原因**:Apple 这样做是为了确保订阅在到期前自动续订,防止用户服务中断。更多详情请参阅 Apple 开发者论坛:[Server Notifications for Subscriptions](https://developer.apple.com/forums/tags/app-store-server-notifications)。
- **受影响的事件类型**:通常,此情况适用于订阅续订和试用转付费转化。这些事件可能带有未来时间戳,因为 Apple 会提前通知系统。
- **其他事件类型**:额外的应用内购买和订阅计划变更会以实际时间戳记录,因为这些事件无法提前预测。
- **对分析和事件流的影响**:这些事件只有在其时间戳过后才会出现在 **Analytics** 和 **Event Feed** 中。带有未来时间戳的事件不会显示在这两个板块中。
- **对集成的影响**:Adapty 会在收到事件后立即将其发送至集成。如果事件带有未来时间戳,Adapty 会将该事件连同未来时间戳原样发送至您的集成。
## 后续步骤 \{#next-steps\}
- 若要使用用户画像看板查找和管理用户,请参阅 [用户画像](profiles-crm)。
- 若要在您的应用中设置用户身份识别,请参阅 [识别用户](identifying-users) SDK 指南。
- 若要配置访问共享策略,请参阅 [在用户账户之间共享付费访问权限](sharing-paid-access-between-user-accounts)。
---
# File: sharing-paid-access-between-user-accounts
---
---
title: "在用户账户之间共享付费访问权限"
description: "在不同用户账户之间共享付费访问权限,以适应拥有多台设备或多个应用账户的用户"
---
当用户完成购买后,Adapty 会为其当前的[用户画像](identifying-users)分配新的[访问等级](access-level),该等级授权购买者访问付费内容。
如果用户重新安装应用或登录新的应用内账号,买家的用户画像可能会发生变化。为确保访问不中断,Adapty 会自动在原始用户画像与后续用户画像之间共享用户的访问等级。
这种方式适用于大多数应用。但如果您的业务逻辑有特殊需求,也可以选择限制性更强的付费访问共享策略。
打开 [General Settings](https://app.adapty.io/settings/general) 页面,设置访问等级共享策略。为方便测试,你可以仅针对[沙盒环境](#sharing-paid-access-on-sandbox)更改此设置。
## 已启用(默认) \{#enabled-default\}
此设置最适合**没有内置身份验证**的应用程序。购买完成后,与同一应用商店账户关联的所有用户画像将自动*继承*该访问等级。
* 如果用户使用新的凭证登录您的应用,他们仍可访问已购内容。
* 如果用户在恢复出厂设置后重新安装应用,他们仍可访问已购内容。
* 如果用户使用相同的应用商店账号在其他设备上安装该应用,购买内容将在所有设备上可用。即使每个应用实例拥有各自独立的客户用户画像。
## 将访问权限转移给新用户 \{#transfer-access-to-new-user\}
此设置最适合允许**有无账号均可购买**的应用,或希望强制执行**每位用户仅限一台设备**策略的应用。
Adapty 每次限制一个客户 ID 持有购买访问权限。设备所有者可以重新安装应用、登录或退出账号,但无法同时从多个客户 ID 访问同一产品。
启用此设置后,匿名用户画像(例如,用户登出后激活的用户画像)始终继承上一个活跃 customer ID 的访问等级。这样可以防止用户之后失去访问权限。
:::warning
当你关闭默认设置并启用 **Transfer access to new user** 后,Adapty 不会立即更新现有 customer 用户画像的访问等级。
切换操作会在用户触发新的商店事件时发生:例如,续订订阅或恢复购买。
:::
:::important
只有当 SDK 传播交易时新用户画像已设置 [Customer User ID](identifying-users#set-customer-user-id-on-configuration),Adapty 才会撤销旧用户画像。如果 `restorePurchases` 在匿名用户画像上运行,旧的 Customer User ID 和新的匿名用户画像都会获得访问等级。旧用户画像会在您识别该匿名用户画像后才被撤销。
为避免此问题,请按顺序调用 SDK 方法:`activate` → `identify` → `restorePurchases`。
:::
## 禁用付费访问共享 \{#disable-paid-access-sharing\}
此设置**仅适用于**具有**强制身份验证**或独立访问管理实现的应用程序。在其他情况下,用户可能无法访问其已购买的内容,您的应用程序将面临**无法通过商店强制审核**的风险。
如果您禁用付费访问共享,Adapty 会将产品绑定到购买时处于活跃状态的[客户 ID](identifying-users#set-customer-user-id-on-configuration),且不会与任何其他用户画像共享该访问等级。此策略实现了严格的一对一产品分配。
:::warning
禁用付费访问共享后,客户 ID 将无法继承付费访问权限。如果某个客户 ID 过去已继承了付费访问权限,则无法自动撤销。
:::
:::important
在紧急情况下,您可能需要[删除用户画像](api-adapty/operations/deleteProfile),以便下一个可用的用户画像(无论是已识别的还是匿名的)能够获得其访问等级。
:::
## 实用参考 \{#practical-reference\}
选定合并模式后,以下规则说明了你可以期望的结果:哪些用户画像能看到该访问等级、旧用户画像何时失去访问权限,以及会触发哪些 webhook 事件。
| 模式 | 多个用户画像共享同一次购买? | 转移时吊销旧用户画像? | 何时吊销旧用户画像 | 第二个用户画像认领订阅时触发的 Webhook 事件 |
| --- | --- | --- | --- | --- |
| **已启用(默认)** | 是——每个通过恢复购买或登录的用户画像均可继承访问等级 | 从不 | 不适用 | 每个继承访问权限的新用户画像均触发 `access_level_updated`(`is_active=true`) |
| **将访问权限转移给新用户** | 否——独占,但可在用户画像之间转移 | 是 | 新的已识别设备传播该交易时立即吊销(`restorePurchases`、identify 或下一次商店侧事件) | 新用户画像:`access_level_updated`(`is_active=true`)。旧用户画像:`access_level_updated`(`is_active=false`) |
| **已禁用** | 否——每次购买永久绑定唯一一个 Customer User ID | 不适用——访问权限永不转移 | 不适用 | 第二个用户画像不触发任何事件。SDK 对该用户画像不显示任何访问权限 |
## 在沙盒环境中共享付费访问权限 \{#sharing-paid-access-on-sandbox\}
您可以专门为沙盒环境设置共享付费访问权限策略。在沙盒环境中测试购买时,请注意以下行为:
* Apple 会将您过去的购买记录存储在账户的购买历史中,Adapty SDK 也可以访问这些记录。
* 如果您重新安装应用,Adapty 检测到该产品已被购买,当前用户画像将继承相应的访问等级。
* 如果 Apple 检测到该产品已有购买记录,即使当前用户画像没有所需的访问等级,也不会允许您重复购买同一产品。
此行为**与您的共享付费权限设置无关**。如果您的应用未显示付费墙,您就无法购买该产品。唯一的解决方案是**清除您账号的购买记录**。请参阅[沙盒测试指南](test-purchases-in-sandbox)获取详细说明。
:::warning
沙盒环境中的 Apple 订阅每隔几分钟就会自动续订。这种快速续订可能导致 Adapty 识别的[父级](how-profiles-work#parent-and-inheritor-profiles)用户画像发生切换——这种链式模式在正式环境中很少出现。请在与生产环境一致的模式下进行测试,并在得出结论之前,使用真实 Apple ID 验证实际行为。
:::
## 付费访问共享在分析中的表现 \{#paid-access-sharing-in-analytics\}
* Adapty 按实际发生的交易进行记录。单笔交易可能关联多个用户画像,但不会被重复计算。
* 如果两个或多个用户画像共享同一访问等级,该购买将归因于[父级用户画像](how-profiles-work#parent-and-inheritor-profiles)。
* 访问等级的继承不影响安装量统计。如需了解 Adapty 如何统计安装量,可在设置页面选择两种可用的[安装定义](installs#counting-modes)之一。
---
# File: segments
---
---
title: "市场细分"
description: "在 Adapty 中创建和管理用户市场细分,以实现更精准的定向投放。"
---
**市场细分**是一组过滤条件,用于将具有共同属性的用户归为一类。通过市场细分,可以更精准地定向投放付费墙和 A/B 测试。
:::note
事件feed中的事件到达看板时会有延迟。新的用户画像和属性变更可能不会立即显示。
:::
创建市场细分后,您可以[将其作为**目标受众**用于版位和 A/B 测试](audience),从而控制用户看到哪个付费墙(单个或多个)。示例:
- 向非订阅用户展示标准付费墙,向曾经取消订阅或试用的用户提供折扣优惠。
- 向不同国家的用户展示不同的付费墙。
- 根据 Apple Search Ads 归因数据定向用户。
- 确保使用旧版应用的用户继续看到现有付费墙,而新版本用户看到更新后的付费墙。
- [在分析中](controls-filters-grouping-compare-proceeds#filter-and-group-data),按市场细分筛选数据,查看特定用户群体的表现;按市场细分分组,可在**所有用户**中对比各组表现或贡献占比。
## 创建 \{#creation\}
要创建市场细分,请输入名称并选择定义其过滤条件的属性。当您选择多个属性时,用户必须满足所有条件。Adapty 在属性之间应用 AND 逻辑。
## 可用属性 \{#available-attributes\}
:::note
虽然许多用户属性会自动设置(如 **Country** 或 **Calculated total revenue USD**),但 **Age**、**App user ID**、**Attribution** 数据、**Gender** 和 **Custom attributes** 不会自动定义。如需将这些属性用于市场细分,您必须[设置用户属性](setting-user-attributes)或[传递归因数据](attribution-integration)。
:::
:::tip
对于基于日期的属性,您可以使用以下方式进行筛选:
- **固定日期**:从日历中选择具体日期(例如,向黑色星期五至网络星期一期间安装应用的用户展示特别优惠)
- **相对范围**:设置动态时间窗口,例如"最近 7 天"或"最近 3 个月"(例如,重新触达 30 天以上未活跃的用户,或定向最近安装的用户)
相对范围会自动更新,非常适合持续性活动。固定日期则更适合有时间限制的促销活动。
:::
| 属性 | 筛选依据 |
|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Age** | 用户年龄。请注意,年龄在 Adapty 首次获取时计算,之后不会更新。 |
| **App User ID** | 用户在您应用中的标识符([customer_user_id](profiles-crm#user-attributes))。您可以按其是否存在进行筛选,例如仅向未登录的用户显示付费墙。 |
| **App version (current)** | 用户设备上当前安装的应用版本,即 Adapty 最后一次收到事件数据时的版本——**随用户升级而更新**,始终反映其正在运行的版本。当您需要向所有运行特定版本的用户推送内容(包括从旧版本升级至该版本的用户)时,请使用此条件。在创建市场细分时,点击 **App version** 旁的铅笔图标并添加新版本,即可立即使用。
| 字段 | 说明 |
| ------ |--------------------------------------------------------------------------------------------------------------------------------------|
| **Name** | 自定义属性的标签,仅在 Adapty 看板中显示。 |
| **Key** | 属性的唯一标识符,必须与 SDK 中使用的键名一致。 |
| **Type** | 可选类型:
## 复制市场细分 \{#duplicate-segments\}
如果你需要创建一个与现有市场细分相似的新细分,直接复制即可,无需从头构建。对于同时运行多个营销活动或 A/B 测试、且用户群体存在重叠的团队来说,这能节省不少时间。
复制市场细分会创建一个包含所有筛选条件和描述的副本。新细分的名称会自动添加"(copy)"后缀,便于与原始细分区分。新细分与原始细分相互独立,修改其中一个不会影响另一个。
在 Adapty 看板中复制市场细分的步骤如下:
1. 在 Adapty 主菜单中打开 **Profiles & Segments** 部分,切换到 [**Segments**](https://app.adapty.io/segments) 标签页。
2. 点击市场细分旁边的 **3-dot** 按钮,选择 **Duplicate**。
3. 打开新的市场细分,根据需要调整其筛选条件。
## 删除市场细分 \{#delete-segments\}
当某个市场细分不再需要时,你可以将其永久删除。
如果该市场细分正被以下任一项用作目标受众,Adapty 将阻止删除操作:
- **版位**:至少有一个未删除的版位将该市场细分用作其目标受众。
- **A/B 测试(进行中或已完成)**:至少有一个未删除的 A/B 测试将该市场细分用作其目标受众。
对于市场细分的删除,Adapty 将 **Live** 和 **Completed** 状态的 A/B 测试均视为活跃状态。已完成的测试仍会使用该目标受众向匹配用户展示测试结束后的付费墙或用户引导,且该测试的历史数据图表也限定在该市场细分的范围内。只有在 A/B 测试本身被删除后,市场细分才会被释放。
:::warning
市场细分的删除是永久性的,无法恢复。
:::
在 Adapty 看板中删除市场细分的步骤如下:
1. 前往 Adapty 主菜单中的 **Profiles & Segments**,切换到 [**Segments**](https://app.adapty.io/segments) 标签页。
2. 点击该市场细分旁边的 **3-dot** 按钮,选择 **Delete**。
3. 在确认输入框中输入市场细分名称,然后点击 **Delete forever**。
:::info
如果该市场细分正在被使用,对话框会列出引用它的版位和 A/B 测试。
要解除删除限制,请从列表中打开每个版位或 A/B 测试,然后将该市场细分从其目标受众中移除,或者直接删除对应的版位或 A/B 测试。待没有任何内容引用该市场细分后,即可将其删除。
:::
---
# File: event-feed
---
---
title: "事件流"
description: "通过 Adapty 的事件流监控和分析用户活动。"
---
事件流让你可以直观地追踪 Adapty 生成的[事件](events),并查看其导出到第三方集成(包括 webhook)的状态。
:::warning
事件流不显示以下内容:
- **服务端 API v1 的交易记录**:使用 [服务端 API(第 1 版)](server-side-api-specs-legacy#requests) 创建的交易。请改用 [服务端 API(第 2 版)](api-adapty/operations/setTransaction) 以使其出现在事件流中。
- **没有用户画像的事件**:在 SDK 识别用户之前到达的交易(例如商店服务器通知)。若要将其包含在导出中,请在 [S3](s3-exports) 或 [Google Cloud Storage](google-cloud-storage) 集成中启用 **Include events without profile**。
:::
:::note AppsFlyer、Facebook Ads 和 Branch 的发送状态可能不准确,因为它们并不总是在发生错误时返回错误信息。 ::: 要查看发起该交易的用户画像,请点击事件详情中的 **View Profile** 按钮。 --- # File: ab-tests --- --- title: "A/B 测试" description: "通过 Adapty 的 A/B 测试优化订阅定价,提升转化率。" --- :::tip 你无需自行研究,就能获得可落地的 A/B 测试方案。[AI 增长顾问](autopilot) 会审查你的付费墙、对标竞品,并基于 Adapty 追踪的 20,000 款订阅应用的匿名数据为你生成建议。 ::: 通过在 Adapty 中运行 A/B 测试来提升应用收益。对比不同的用户流程、付费墙和用户引导,找出转化效果最佳的方案——无需修改代码。例如,你可以测试: - 订阅价格 - 付费墙的设计、文案和布局 - 试用期时长与订阅周期 - 用户引导界面设计 ## 前提条件 \{#prerequisites\} 在设置 A/B 测试之前,您需要准备: - **版位**:一个或多个用于展示流程、付费墙或用户引导的[版位](placements)。 - **流程**:至少两个[流程](adapty-flow-builder)。 - **付费墙**:至少两个[付费墙](paywalls)。 - **用户引导**:至少两个[用户引导](onboardings)。 :::warning 如果您没有使用 [Adapty Flow 编辑工具](adapty-flow-builder)或 [Adapty 付费墙编辑工具](adapty-paywall-builder),请通过 `.logShowFlow()`(iOS SDK v4+)/ `.logShowPaywall()` [向 Adapty 上报付费墙展示事件](present-remote-config-paywalls#track-paywall-view-events)。若未调用此方法,Adapty 将无法统计测试中的付费墙展示次数,转化数据也会不准确。 ::: ## A/B 测试类型 \{#ab-test-types\} Adapty 支持两种主要的 A/B 测试类型: - **常规测试**:在单个流程/付费墙/用户引导版位上运行。 - **跨版位测试**:跨多个付费墙版位运行,向同一用户在所有版位展示相同的实验变体。目前仅支持付费墙。 如需了解各类型的完整对比、使用场景及优先级规则,请参阅 [A/B 测试类型](ab-test-types)。 ## 后续步骤 \{#next-steps\} - [AI 增长顾问](autopilot) — 分析你的付费墙,获取市场洞察,并生成 A/B 测试方案 - [A/B 测试类型](ab-test-types) — 了解各种测试类型及其适用场景 - [创建、运行和停止 A/B 测试](run_stop_ab_tests) — 配置并运行你的第一个测试 - [A/B 测试结果与数据图表](results-and-metrics) — 解读 A/B 测试数据并选出胜出实验变体 --- # File: ab-test-types --- --- title: "A/B 测试类型" description: "了解 Adapty 中的 A/B 测试类型。" --- Adapty 提供两种 A/B 测试类型,分别适用于不同的测试场景: - **常规 A/B 测试:** 针对单个[流程](adapty-flow-builder)/[付费墙](paywalls)/[用户引导](onboardings)版位创建的 A/B 测试。 - **跨版位 A/B 测试:** 针对应用中多个付费墙版位创建的 A/B 测试。一旦 A/B 测试分配了
## 主要区别 \{#key-differences\}
| 功能 | 普通 A/B 测试 | 跨版位 A/B 测试 |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **测试对象** | 单个流程/付费墙/用户引导 | 同一实验变体下的一组付费墙 |
| **实验变体一致性** | 每个版位独立决定实验变体 | 所有付费墙版位使用相同的实验变体 |
| **目标受众设置** | 按流程/付费墙/用户引导版位分别定义 | 在所有付费墙版位间共享 |
| **数据分析** | 分析单个流程/付费墙/用户引导版位 | 分析测试所涉及的所有版位的整体应用表现 |
| **实验变体流量分配** | 按流程/付费墙/用户引导分别设置 | 按一组付费墙整体设置 |
| **适用用户** | 所有用户 | 仅限新用户(从未看过 Adapty 付费墙的用户) |
| **Adapty SDK 版本要求** | 流程:v4.0.0+;付费墙:不限版本;用户引导:v3.8.0+(iOS、Android、React Native、Flutter),v3.14.0+(Unity),v3.15.0+(KMP、Capacitor) | 3.5.0+ |
| **最适合** | 在不考虑整体应用经济的情况下,测试单个流程/付费墙/用户引导版位的独立变更 | 在全应用范围内评估整体变现策略 |
## A/B 测试选择逻辑 \{#ab-test-selection-logic\}
**跨版位 A/B 测试的优先级高于普通 A/B 测试。** 但跨版位测试仅面向**新用户**展示——即从未看过任何 Adapty 付费墙的用户(从未为其调用过 `getPaywall` SDK 方法)。这确保了跨版位结果的一致性。
下图展示了 Adapty 为某个版位选择 A/B 测试时所使用的逻辑:
在 **A/B Tests** 页面中,付费墙、用户引导、流程和跨版位测试分别显示在不同的标签页中。
## 跨版位 A/B 测试的限制 \{#crossplacement-ab-test-limitations\}
:::warning
跨版位 A/B 测试不能包含流程或用户引导类版位。
:::
跨版位 A/B 测试保证每位用户在测试涉及的所有版位中看到相同的实验变体。这带来以下限制:
* 只有新用户才能参与。新用户是指从未看过 Adapty 付费墙、且其应用从未调用过 `getPaywall` 的用户。Adapty 无法为其他用户保证一致的付费墙链路。
* 用户遇到的第一个版位决定了 Adapty 展示哪个付费墙。你无法更改用户的分配,也无法将同一用户加入多个跨版位 A/B 测试。
:::warning
用户一旦收到跨版位付费墙,即便你已停止测试,该用户在 90 天内仍会看到同一付费墙。如需调整此时长,请在 **General** 设置中修改 **[Cross-placement variation stickiness](general#9-cross-placement-variation-stickiness)**。
:::
## 跨版位 A/B 测试优先级 \{#crossplacement-ab-test-priority\}
* 跨版位 A/B 测试始终优先于常规 A/B 测试和用户引导 A/B 测试。如果新用户同时符合跨版位测试和同一版位的常规测试条件,系统将展示跨版位测试。
* 当多个面向相同目标受众的跨版位 A/B 测试共享同一版位时,Adapty 会根据测试的添加顺序自动分配优先级,最先添加的测试优先级最高,且无法手动调整。
* 针对较小目标受众群体的测试会自动优先于针对所有用户群体的测试。
:::note
在 Analytics 中,跨版位 A/B 测试会显示为多个子测试,每个版位对应一个。子测试的命名格式为 `
2. 在右上角,点击 **Create A/B test**。
3. 在 **Create the A/B test** 窗口中,输入 **Test name**。此项为必填。请选择一个能清晰描述测试内容的名称,以便在查看结果时快速识别。
4. 填写 **Test goal**,说明您希望达成的目标(例如提升订阅量或降低流失率)。
5. 点击 **Select placement**,选择一个流程、付费墙或用户引导版位。
6. 在 **Variants** 表格中配置测试内容。每一行代表一个实验变体,每一列代表一个版位。在每个交叉处添加一个付费墙。
默认情况下,表格包含 2 个实验变体和 1 个版位。你最多可以添加 20 个实验变体。添加第二个版位后,测试将变为跨版位 A/B 测试。请注意,跨版位 A/B 测试仅适用于付费墙。
7. 保存测试。你有两种选择:
1. **Save as draft**:测试不会立即上线。你可以稍后从版位或 A/B 测试列表中启动它。在正式启动前,可以用这个选项检查配置是否正确。
2. **Run A/B test**:立即启动测试。点击该按钮后,测试将立刻上线。
保存为草稿后,继续参阅[运行 A/B 测试](#run-an-ab-test)。
## 编辑 A/B 测试 \{#edit-an-ab-test\}
您只能编辑已保存为草稿的 A/B 测试。一旦测试生效,就无法更改。要更新正在运行的测试,请使用 **Modify** 选项——这会创建一个同名副本,您可以在其中进行更改。Adapty 会停止原始测试,原始版本和修改版本将分别出现在您的数据分析中。
## 运行 A/B 测试 \{#run-an-ab-test\}
在 Adapty 中运行 A/B 测试,意味着将其分配到某个版位,从而开始向用户展示付费墙和用户引导。
1. 从 Adapty 主菜单进入 [A/B tests](ab-tests) 板块。
2. 确认你正在查看正确的列表——**Paywall**、**Flow**、**Onboardings** 和 **Crossplacement** A/B 测试分别显示在不同标签页中,可以切换查看。
3. 切换到 **Drafts** 标签页。只有草稿状态的测试才能启动。
4. 在您要启动的测试旁边,点击 **Run A/B test**。
5. **编辑 A/B 测试**窗口随即打开。请检查配置,并在此时完成最终调整。如果版位或目标受众尚未填写,请在此添加。
6. 确认配置无误后,点击 **Run A/B test** 开始运行。
测试启动后,你可以在 [A/B 测试结果与数据图表](results-and-metrics) 页面跟踪测试进度并查看性能数据。
## 停止 A/B 测试 \{#stop-an-ab-test\}
停止 A/B 测试后,测试将结束,你可以查看结果。同时,你还需要决定测试结束后在相关版位中向用户展示哪个付费墙。
1. 打开 [A/B tests](https://app.adapty.io/ab-tests) 页面,切换到 **Live** 标签页。
2. 在要停止的测试旁边,点击三点菜单,然后选择 **Stop A/B test**。
3. 在 **Stop the A/B test** 窗口中,决定测试结束后的处理方式。你有三个选项:
| 选项 | 描述 |
|----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 展示某个已测试的付费墙/用户引导 | 根据收入、最优概率(**P2BB**)和每千用户收入等测试结果,选择胜出的付费墙或用户引导。所选版位和目标受众将展示该付费墙或用户引导。 |
| 选择不参与 A/B 测试的付费墙/用户引导 | 选择任意不属于当前 A/B 测试的付费墙或用户引导。当所有测试实验变体均未达到预期目标时,可使用此选项。 |
| 不指定任何付费墙/用户引导 | A/B 测试结束后,所选版位和目标受众不会指定特定的付费墙或用户引导,而是根据目标受众优先级展示下一个可用的付费墙或用户引导。如果您希望由现有配置自动决定展示哪个付费墙或用户引导,而无需手动选择,这是一个不错的选择。 |
:::note
停止 A/B 测试是不可逆操作——测试一旦停止便无法重新启动。请确保在决定停止之前已收集了足够的数据。
:::
4. 点击 **Stop and complete this A/B test** 按钮。
A/B 测试结束后,它将不再处于活跃状态,其中的付费墙或用户引导也不再向新用户展示。
您仍可在 [A/B 测试数据图表页面](results-and-metrics#metrics-controls)查看测试结果与数据图表,了解测试期间参与用户的表现。随着新的购买或收入事件归因到这些用户,数据图表可能会持续更新。
---
# File: ab-test-no-paywall-variants
---
---
title: "添加不含流程或付费墙的 A/B 测试实验变体"
description: "运行一个 A/B 测试,其中一个实验变体跳过流程或付费墙,使用远程配置标志控制是否展示。"
---
你可以通过运行一个包含空实验变体的 A/B 测试来衡量流程或付费墙的影响。一个实验变体展示你的流程/付费墙,另一个什么都不显示。你的应用通过读取远程配置中的标志来决定是否渲染。
## 工作原理 \{#how-it-works\}
该设置在同一版位中使用两个流程/付费墙:
- **流程/付费墙 A**:你想要测试的流程或付费墙,其远程配置中 `show_paywall` 设置为 `true`。
- **流程/付费墙 B**:一个空的流程或付费墙,其远程配置中 `show_paywall` 设置为 `false`。
当 SDK 返回流程或付费墙时,你的应用会读取 `show_paywall` 标志。如果标志为 `true`,应用正常渲染;如果为 `false`,应用跳过渲染,用户无需看到任何内容即可继续。
## 1. 在远程配置中添加 show_paywall 标志 \{#1-add-the-show_paywall-flag-in-remote-config\}
在同一个版位中需要两个流程或付费墙:流程/付费墙 A(需要测试的那个)和流程/付费墙 B(一个空的)。为每个流程/付费墙在其远程配置中添加一个 `show_paywall` 字段,这样你的应用就可以用同一个键名对两个实验变体进行分支处理。
为流程/付费墙 A 添加该标志:
1. 在 Adapty 主菜单中打开 [**Flows**](https://app.adapty.io/flows)/[**Paywalls**](https://app.adapty.io/paywalls) 部分,选择 Flow/Paywall A。
2. 打开 **Remote config** 部分。
3. 创建一个名为 `show_paywall`、值为 `true` 的字段。在 **JSON** 视图中,该条目如下所示:
```json showLineNumbers
{
"show_paywall": true
}
```
4. 保存更改。
对 Flow/Paywall B 重复上述步骤,但将 `show_paywall` 设置为 `false`。
有关远程配置的完整详情,请参阅[使用远程配置自定义流程](customize-flow-with-remote-config)或[使用远程配置设计付费墙](customize-paywall-with-remote-config)。
:::tip
在两个实验变体上都设置 `show_paywall`,可以让两组的代码路径保持一致,也便于后续扩展更多实验变体。
:::
## 2. 设置 A/B 测试 \{#set-up-the-ab-test\}
1. 在版位上[创建 A/B 测试](run_stop_ab_tests),并将两个流程/付费墙作为实验变体添加进去。
2. 设置实验变体的流量权重,以便在看到流程/付费墙的用户与未看到的用户之间分配流量。
## 3. 在应用中检查标志 \{#check-the-flag-in-your-app\}
从 SDK 返回的远程配置中读取 `show_paywall`。如果该标志为 `false`,则跳过渲染,让用户继续操作。
:::note
Adapty 使用 [currencylayer.com](https://currencylayer.com/) 的汇率(每 8 小时更新一次)将其他货币换算为美元。汇率**在交易发生时锁定**——后续汇率变动不会影响已换算的结果。
:::
**收入**:该数据图表显示从购买和续订中产生的总收入(以美元计),已扣除退款金额。它同时包含首次购买和后续订阅续订的金额。通过收入数据,你可以了解每个 A/B 测试实验变体的财务表现,并找出哪个实验变体带来的收益最高。
了解更多关于[付费墙](paywall-metrics)数据图表的信息。
**成为最优项的概率**:Adapty 采用严谨的数学分析框架对 A/B 测试结果进行分析,并提供一项名为"成为最优项的概率"的数据图表。该指标评估某个特定实验变体在所有测试变体中表现最佳(从长期收入角度)的可能性,以 1% 至 100% 的百分比形式呈现。关于 Adapty 如何计算该指标的详细信息,请参阅[文档](maths-behind-it)。表现最佳的选项(以每千用户收入为衡量依据)会以绿色高亮显示,并自动设置为默认选项。
**每千用户收益**:每千用户收益数据图表用于计算每个 A/B 测试实验变体在每 1,000 名用户中产生的平均收益。该指标帮助你了解各实验变体的收益效率,而无需关注用户总量。通过统一的标准化维度,你可以比较不同实验变体的表现,并根据收益产生效率做出明智的决策。
**每千用户收入的趋势预测区间**:每千用户收入数据图表还包含趋势预测区间。趋势预测区间表示根据现有数据和统计分析,某一实验变体的真实每千用户收入预计落入的范围。
在 A/B 测试中,分析不同实验变体产生的收入时,我们会计算每个实验变体每 1,000 名用户的平均收入。由于不同用户的收入可能存在差异,预测区间能够清晰地反映每 1,000 名用户收入的合理取值范围,同时充分考虑了预测过程中的变异性和不确定性。
通过将预测区间纳入每千用户收入指标,Adapty 让你能够在考虑潜在收入结果范围的同时,评估 A/B 测试各实验变体的收入效率。这些信息帮助你做出数据驱动的决策,并有效优化订阅策略——同时充分考虑预测过程中的不确定性以及每千用户收入的合理取值范围。
通过分析 Adapty 提供的这些数据图表,您可以深入了解 A/B 测试各实验变体的财务表现、统计显著性和收益效率,从而做出基于数据的决策,有效优化订阅策略。
## A/B 测试数据图表 \{#ab-test-metrics\}
Adapty 提供了一套全面的数据图表,帮助你有效衡量付费墙或用户引导实验变体的 A/B 测试效果。这些数据图表会实时持续更新,但展示次数除外——展示次数为定期更新。深入了解这些数据图表,有助于你评估不同实验变体的效果,并基于数据做出决策,优化付费墙或用户引导策略。
在 A/B 测试列表页面,你可以查看所有 A/B 测试的数据图表,快速了解各测试的整体表现。该视图汇总了每个实验变体的关键指标,方便你对比各变体的表现并发现显著差异。如需更深入地分析某个 A/B 测试,可进入该测试的详情页查看详细数据图表。详情页针对所选 A/B 测试提供专项深度指标,帮助你深入了解各实验变体的具体表现。
除浏览量外,所有数据图表均归因于付费墙或用户引导中的产品。
## 数据图表控件 \{#metrics-controls\}
系统根据所选时间段显示数据图表,并按左侧列参数以三级缩进方式进行组织。
### 用户画像安装日期筛选 \{#profile-install-date-filtration\}
**按安装日期筛选数据图表**复选框可根据用户画像的安装日期来筛选数据图表,而非默认的以试用/购买日期(针对交易)或查看日期(针对付费墙或用户引导浏览量)进行筛选。勾选此复选框后,您可以将数据图表与用户画像安装日期对齐,从而专注于衡量特定时期的用户获取效果。此选项适用于根据具体需求自定义数据图表分析。
### 时间范围 \{#time-ranges\}
您可以从多种时间段中选择来分析数据图表数据,支持按天、周、月或自定义日期范围等特定时长进行聚焦分析。
### 可用的筛选与分组选项 \{#available-filters-and-grouping\}
:::link
主要文章:[数据分析控件](controls-filters-grouping-compare-proceeds)
:::
Adapty 提供了强大的筛选和自定义数据图表分析工具,满足您的各种需求。在 Adapty 的数据图表页面,您可以使用多种时间范围、分组选项和筛选条件。
- ✅ 筛选依据:目标受众、归因、国家、付费墙、付费墙状态、付费墙分组、用户引导、版位、国家、商店、产品及产品商店。
- ✅ 分组依据:产品和商店。
:::note
按 A/B 测试筛选时,跨版位 A/B 测试会以独立的子测试形式显示(例如 `My test child-0`、`My test child-1`),每个版位对应一个子测试。详情请参阅[跨版位 A/B 测试的限制](ab-test-types#crossplacement-ab-test-limitations)。
:::
## 单项数据图表 \{#single-metrics-chart\}
付费墙或用户引导数据图表页面的核心组件之一是图表区域,它以可视化方式呈现所选数据图表,便于快速分析。
在 A/B 测试数据图表页面的图表区域,有一个水平条形图,直观地展示所选数据图表的各项数值。图表中每条柱子对应一个数据图表值,其长度与数值大小成比例,让你一眼就能看懂数据。横轴表示所分析的时间范围,纵轴显示各数据图表的具体数值。所有数据图表值的汇总结果显示在图表旁边。
此外,点击数据图表区域右上角的箭头图标可以展开视图,在完整的折线图中显示所选数据图表。
## A/B 测试摘要 \{#ab-test-summary\}
在单项数据图表旁边,会显示 A/B 测试详情摘要区域,其中包含有关 A/B 测试的状态、持续时间、版位及其他相关详情信息。
## 数据图表定义 \{#metrics-definitions\}
以下是 A/B 测试可用的关键数据图表:
### 收入 \{#revenue\}
收入是指 A/B 测试产生的所有购买和续订所带来的美元总金额,包括首次购买和后续订阅续订。该数据图表在扣除 App Store 或 Play Store 佣金之前计算。
了解更多关于[付费墙](paywall-metrics#revenue)收入数据图表的信息。
### CR to purchases \{#cr-to-purchases\}
购买转化率衡量您的 A/B 测试将浏览转化为实际购买的效果。计算方式为购买次数除以浏览次数。例如,若有 10 次购买和 100 次浏览,购买转化率为 10%。
### CR trials \{#cr-trials\}
试用转化率(CR)是从 A/B 测试中启动的试用次数除以浏览次数。试用转化率衡量您的 A/B 测试将浏览转化为试用激活的效果。计算方式为启动的试用次数除以浏览次数。
### Purchases \{#purchases\}
Purchases 数据图表表示 A/B 测试在付费墙或用户引导中产生的交易总数。包含以下类型的购买:
- 新增购买。
- 已激活试用的试用转化。
- 订阅的降级、升级和跨级。
- 订阅恢复(例如,订阅在未自动续订的情况下过期后被恢复)。
请注意,续订不包含在 Purchases 数据图表中。
### Trials \{#trials\}
Trials 数据图表表示 A/B 测试中激活的试用总数。
### Trials cancelled \{#trials-cancelled\}
Trials cancelled 数据图表表示已关闭自动续订的试用数量。当用户手动取消试用订阅时即发生此情况。
### Refunds \{#refunds\}
A/B 测试的 Refunds 表示与测试变体相关的退款购买和订阅数量。
### Views \{#views\}
Views 是 A/B 测试所包含的付费墙或用户引导的浏览次数。如果用户访问两次,则计为两次浏览。
### Unique views \{#unique-views\}
Unique views 是付费墙或用户引导的唯一浏览次数。如果用户访问两次,则计为一次唯一浏览。
### Probability to be the best \{#probability-to-be-the-best\}
Probability to be the best 数据图表量化了 A/B 测试中某一特定实验变体在所有测试付费墙或用户引导中成为表现最佳选项的可能性。它提供一个数值概率,表示每个付费墙或用户引导的相对表现。该数据图表以百分比表示,范围为 1% 至 100%。
### ARPU(每用户平均收入)\{#arpu-average-revenue-per-user\}
仅适用于用户引导 A/B 测试。衡量特定时期内每位用户产生的平均收入。计算方式为总收入除以唯一用户数。
### ARPPU(每付费用户平均收入)\{#arppu-average-revenue-per-paying-user\}
ARPPU 是 A/B 测试产生的每付费用户平均收入。计算方式为总收入除以唯一付费用户数。例如,若您从 1,000 名付费用户中获得了 $15,000 的收入,则 ARPPU 为 $15。
### ARPAS(每活跃订阅者平均收入)\{#arpas-average-revenue-per-active-subscriber\}
ARPAS 是一项数据图表,用于衡量运行 A/B 测试期间每位活跃订阅者产生的平均收入。计算方式为总收入除以已激活试用或订阅的订阅者数量。例如,若总收入为 $5,000,订阅者数量为 1,000,则 ARPAS 为 $5。此数据图表有助于评估每位订阅者的平均变现潜力。
### Proceeds \{#proceeds\}
A/B 测试的 Proceeds 数据图表表示应用所有者从购买和续订中实际收到的 USD 金额,已扣除适用的 App Store / Play Store 佣金。它反映与 A/B 测试中测试变体相关的净收入,直接贡献于应用的收益。有关 Proceeds 计算方式的更多信息,请参阅 Adapty [文档。](analytics-cohorts#revenue-vs-proceeds)
### Unique subscribers \{#unique-subscribers\}
Unique subscribers 数据图表表示通过 A/B 测试变体订阅或激活试用的不同用户数量。无论每位订阅者发起多少次订阅或试用,均只计算一次。
### Unique paid subscribers \{#unique-paid-subscribers\}
Unique paid subscribers 数据图表表示通过 A/B 测试变体成功完成购买并成为付费订阅者的唯一用户数量。
### Refund rate \{#refund-rate\}
A/B 测试的退款率计算方式为:与测试变体相关的退款次数除以首次购买次数(不含续订)。例如,若有 5 次退款和 1,000 次首次购买,退款率为 0.5%。
### Unique CR purchases \{#unique-cr-purchases\}
A/B 测试的唯一购买转化率计算方式为:与测试变体相关的购买次数除以唯一浏览次数。例如,若有 10 次购买和 100 次唯一浏览,唯一购买转化率为 10%。
### Unique CR trials \{#unique-cr-trials\}
A/B 测试的唯一试用转化率计算方式为:与测试变体相关的启动试用次数除以唯一浏览次数。例如,若有 30 次启动试用和 100 次唯一浏览,唯一试用转化率为 30%。
### Completions & unique completions \{#completions--unique-completions\}
仅适用于用户引导 A/B 测试。Completions 统计用户通过 A/B 测试变体完成用户引导的次数,即从第一屏到最后一屏的完整流程。如果某人完成两次,则计为两次 **completions**,但只有一次 **unique completion**。
### Unique completions rate \{#unique-completions-rate\}
仅适用于用户引导 A/B 测试。唯一完成次数除以唯一浏览次数。此数据图表帮助您了解用户通过 A/B 测试变体与用户引导的互动情况,当您发现用户忽略用户引导时可据此进行优化。
---
# File: maths-behind-it
---
---
title: "A/B 测试背后的数学原理"
description: "了解订阅分析背后的数学原理,以获得更好的收入洞察。"
---
A/B 测试是一种强大的技术,用于比较两个不同版本的流程、付费墙或用户引导的表现。其核心目标是根据 12 个月内的平均每用户收入,判断哪个版本更有效。然而,等待整整一年来收集数据并做出决策并不现实。因此,系统采用 2 周每用户收入作为代理指标——该指标基于历史数据分析选定,可近似反映目标指标。为了获得准确可靠的结果,必须采用能够处理多种数据类型的稳健统计方法。贝叶斯统计是现代数据分析中的主流方法之一,为 A/B 测试提供了灵活直观的分析框架。通过引入先验知识并用新数据持续更新,贝叶斯方法能够在不确定性条件下做出更优决策。本文档详细介绍了 Adapty 在评估 A/B 测试结果时所采用的数学分析方法,为数据驱动决策提供有价值的参考依据。
## Adapty 的统计分析方法 \{#adaptys-approach-to-statistical-analysis\}
Adapty 采用全面的统计分析方法来评估 A/B 测试的表现,并提供准确可靠的洞察。我们的方法论由以下关键步骤组成:
1. **指标定义:** 要成功开展 A/B 测试,您需要识别并定义与分析的具体目标相符的关键指标。Adapty 利用大量订阅应用的历史数据,确定哪个指标最适合作为"1 年后平均收入"这一长期目标的代理指标——结果是 14 天后的 ARPU。
2. **假设制定:** 我们为 A/B 测试创建两个假设。零假设(H0)假设对照组(A)和测试组(B)之间没有显著差异。备择假设(H1)则表明两个或多个组之间存在显著差异。
3. **分布选择:** 我们根据数据特征和观测指标选择最佳的分布族。最常见的选择是对数正态分布(考虑零值的情况)。
4. **最优概率计算:** 利用贝叶斯 A/B 测试方法,我们计算参与测试的每个付费墙或用户引导变体成为最佳选项的概率。该值与我们之前使用的 p 值相关,但本质上是一种不同的方法,更加稳健且易于理解。
5. **结果解读:** "成为最优的概率"正如其字面意思。概率越大,某一选项成为该任务最佳选择的可能性越高。您需要自行确定决策阈值,这应取决于您具体情况的许多其他因素,但通常使用 95% 作为概率标准。
6. **预测区间:** Adapty 计算每个组的表现指标的预测区间,提供真实总体参数可能落入的值域范围。这有助于量化与估计表现指标相关的不确定性。
## 样本量确定 \{#sample-size-determination\}
确定合适的样本量对于获得可靠且具有结论性的 A/B 测试结果至关重要。Adapty 考虑统计功效和预期效应量等因素(在贝叶斯方法下这些因素仍然重要),以确保样本量充足。针对我们现在采用的贝叶斯方法,有专门的方法用于估算所需样本量,确保分析的可靠性。
如需进一步了解 A/B 测试的功能,我们建议参阅我们关于[创建](ab-tests)和[运行 A/B 测试](run_stop_ab_tests)的文档,以及了解各种 [A/B 测试数据图表与结果](results-and-metrics)。
Adapty 的 A/B 测试分析框架现已采用贝叶斯方法,但核心仍聚焦于数据指标的定义、假设的构建以及分布的选择。与此前计算 p 值不同,我们现在计算后验分布,并求出每个实验变体成为最优版本的概率,同时给出预测区间。这一改进后的方法不仅更加全面,也更为稳健,能够提供更直观、更易于解读的洞察。我们的目标始终如一:通过对 A/B 测试进行严谨的统计分析,帮助企业优化策略、提升效果、实现增长。
---
# File: autopilot-how-it-works
---
---
title: "AI 增长顾问:工作原理"
description: "了解 AI 增长顾问背后的逻辑,让我们帮助您提升收入。"
---
[AI Growth Advisor](autopilot)(AI 增长顾问)能根据你的实际性能数据以及同类应用在市场上的表现,帮助你确定该运行哪些实验。它不是靠猜测,而是给出更有可能提升效果的具体测试建议。
本文将透明地介绍 AI Growth Advisor 的工作方式——它使用哪些数据、如何评估机会、以及为什么会出现某些建议。目的是帮助你在日常增长工作流中放心地使用它。
## AI Growth Advisor 的实际功能 \{#what-ai-growth-advisor-actually-does\}
AI Growth Advisor 会分析你的应用和付费墙数据,找出最有可能提升收入的实验方向。它的分析维度包括:
- **当前配置**:定价、试用、产品以及转化效果
- **市场规律**:同类应用的定价结构和收费方式
- **测试历史**:你已经运行过的实验及其结论
- **增长潜力**:哪些调整最有可能带来显著变化
Growth Advisor 利用 AI 综合评估上述因素,并将其转化为可立即启动的 A/B 测试。你将获得一套现成的方案,无需调研竞品,也无需猜测下一步该测试什么。
## AI 增长顾问背后的数据 \{#the-data-behind-ai-growth-advisor\}
每条建议都基于三个协同工作的主要数据来源。
#### 你的应用自身数据 \{#your-apps-own-data\}
AI 增长顾问会分析你的应用当前的表现:
- 各付费墙的转化数据指标
- 定价与产品结构
在提出任何优化建议之前,AI 增长顾问会先以此作为基准参考。
:::note
我们不会使用你的应用性能数据来为其他应用训练推荐模型。你的数据完全私密。
:::
#### 付费墙分析 \{#paywall-analysis\}
AI Growth Advisor 会分析您的付费墙截图,并将其设计与同类别顶级应用所采用的成熟模式进行对比。它会评估布局设计、文案、订阅套餐展示,以及转化导向元素(如优惠标签或用户评价区块)。
分析完成后,会生成两类建议:
- **基准对比建议**,基于头部应用的差异化做法,每条建议均附有具体数据支撑(例如"72% 的头部教育类应用采用了这一做法")。
- **视觉分析建议**,由 AI 根据你的截图自动生成,涵盖文案优化、布局调整及其他设计改进。
这些建议会直接进入你的[增长计划](autopilot-growth-plan#view-the-growth-plan),作为可[发起 A/B 测试](autopilot-execute-plan)的假设。
#### 竞品数据 \{#competitor-data\}
AI Growth Advisor 会利用定价、订阅结构以及同类应用的常见模式等公开信息,将您的配置与同市场中的同类应用进行对比。由于竞争对手的定价和结构因市场而异,这些比较是按国家/地区进行的。竞争对手的定价数据来自第三方和公开来源(如 App Store),与数据图表分析所使用的匿名 Adapty 网络数据不同。
这样,你测试的是那些在同类应用中已经验证有效的策略,而不是随机的想法。看到分析结果后,你可以将自己的基准数据和竞品定价并排对比。如果类似的应用在不同定价或结构下表现更好,这就是一个有力信号,说明相同的策略也可能适合你。
:::tip
AI Growth Advisor 会根据你实际具有竞争力的范围自动筛选相关竞争对手。我们通常建议保留这些推荐,而不是添加差距过大的应用。如果你的应用跨越多个品类,可以调整列表,聚焦于最相关的细分市场。
:::
#### 行业基准 \{#industry-benchmarks\}
AI Growth Advisor 基于 Adapty 追踪的 20,000 款订阅应用的匿名数据,帮助您了解自己在特定国家/地区内与同类应用平均水平的差距。这些数据经过全网汇总,不与任何具体应用挂钩。
例如,您的转化漏斗和每次安装收入会与同类别、同国家/地区的应用平均值进行对比,让您清晰了解自己是低于平均水平、处于平均水平,还是已经领先于市场。
#### 地区市场数据 \{#geographic-market-data\}
AI 增长顾问会分析各个地理市场——借助 Adapty 网络中 20,000 款应用的数据规律——找出哪些地区通过调整定价可以释放更多收益。针对每个国家/地区,它会评估以下维度:
- **转化率**:安装到付费的转化率与全球平均水平的对比。转化率较高,可能意味着存在提价空间;转化率较低,则可能表明用户对价格较为敏感。
- **价格指数**:该国家/地区在 [Adapty 定价指数](https://uploads.adapty.io/adapty_pricing_index.pdf) 中的位置,反映当地居民的消费能力。
您可以根据增长计划中的[地区定价建议](autopilot-growth-plan#geo-pricing-hypotheses)创建 A/B 测试,从而将这些建议付诸实践。
## AI 增长顾问如何生成建议 \{#how-ai-growth-advisor-decides-what-to-recommend\}
AI 增长顾问会生成一批建议,帮助你提升付费墙转化率。这些建议应逐一测试,以便准确衡量每项变更的效果。
以下是 AI 增长顾问生成建议的方式:
1. **找出最大的提升空间**
AI Growth Advisor 会审查您的定价、产品和漏斗表现,然后与行业规律及同类应用进行对比。分析以您主要市场的货币为基准——而不仅仅是美元——因此价格建议与您的订阅用户实际支付的金额相符。它会找出最具改进空间的环节,无论是调整价格、添加试用期,还是改变优惠结构。
2. **选择下一个实验**
每个假设都基于您现有的测试历史生成。AI 增长顾问了解您已运行过哪些实验、哪些胜出、哪些方向仍值得探索。下一条建议会在上一条实验结果的基础上生成,而非遵循固定的顺序。
3. **进行胜者与挑战者对比测试**
每次实验结束后,胜出方将成为新的基准。该结果将影响增长计划中的下一条建议——AI 增长顾问会保留有效的内容,排除无效的内容,并在此基础上选择下一个测试。
4. **保持实用性**
AI Growth Advisor 只会建议你使用现有产品和设置即可发起的测试,或仅需少量改动(如新建产品或调整价格)的测试。其目标是让测试保持高效、易于管理。
5. **向你展示推理依据**
针对每条推荐,AI Growth Advisor 都会提供清晰的假设说明,解释为什么这个测试值得运行。你可以了解到当前数据指标与竞品及行业平均水平的对比、潜在机会所在,以及我们预期哪些核心指标会得到提升。
这让实验成为一个可重复的流程——每次测试都能带来新的洞察,推动你打造出更高效的付费墙。
## 每次实验结束后会发生什么 \{#what-happens-after-each-experiment\}
建议不会用完。每一次完成的测试都会成为后续实验的基础。只要你持续测试,AI Growth Advisor 就会不断给出下一步的建议。
若要刷新底层市场数据,可在同一版位上重新运行分析。每次重新运行都会拉取最新的竞品定价、转化率基准和品类趋势,并将新发现的假设添加到增长计划中,而不会影响已有内容。AI 生成的假设、自定义假设以及进行中的 A/B 测试均会在重新运行后保留。
一旦你完成了基准优化,也可以与更强的竞争对手展开角逐。这种迭代方式能帮助你随着应用的成长和市场的演变,持续实现收益最大化。
:::tip
准备好了吗?启动 [AI Growth Advisor](autopilot-analysis) 来分析你的付费墙并生成包含 A/B 测试的增长计划。使用[内置向导](autopilot-execute-plan)无缝启动复杂测试:它将引导你完成产品创建、付费墙复制和市场细分设置。
:::
---
# File: autopilot-analysis
---
---
title: "付费墙与市场分析"
description: "为你的应用生成基于数据的定制化增长计划。"
---
按照本文步骤运行 AI 增长顾问分析,生成增长计划。
如果你已经为目标版位生成过增长计划,本次分析将生成新的假设供你选择。
:::tip
在开始之前,请确保您已满足[分析所需的前提条件](autopilot#prerequisites)。
:::
## 付费墙分析 \{#paywall-analysis\}
### 选择要分析的付费墙 \{#select-a-paywall-for-analysis\}
1. 打开 **AI Growth Advisor** 页面,点击 [Get Growth plan](https://app.adapty.io/ab-tests/analysis/start) 按钮。
2. 在 **Paywall Diagnostic** 页面,从下拉菜单中选择 **Placement** 和 **Paywall**。Adapty 会自动预选收入最高的版位及其表现最佳的付费墙。如需分析其他付费墙,请先切换版位。
3. 上传截图。AI Growth Advisor 需要一张截图来分析你的付费墙设计和内容。
4. 查看付费墙的有效产品。右侧的产品卡片会显示每个产品的订阅时长、价格和试用期。
5. 点击 **Confirm & Analyze** 继续。Adapty 将分析你的付费墙并生成诊断报告。
### 付费墙分析报告 \{#paywall-analysis-report\}
选择付费墙并上传截图后,Adapty 会分析你的付费墙设计模式,指出其中的亮点和可改进之处。
#### 哪些地方做得好 \{#whats-working-well\}
本节会列出你已采用的、有助于提升转化率的成熟设计模式。例如:醒目的折扣标签、突出的用户评价区块,或清晰的订阅方案说明。
#### 付费墙需要改进的地方 \{#what-to-fix-on-your-paywall\}
Adapty 将改进建议分为两类:
- **基准建议**:基于同类别顶尖应用数据驱动生成的建议。每条建议均包含一项基准统计数据(例如:"72% 的顶尖教育类应用采用此方案")以及具体的优化说明。
- **视觉分析建议**:基于付费墙截图由 AI 生成的建议,涵盖文案优化、布局调整等内容。
:::tip
您的[增长计划](autopilot-growth-plan#view-the-growth-plan)将包含基于基准建议的假设。您可以手动将视觉分析建议添加到计划中。
:::
点击 **Get Market Insights** 继续。
## 市场与竞品分析 \{#market-and-competitor-analysis\}
:::note
市场与竞品分析需要先完成[付费墙分析](#paywall-analysis)。
:::
市场洞察分析会将您应用的定价和转化数据与竞品及行业平均水平进行比较,且比较结果按国家区分。为提供基准参考,Adapty 会汇总并分析 App Store 中同一子类目和国家下其他应用的数据,这些数据在其他地方并不公开。
### 选择竞品 \{#select-competitors\}
最多可选择 5 款竞品进行对比。
Adapty 会自动推荐 5 款,并额外建议 5 款供参考。你也可以通过 App Store 链接手动添加应用。为获得更好的分析结果,建议选择 MRR 高于自身的应用。
点击 **Generate report** 确认列表,等待分析完成。
### 选择国家/地区 \{#select-a-country\}
使用**国家/地区**下拉菜单选择一个主要市场,查看详细分析数据。
### 收入分布 \{#revenue-distribution\}
收入分布数据图表展示了您的收入来自哪些国家/地区,并提供百分比细分。图表会突出显示您的前 5 个国家/地区,这也是后续分析的重点。
### 竞品价格对比 \{#competitor-pricing\}
竞品价格对比表展示了付费墙中的订阅价格与竞品在[所选国家](#select-a-country)的定价差异,并按订阅时长分列显示。
### 转化漏斗 \{#conversion-funnel\}
该数据图表展示了你的转化率——浏览到试用、试用到付费、浏览到付费——以及同类应用的平均水平,供你对比参考。
### 按时长划分的收入分布 \{#revenue-distribution-by-duration\}
此数据图表展示了不同订阅时长对收入的贡献比例,并与行业平均水平进行对比。如果您的收入高度集中于某一时长,可能意味着有优化定价策略的空间。
### 激活 ARPU \{#activation-arpu\}
**激活 ARPU:您的应用与类别对比** 数据图表将您应用的每次新安装平均收入与类别平均值进行比较。
可结合[转化漏斗](#conversion-funnel)一起使用:
- 转化漏斗显示有多少用户付费。
- 激活 ARPU 显示每位用户的平均收入。
转化率高但激活 ARPU 低,可能意味着定价偏低。
该数据图表基于**同期群**计算。Adapty 取过去 90 天内安装应用的用户,将其产生的收入除以用户数量得出结果。
#### 与其他数据图表的比较 \{#comparison-to-other-metrics\}
激活 ARPU 与看板其他地方显示的 ARPU 值不会相同——每个数据图表衡量的内容不同。
- **[ARPU 数据图表](arpu)**:包含旧同期群的续订收入,因此数值通常是激活 ARPU 的数倍。
- **[收入数据图表](revenue),Period 筛选器设为"Activation"**:仅统计每位用户的首次付款,不计算该同期群在 90 天窗口内产生的续订收入。
- **[同期群收入](analytics-cohorts)(90 天)**:最接近的参考指标——建议以此作为对照。
## 后续步骤 \{#next-steps\}
阅读[管理并执行增长计划](autopilot-growth-plan)一文,了解如何根据分析结果运行 A/B 测试。
你随时可以在 Growth Plan 页面查看分析结果。点击 **Analysis Results** 标签页即可。
---
# File: autopilot-growth-plan
---
---
title: "管理增长计划"
description: "添加自定义假设、将其归档并更新增长计划。"
---
完成[分析](autopilot-analysis)后,Adapty 会呈现你的增长计划——一份**可执行的改进假设**列表。每个条目都会建议新的价格方案或设计优化方向。
打开假设,[通过 A/B 测试来验证](autopilot-execute-plan)。
每个版位都有各自的增长计划。随着市场环境变化,你可以重新运行分析来刷新建议。历史运行记录会保存在版本历史中。
## 假设 \{#hypotheses\}
切换增长计划顶部的标签页,按类型筛选假设:
- **Top priority(高优先级)** 包含最值得关注的高影响力假设。若无符合条件的假设,此标签页将自动隐藏。
- **All(全部)** 显示当前方案中的所有假设。
- **Pricing(定价)** 假设探索新的价格点或试用配置,每项均基于付费墙诊断或市场洞察报告中的具体建议。
- **Visual(视觉)** 假设是设计改进建议,可能涉及文案、布局或其他视觉元素的调整。
- [**Geo-pricing(地域定价)**](#geo-pricing-hypotheses) 假设测试针对特定国家的价格调整。
- [**Archived(已归档)**](#archive-a-hypothesis) 假设是您从当前方案中移除的建议,随时可恢复。
你可以[添加自己的假设](#add-your-own-hypothesis),也可以[归档](#archive-a-hypothesis)不想测试的假设。
每次测试一个假设,顺序不限。地理定价测试是个例外——由于其目标受众互不重叠,可以并行运行。
### 地理定价假设 \{#geo-pricing-hypotheses\}
:::important
一次性购买不适用于区域价格优化。
:::
打开 **Geo-pricing** 标签页,查看地理定价建议列表。每条建议针对一个国家进行单次价格调整,并作为独立的 A/B 测试运行。
Adapty 会检测需要价格调整的国家,并提供经 [Adapty Pricing Index](https://uploads.adapty.io/adapty_pricing_index.pdf) 验证的数据驱动建议。
## 漏斗图逐步解析 \{#funnel-chart-step-by-step\}
下面逐一介绍漏斗的各个组成部分,帮助你读懂图表中的用户旅程。
### 安装量 \{#installs\}
第 1 列(1)显示的是安装量。它以绝对值(2)的形式呈现总安装次数(非独立用户数),同时以 100% 作为基准,用于计算后续各步骤的转化率。如果用户删除应用后重新安装,则计为两次独立安装。
旁边的灰色区域表示各步骤之间的过渡参数。转化至下一步骤(展示付费墙)的转化率显示在标签(3)上。流失百分比及流失绝对值显示在下方(4)。
### 付费墙展示 \{#paywall-displayed\}
第 2 列(5)显示了至少看过一次付费墙的用户数量(6)。这些用户仅来自所选时间段内完成安装的用户。如果某用户在所选时间段内查看了付费墙,但其安装日期不在该范围内,则该次查看不计入统计。
此外,该列还显示了此类查看占第 1 步的百分比(7)。你会注意到,这个百分比与第 1 步的灰色标记(3)数值相同。这种相等关系仅在这两个第一步之间成立。
我们通过所有调用了 `logShowFlow()`(iOS SDK v4+)/ `logShowPaywall()` 方法的付费墙来收集此步骤的数据。因此,请务必按照[文档](present-remote-config-paywalls#track-paywall-view-events)所述,使用该方法将每次付费墙展示事件上报给 Adapty。
第 2 列旁边的灰色区域表示转化过渡。旗标(8)上显示的是流向下一步骤(试用)的转化率。下方(9)显示的是流失率及付费墙之后的绝对流失用户数。
### 试用期
第3列(10)显示在所选时间段内安装应用的用户(11)在付费墙上激活的试用期数量。如果筛选条件设置为非试用产品,该值将为零,且该列为空。
另外,您还可以看到从第 1 步开始的试用转化率(12),即从安装到试用的转化比例。
您可能会发现,这个百分比与上一步转化率的灰色标签(8)并不相同。这是因为当前数值分别与数据图表顶部的第 1 步以及灰色标签上的上一步进行比较。
因此,第 3 列旁边的灰色区域显示的是进入下一步(付费)的转化率百分比,该数值展示在标签(13)上。试用期间的流失率百分比和流失用户绝对数量显示在下方(14)。
### 订阅与续订 \{#subscriptions-and-renewals\}
第 4 列显示已激活的订阅数量(15)。对于没有试用期的产品,这个数字包含从付费墙直接发起的订阅量。对于有试用期的产品,这个数字代表从试用转化为付费订阅的数量。如果你同时拥有有试用期和没有试用期两种类型的产品,则显示两者之和。
顶部的百分比显示从安装数(16)的转化率。
灰色旗帜上的百分比显示到下一步的转化率(续订至第 2 个周期)(17)。
续订至第 2 个周期前的流失百分比和绝对值显示在转化率下方(18)。
这一步骤开启了一系列结构相似的步骤序列。第 2 次续订之后是第 3 次、第 4 次,以此类推。如果您的应用历史数据足够丰富,通过水平滚动可能会看到数十个周期。这些步骤的逻辑保持一致:
- 顶部显示相对于安装量的百分比,
- 底部显示相对于上一步骤的百分比,
- 顶部显示续订的绝对数量,
- 底部显示流失的绝对数量,
- 悬停时弹出流失原因。
### 流失原因 \{#churn-reasons\}
Adapty 会详细列出试用阶段及后续阶段的*流失*统计数据。每个进入某一阶段但未进入下一阶段的用户,都会被计为一次流失。
* 如果某个具体事件(例如试用到期或账单问题)导致用户未能转化,Adapty 会显示相应原因。
* **unknown**(未知)状态是一种临时状态,表示该用户尚未遇到允许其进入下一阶段的事件。
在试用阶段,这通常意味着试用期尚未结束。当查看短日期范围或单日的漏斗时,这种情况较为常见,因为试用需要一定时间才能完成转化。
一旦用户完成转化或取消试用,Adapty 将更新相关信息。
### 表格视图、筛选器与 CSV 导出 \{#table-view-filters-and-csv-export\}
漏斗数据图表配有详细数据表格,方便你直接处理具体数字。
此表格沿用了漏斗的分析方式,但做了一些调整。
表格中包含除"首次付费订阅"步骤以外所有步骤的数据列。
取而代之的是两列独立数据:安装 -> 付费 和 试用 -> 付费。这两列展示了免费用户转化为付费用户这一核心转化节点。
产品类型的划分看似是这样的:Install -> Paid 列只显示无试用期的产品,而 Trial -> Paid 列只显示有试用期的产品。但实际情况并非如此。因为我们还会将那些试用期已过期、之后又购买了含试用期产品的用户,视为购买了不含试用期的产品来计算。
深入挖掘数据时,你会发现筛选工具非常强大,有助于提出新的假设。
可以从不同维度自由设置条件,基于数据获取真实洞察。
可变维度:
1. 产品类型——定价策略、时长等。
2. 时间范围。
3. 国家维度细分。
4. 流量归因。
5. 应用商店。
选择绝对值(Absolute #)、相对值(Relative %),或两者同时显示,以便只查看所需数据。
最后,控制面板右侧有一个按钮,可将漏斗数据导出为 CSV 文件,之后可在 Excel 或 Google Sheets 中打开,也可导入到您自己的分析系统。
:::important
如果您的应用参与了佣金减免计划,请务必通知 Adapty。为确保计算准确,请在您的[应用设置](general)中填写 [Small Business Program](app-store-small-business-program) 和 [Reduced Service Fee program](google-reduced-service-fee) 的状态。
:::
---
# File: analytics-retention
---
---
title: "留存分析"
description: "了解用户留存分析,优化您的订阅策略。"
---
留存数据图表可以帮助您解答以下问题:
1. 您的应用如何留住每个周期的客户?
2. 哪些产品更具吸引力、留存率更高?
3. 哪些用户群体更忠诚?
4. 哪个留存水平可以作为增长的基准?
5. 当然,如何通过将资金投入已获取的用户群来节省成本,而非一味拓展新用户?
通过设置筛选条件和分组,您可以深入了解用户行为。
留存数据来源于我们通过 SDK 和应用商店通知收集的信息,无需您进行任何额外配置。
### 我们如何计算留存率?\{#how-do-we-calculate-retention\}
通过观察留存数据图表,你可以了解用户数量与其所处步骤之间的关系:试用(如果勾选了"显示试用"复选框)、第 1 次付款、第 2 次付款,以此类推。下面说明在为留存数据图表选择日期范围时,系统会统计哪些用户。
例如,你在日历中选择了最近 3 个月,且未勾选"显示试用"复选框。这意味着我们只统计在最近 3 个月内完成第 1 次订阅的用户。如果勾选了"显示试用"复选框,且日历中选择了最近 3 个月,则统计所有在这 3 个月内开始试用的用户。对于这些订阅者,第 N 步的绝对留存值为完成第 N 次付款的用户数;第 N 步的相对留存值则为第 N 次付款的绝对数量与所选时间范围内订阅(或试用)总数的比值。
:::info
留存率会随时间回溯变化
无论何时查看数据图表,所选时间段的基准数值(100%)始终保持不变。但下一周期的留存率可能会随时间增长。
例如,对于月度订阅,若在 12 月 1 日至 12 月 31 日之间有 20 笔首次购买,那么在整个 1 月份(乃至之后),随着用户陆续进入下一个订阅周期,第二周期的留存率预计会持续增长(例如由于宽限期等原因)。
:::
### 退款处理 \{#refund-handling\}
退款**不会**从留存率中剔除。已退款的用户仍计入留存曲线,这可能导致同一同期群的留存率看起来高于[活跃订阅](active-subscriptions)或[收入](revenue)。
如需了解各数据图表对退款的完整处理方式对比,请参阅[各指标如何处理退款](refund-events#how-metrics-handle-refunds)。
### 留存机会 \{#retention-opportunities\}
让我们来看看如何充分利用 Adapty 的留存功能。
除了对数据本身的热情,更重要的是在落地分析结果后看到真实的业务价值。所以不妨先想清楚目的是什么。深入了解数据图表功能之前,有必要搞清楚这些数据能带来什么影响。
让我们一起从"为什么"和"怎么做"两个维度来看。
1 - 与目标受众互动。
首先,留存率关乎目标受众、他们的偏好,以及你的产品在整个使用周期内是否符合他们的预期。如果你想衡量业务中最核心的那条"生钱"关系,留存率正是你需要的工具。
这种衡量方式很有价值,因为向现有用户销售通常比开发陌生用户成本更低。成本低有两个原因:销售所需的努力更少,客单价也更高。因此,当留存率下滑时,投资于订阅用户的忠诚度往往是明智之举。
2 - 与产品协作。
第二个"为什么"在于:留存数据图表能够反映产品的实际消费生命周期,并支持长期趋势预测。如果你希望有所改进,就调整负责交付产品的工作,改变其生命周期,然后重新预测,以便更接近业务目标。这类更新可以融入战略愿景,与预测流程协同推进。是的,这个过程永无止境——因为在一个不断变化的环境中,我们都在拼命奔跑,只为保持原地。
3 - 把握市场。
比主要竞争对手跑得更快固然不错,但有时跳出常规竞争反而能带来更大的收益。当你分析不同国家和应用商店中用户的行为时,一些本地特性往往能带来绝佳的洞察,为业务开辟新的机会。文化与市场背景可以从留存率的角度加以分析,进而用于市场细分和后续发展。例如,你可能会在某些地区发现蓝海市场,并在那里实现更快速的增长。
当然,留存数据的用途远不止这些基础解读,但如果你想快速获取实际价值,这不失为一个好的起点。
### 曲线、表格视图、筛选器与 CSV 导出 \{#curves-table-view-filters-and-csv-export\}
现在我们对留存目的和基本解读方式有了共同认识,接下来介绍让这一切变得便捷的工具。
Adapty 留存功能的核心是数据图表。它展示了留存率如何随用户生命周期各阶段的推进而变化。
各阶段显示在横轴上:Trial(试用)、Paid(第 1 次订阅)、P2(第 2 次订阅)、P3、P4,依此类推。
请注意,只有勾选了"Show trials"复选框时,横轴才会从 Trial 阶段开始。
该复选框对数据计算的影响如下:勾选"Show trials"后,横轴从 Trial 阶段起始,此时仅展示包含试用的路径,不显示从安装直接产生的交易,Paid 阶段也仅包含由试用转化而来的交易。若未勾选"Show trials",横轴从 Paid 阶段起始,则第一个阶段包含所有首次交易,既包括来自试用的,也包括从安装直接产生的。
当您将鼠标悬停在数据图表上时,会弹出一个包含数据摘要的浮层。如果您将鼠标悬停在下方表格的某一列上,同样会看到一个摘要浮层,并在数据图表上高亮显示相关数据。
表格中的分组和筛选条件与数据图表保持一致。
自由组合筛选条件与分组方式,进行深度分析,从数据中获取真实洞察。
可变维度:
1. 产品类型
2. 时长
3. 时间范围
4. 国家
5. 流量归因
6. 商店
使用 #Absolute 和 %Relative 控件切换所需的数据视图。
最后,在控制面板右侧有一个按钮,可将漏斗数据导出为 CSV 格式。你可以在 Excel 或 Google Sheets 中打开该文件,也可以将其导入自己的分析系统,在你熟悉的环境中继续进行分析和预测。
:::warning
请务必在 [Adapty General Settings](https://app.adapty.io/settings/general) 中标注你的应用已加入 Small Business Program。
:::
---
# File: analytics-conversion
---
---
title: "转化分析"
description: "使用 Adapty 的分析工具衡量订阅转化率。"
---
漏斗分析帮你掌握整体概况,留存分析关注用户忠诚度,而转化分析则专注于评估用户旅程中每个关键步骤的效果——并追踪其随时间的变化趋势。
转化分析可以帮助你回答以下问题:
1. 应用转化率随时间如何变化?是否存在季节性趋势?
2. 营销活动或其他新情况发生时,转化率如何变化?
3. 不同地区的用户对应用更新的响应有何不同?
4. 哪种产品类型的长期转化效果更好?
转化数据来源于我们通过 Adapty SDK 和应用商店通知收集的信息,无需您进行额外配置。
## 主要控件与数据图表 \{#main-controls-and-charts\}
营收虽然是衡量成功的常用指标,但它只是整体图景的一部分。了解业务在不同用户行为和生命周期阶段随时间的表现同样重要,这正是转化分析的用武之地。
通过设置筛选条件和分组,您可以挖掘更多关于用户行为的有价值洞察。要识别和分析趋势,可以按日、月或年监控转化数据的变化情况。
在数据图表左侧,你可以找到转化步骤控制器,用于选择要追踪的具体转化路径——例如安装 → 试用、试用 → 付费,或付费 → 续订。
每项转化数据图表遵循以下逻辑:
- 设 **X** 为在所选日期进入起始状态的用户数(例如安装量)。
- 设 **Y** 为其中最终达到目标状态的用户数(例如开始试用)。
- 转化率计算公式为:**转化率 = (Y / X) × 100%**
:::note
数据图表中显示的日期对应用户进入初始状态 (X) 的时间——即他们具备转化条件的那一刻。
:::
请参阅下方各转化说明及对应示例。
### 安装 -> 付费 \{#install---paid\}
此数据图表显示在特定日期安装应用的用户中,最终购买了首个订阅的用户占比。
**预测收入**列显示所选时间范围内,某个订阅者同期群在创建后预计产生的总收入估算值。该值基于应用历史同期群留存数据,由 Adapty 的趋势预测模型计算得出。
**预测 LTV**列显示所选同期群中每位用户的预计生命周期价值。该值由预测收入除以同期群中预测付费用户数得出。
### 选择预测周期 \{#select-the-horizon\}
如需更改趋势预测周期,请从 **Predictions** 下拉菜单中选择一个值。可选项包括同期群创建后的 3、6、9、12、18 和 24 个月。
### 按产品筛选 \{#filter-by-product\}
您可以按产品筛选预测收入和 LTV。默认情况下,趋势预测基于所有购买数据——按产品筛选后,可以查看每个产品的贡献情况。
## 预测不可用的情况 \{#when-predictions-are-unavailable\}
当某个同期群无法生成趋势预测时,"预测收入"和"预测 LTV"列将显示破折号(—)而非具体数值。这可能有以下几种原因:
- **同期群创建后时间不足**:趋势预测仅在同期群完成首次续费周期后才可用——周订阅约需一周,月订阅约需四周。
- **同期群规模过小**:付费订阅者数量太少,无法生成可靠的预测结果。
- **同期群行为异常**:该同期群与模型预期的规律存在显著偏差。等待数周后随着数据积累,此问题可能自行解决。
- **超出预测范围**:同期群的存续时间超过所选预测范围。例如,3 个月的预测在三个月后隐藏,12 个月的预测在十二个月后隐藏,超过 24 个月的同期群将不显示任何预测。
:::warning
启用趋势预测时,请注意,Revenue 和 LTV 的趋势预测数据可能最多延迟 24 小时才会显示在您的 Adapty 看板上。
:::
---
# File: predictions-in-ab-tests
---
---
title: "A/B 测试中的趋势预测"
description: "了解 A/B 测试中的趋势预测如何帮助优化订阅定价策略。"
---
欢迎阅读 Adapty A/B 测试功能的预测分析文档。该工具将为您正在运行的 A/B 测试提供未来结果洞察,并帮助您借助 Adapty 的机器学习驱动趋势预测,更快速地做出数据驱动决策 🚀。
### A/B 测试趋势预测是什么?\{#what-are-ab-test-predictions\}
Adapty 的 A/B 测试趋势预测采用先进的机器学习技术(特别是梯度提升模型),对 A/B 测试中所比较付费墙的长期收入潜力进行预测。
该预测模型使您能够根据一年后的预计收入来选择最有效的付费墙,而不仅仅依赖于测试运行期间观察到的数据图表。这样一来,您可以更可靠、更快速地确定获胜者,无需等待数周时间积累数据。
### 模型是如何工作的? \{#how-does-the-model-work\}
该模型基于来自不同类别应用的大量历史 A/B 测试数据进行训练,并整合了多维度特征,用于预测付费墙在实验开始后一年内可能产生的收入。这些特征包括:
- 不同时间段内的用户交易情况与转化率
- 用户的地理分布
- 平台使用情况(iOS 或 Android)
- 退出率和退款率
- 订阅产品及其订阅周期长度(日、月、年等)
- 其他与交易相关的数据
该模型还会考虑付费墙中的试用期,使用历史转化率来预测收入,就如同用户已完成转化一样。这确保了有试用优惠和无试用优惠的付费墙之间的公平比较,因为我们也会将正在进行的试用期可能带来的未来收入纳入计算。
### 预测 P2BB 与普通 P2BB 有何不同?\{#how-is-predicted-p2bb-different-from-just-the-p2bb\}
我们的 A/B 测试采用贝叶斯方法:简单来说,我们对每位用户的收入分布(具体为"每 1000 用户收入")进行建模,然后计算一个分布"真正"优于另一个分布(而非随机偶然)的概率——这就是我们所说的"成为最优方案的概率"(P2BB)。如需了解更多,请参阅[此处](maths-behind-it)。
需要注意的是,在此过程中,我们仅依赖测试运行期间累积的收入数据。因此,如果您要运行一个对比年度订阅与周订阅的测试,则需要等待相当长的时间才能真正了解哪种方案表现更好。当您在 A/B 测试中对比试用订阅与非试用订阅时,也会出现类似情况——因为那些可能影响胜出者结果的有效试用期,在收入统计中始终未被纳入计算。
这就是我们预测模型发挥作用的地方。它基于 A/B 测试中当前的收入分布,并在大量数据集上完成训练,能够预测收入分布的未来状态(即一年后的情况)。在此基础上,它会输出一个预测的 P2BB——即如果你将测试运行整整一年所能得到的结果。
请注意,有时预测的 P2BB 可能与当前的 P2BB 相矛盾。遇到这种情况时,我们会用黄色高亮显示对应的实验变体行,如下所示:
我们认为这是一个信号,提示您需要积累更多数据来确认获胜者,或深入分析 A/B 测试以找出背后的原因。一般来说,我们建议优先参考预测 P2BB,而非当前 P2BB,因为前者纳入了更多数据作为依据,但最终决策当然由您来做。
### 模型准确性与置信度 \{#model-accuracy-and-certainty\}
该模型准确性较高,平均绝对百分比误差(MAPE)略低于 10%。这一精度水平使企业能够在做出数据驱动的决策时,放心地依赖模型的趋势预测结果。
为进一步保证结果的稳定性,模型采用了基于以下三个因素的"置信度"标准:
- 较窄的预测区间——模型对其结果有较高把握
- 测试中有足够数量的订阅与收入数据
- 距测试开始至少已过去 2 周
当以下三个标准中至少满足两个时,趋势预测被视为可靠。
当新的 A/B 测试开始时,模型会为每个付费墙提供未来一年每千次展示收入(我们 A/B 测试的核心数据图表)的趋势预测。只有满足确定性标准时,才会显示趋势预测。如果数据不足,模型将显示"数据不足,无法进行趋势预测"。
### 限制与注意事项 \{#limitations-and-considerations\}
虽然我们的预测模型是一个强大的工具,但了解其局限性同样重要。
模型的表现取决于可用数据的质量和代表性。异常的同期群行为,或训练集中未包含的新应用,都可能影响预测准确性。
尽管如此,趋势预测每天都会更新,以反映最新数据和用户行为,确保您获取的洞察始终基于最新信息。
🚧 注意:此工具是对您专业判断和对应用独特动态理解的补充,而非替代。请将这些趋势预测作为参考,结合其他数据图表和市场知识,做出明智的决策。
---
# File: adapty-ads-manager
---
---
title: "Adapty Ads Manager"
description: "从 Apple Ads 获取实时分析数据,管理和优化您的广告活动"
---
**Adapty Ads Manager** 是一款一体化平台,专为帮助您更高效地管理、优化和扩展 Apple Ads 广告系列而设计。它将您的 Apple Search Ads 效果数据与关键收入指标(如安装量、试用、订阅和用户生命周期价值)连接起来,无需借助 MMP。
借助实时分析、AI 驱动的预测和智能自动化,Adapty Ads Manager 消除了繁琐的人工出价调整、电子表格操作和凭感觉猜测,取而代之的是清晰的洞察和帮助您快速采取行动的工具。
使用 Adapty Ads Manager,您将获得:
- **[概览](ads-manager-overview)**:一览所有关键数据——花费、收入、ROAS、CPA 等——每项均附有每日趋势数据图表
- **[AI 助手](ads-manager-ai-agent)**:用自然语言提问,获取全链路分析与优化建议
- **实时效果数据**:覆盖广告系列、广告组和关键词
- **端到端收入追踪**:从搜索 → 安装 → 试用 → 订阅 → LTV
- **AI 趋势预测与建议**:助力盈利增长
- **批量管理**:出价、预算、状态与结构
- **[基于规则的自动化](ads-manager-automations)**:管理关键词全生命周期
- **[市场洞察](ads-manager-market-intelligence)**:覆盖 50+ 个国家的竞品关键词策略
- **[CPP A/B 测试](ads-manager-cpp-ab-tests)**:对比自定义产品页面,找出最佳方案
## 第 2 步:连接广告平台并添加跟踪链接 \{#step-2-connect-your-ad-platform-and-add-tracking-links\}
Adapty 通过跟踪链接将应用安装与广告系列数据关联起来。
你必须将跟踪链接作为目标 URL,用于所有希望在 Adapty Attribution 中衡量效果的广告系列。
如果你在多个平台上投放广告,请分别为每个平台设置跟踪链接。
Adapty 与广告平台的对接方式有以下两种:
- **原生集成(Meta Ads、TikTok Ads)。** Adapty 直接与广告平台对接。追踪链接自动生成,广告系列参数根据链接的使用位置动态填充。同一条链接可跨不同广告系列、广告组或创意素材使用,Adapty 会自动接收正确的广告系列数据和广告花费。
- **仅限追踪链接(所有其他广告平台)。** Adapty 不连接广告平台。追踪链接需手动创建,所有推广活动参数必须在创建链接时明确定义。这些平台不支持广告花费数据。
3. 在策略编辑器中,粘贴以下 JSON,并将 `adapty-s3-integration-test` 替换为你的存储桶名称:
```json showLineNumbers title="Json"
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListObjectsInBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::adapty-s3-integration-test"
},
{
"Sid": "AllowAllObjectActions",
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": [
"arn:aws:s3:::adapty-s3-integration-test/*",
"arn:aws:s3:::adapty-s3-integration-test"
]
},
{
"Sid": "AllowBucketLocation",
"Effect": "Allow",
"Action": "s3:GetBucketLocation",
"Resource": "arn:aws:s3:::adapty-s3-integration-test"
}
]
}
```
4. 完成策略配置后,您可以选择添加标签(可选),然后点击 **Next** 进入最后一步
5. 在此步骤中,为您的策略命名,然后点击 **Create policy** 按钮完成创建
#### 1.2. 创建 IAM 用户 \{#12-create-iam-user\}
要允许 Adapty Attribution 将原始数据报告上传到您的存储桶,您需要为拥有该存储桶写入权限的用户提供 Access Key ID 和 Secret Access Key。
1. 前往 IAM 控制台,选择 [Users 部分](https://console.aws.amazon.com/iamv2/home#/users)
2. 点击 **Add users** 按钮
3. 为用户设置名称,选择 **Access key – Programmatic access**,然后继续配置权限
4. 在下一步中,请选择 **Add user to group** 选项,然后点击 **Create group** 按钮
5. 接下来,您需要为用户组指定一个名称,并选择之前创建的策略
6. 选择策略后,点击 **Create group** 按钮完成操作
7. 成功创建群组后,请**选择它**并继续下一步
8. 这是本部分的最后一步,直接点击 **Create User** 按钮即可。
9. 最后,你可以选择**以 .csv 格式下载凭据**,或者直接从看板中复制并粘贴凭据。
### 第 2 步:在 Adapty Attribution 中配置集成 \{#step-2-configure-integration-in-adapty-attribution\}
1. 前往 [**Integrations** -> **Amazon S3**](https://app.adapty.io/ua/integrations/s3)
2. 开启 **Export install events to Amazon S3** 开关。
3. 填写以下字段,以建立 Amazon S3 与 Adapty Attribution 用户画像之间的连接:
| 字段 | 描述 |
|:-----------------------------| :----------------------------------------------------------- |
| **Access Key ID** | 用于验证用户或应用程序访问 AWS 服务的唯一标识符。可在下载的 [csv 文件](ua-amazon-s3#step-1-create-amazon-s3-credentials) 中找到此 ID。 |
| **Secret Access Key** | 与 Access Key ID 配合使用的私钥,用于验证用户或应用程序访问 AWS 服务。可在下载的 [csv 文件](ua-amazon-s3#step-1-create-amazon-s3-credentials) 中找到此密钥。 |
| **S3 Bucket Name** | 在 AWS 云中标识特定 S3 存储桶的全局唯一名称。S3 存储桶是一种简单的存储服务,允许用户在云中存储和检索文件、图片等数据对象。 |
| **Folder Inside the Bucker** | 您希望在所选 S3 存储桶中创建的文件夹名称。请注意,S3 通过对象键前缀来模拟文件夹,这些前缀本质上就是文件夹名称。 |
| **Region**(可选) | 在 AWS 管理控制台中,于您的 IAM 用户账户下获取您的区域信息。 |
## 手动导出数据 \{#manual-data-export\}
除了自动将事件数据导出到 Amazon S3 之外,Adapty Attribution 还提供手动文件导出功能。通过此功能,您可以选择特定日期的用户获取数据,并手动将其导出到您的 S3 存储桶。这让您能够更灵活地控制导出的数据内容及导出时机。
## 表结构 \{#table-structure\}
在 AWS S3 集成中,Adapty Attribution 提供一张表用于存储安装事件的历史数据。该表包含用户画像、收入与实际所得、来源商店等多项数据信息。
:::warning
请注意,随着我们或第三方合作伙伴引入新数据,该结构可能会持续扩展。请确保处理该数据的代码足够健壮,只依赖特定字段,而不依赖整体结构。
:::
以下是事件的表结构:
| 字段 | 说明 |
|--------------------------|-------------------------------------------|
| `adapty_profile_id` | Adapty 用户画像唯一标识符 |
| `install_id` | 安装唯一标识符 |
| `created_at` | 记录创建时间戳(ISO 8601) |
| `installed_at` | 应用安装时间戳(ISO 8601) |
| `store` | 应用商店(`ios`、`android`) |
| `country` | 用户国家代码(ISO 3166-1 alpha-2) |
| `ip_address` | 客户端 IP 地址 |
| `idfa` | iOS 广告主标识符 |
| `idfv` | iOS 供应商标识符 |
| `gaid` | Google 广告 ID(Android) |
| `android_id` | Android 设备 ID |
| `app_set_id` | Android App Set ID |
| `channel` | 归因渠道 |
| `campaign_id` | 广告系列标识符 |
| `campaign_name` | 广告系列名称 |
| `adset_id` | 广告组标识符 |
| `adset_name` | 广告组名称 |
| `ad_id` | 广告标识符 |
| `ad_name` | 广告名称 |
| `keyword_id` | 关键词标识符 |
| `keyword_name` | 关键词名称 |
| `asa_org_id` | Apple Search Ads 组织 ID |
| `asa_keyword_match_type` | ASA 关键词匹配类型(`Exact`、`Broad`) |
| `asa_attribution` | ASA 归因数据(JSON 字符串) |
| `asa_conversion_type` | ASA 转化类型 |
| `asa_country_or_region` | ASA 国家或地区 |
| `asa_creative_set_name` | ASA 创意集名称 |
| `fbclid` | Facebook 点击 ID |
| `ttclid` | TikTok 点击 ID |
| `utm_source` | UTM 来源参数 |
| `utm_medium` | UTM 媒介参数 |
| `utm_campaign` | UTM 广告系列参数 |
| `utm_term` | UTM 词语参数 |
| `utm_content` | UTM 内容参数 |
---
# File: ua-google-cloud-storage
---
---
title: "Adapty Attribution 中的 Google Cloud Storage"
description: "将 Google Cloud Storage 与 Adapty Attribution 集成,实现安全的用户获取数据存储。"
---
Adapty Attribution 与 Google Cloud Storage 的集成,让您可以将用户获取活动数据安全地存储在一个集中位置。您可以将活动效果数据、归因数据和用户获取事件以 .csv 文件的形式保存到您的 Google Cloud Storage 存储桶中。
要设置此集成,您只需在 Google Cloud Console 和 Adapty Attribution 看板中完成几个简单步骤。
:::note
计划
Adapty Attribution 每天 UTC 时间 4:00 将您的数据发送至 Google Cloud Storage。
每个文件包含前一整个自然日(UTC 时间)内产生的事件数据。例如,3 月 8 日 UTC 04:00 自动导出的数据,包含 3 月 7 日 00:00:00 至 23:59:59(UTC)的所有事件。
:::
## 如何设置 Google Cloud Storage 集成 \{#how-to-set-up-google-cloud-storage-integration\}
### 第一步:创建 Google Cloud Storage 凭证 \{#step-1-create-google-cloud-storage-credentials\}
本指南将帮助你在 Google Cloud Platform Console 中创建所需的凭证。
为了让 Adapty Attribution 将原始数据报告上传到你指定的存储桶,需要提供服务账号的密钥,并授予对应存储桶的写入权限。通过提供服务账号密钥并授予存储桶写入权限,你可以让 Adapty Attribution 安全高效地将原始数据报告从其平台传输到你的存储环境。
:::warning
请注意,我们仅支持服务账号 HMAC 密钥授权,因此请务必确保您的服务账号 HMAC 密钥已添加"Storage Object Viewer"、"Storage Legacy Bucket Writer"和"Storage Object Creator"角色,以便正常访问 Google Cloud Storage。
:::
#### 2.1. 创建服务账号 \{#21-create-service-account\}
1. 前往您的 Google Cloud 账号的 [IAM](https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts) 部分,选择相关项目或新建一个项目
2. 接下来,点击 "+ CREATE SERVICE ACCOUNT" 按钮,为 Adapty Attribution 创建一个新的服务账号。
3. 填写第一步中的各个字段,访问权限将在后续步骤中授予。如需了解该页面的更多详情,请参阅[文档](https://docs.cloud.google.com/iam/docs/service-accounts-create)。
4. 要创建并下载 [JSON 私钥](https://docs.cloud.google.com/iam/docs/keys-create-delete),请导航至 KEYS 部分,然后点击 "ADD KEY" 按钮
5. 在 DETAILS 部分,找到与刚创建的服务账户关联的 Email 值并复制。后续步骤中,授权该账户并允许其写入存储桶时需要用到这条信息。
#### 2.2. 配置 Bucket 权限 \{#22-configure-bucket-permissions\}
6. 前往 Google Cloud Storage 的[存储桶](https://console.cloud.google.com/storage/browser)页面,选择一个现有存储桶或新建一个存储桶,用于存储来自 Adapty Attribution 的用户获取数据报告。
7. 导航至 **PERMISSIONS** 部分,选择[授予访问权限](https://docs.cloud.google.com/identity/docs/how-to?hl=en)选项。
8. 在 PERMISSIONS 部分,输入第五步中获取的服务账户 Email,然后选择 Storage Object Creator 角色
9. 最后,点击 SAVE 以保存更改
10. 请记录存储桶的名称,以备后续使用
11. 完成上述步骤后,您已成功完成在 Google Cloud Console 中的必要配置!最后一步是输入存储桶名称,并下载 JSON 文件以在 Adapty 归因中使用
### 第二步:在 Adapty Attribution 中配置集成 \{#step-2-configure-integration-in-adapty-attribution\}
1. 前往 [**Integrations** -> **Google Cloud Storage**](https://app.adapty.io/ua/integrations/google-cloud-storage)
2. 打开 **Export install events to Google Cloud Storage** 开关
3. 填写必填字段,以建立 Google Cloud Storage 与 Adapty Attribution 之间的连接:
| 字段 | 描述 |
|:------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Google Cloud service account key file** | 下载的私有 [JSON 密钥文件](ua-google-cloud-storage#step-1-create-google-cloud-storage-credentials)。 |
| **Google Cloud bucket name** | 您希望在 Google Cloud Storage 中存储数据的存储桶名称。该名称在 Google Cloud Storage 环境中必须唯一,且不能包含空格。 |
| **Folder inside the bucket** | 存储桶内用于存储数据的文件夹名称。该名称在存储桶内必须唯一,可用于整理数据。此字段为选填项。 |
## 手动数据导出 \{#manual-data-export\}
除了自动将事件数据导出到 Google Cloud Storage 之外,Adapty Attribution 还提供手动文件导出功能。借助此功能,您可以选择特定日期的用户获取数据,并手动将其导出到您的 GCS 存储桶。这让您能够更灵活地控制导出的数据内容及导出时机。
## 表结构 \{#table-structure\}
在 Google Cloud Storage 集成中,Adapty 归因提供了一张表,用于存储安装事件的历史数据。该表包含用户画像、收入与收益、来源商店等多个数据点的信息。
:::warning
请注意,随着我们或第三方合作伙伴引入新数据,此结构可能会随时间不断扩展。请确保处理该数据的代码具有足够的健壮性,仅依赖特定字段,而不依赖整体结构。
:::
以下是事件的表结构:
| 列名 | 描述 |
|--------------------------|-------------------------------------------|
| `adapty_profile_id` | Adapty 用户画像唯一标识符 |
| `install_id` | 安装唯一标识符 |
| `created_at` | 记录创建时间戳(ISO 8601) |
| `installed_at` | 应用安装时间戳(ISO 8601) |
| `store` | 应用商店(`ios`、`android`) |
| `country` | 用户国家代码(ISO 3166-1 alpha-2) |
| `ip_address` | 客户端 IP 地址 |
| `idfa` | iOS 广告标识符 |
| `idfv` | iOS 供应商标识符 |
| `gaid` | Google 广告 ID(Android) |
| `android_id` | Android 设备 ID |
| `app_set_id` | Android App Set ID |
| `channel` | 归因渠道 |
| `campaign_id` | 营销活动标识符 |
| `campaign_name` | 营销活动名称 |
| `adset_id` | 广告组标识符 |
| `adset_name` | 广告组名称 |
| `ad_id` | 广告标识符 |
| `ad_name` | 广告名称 |
| `keyword_id` | 关键词标识符 |
| `keyword_name` | 关键词名称 |
| `asa_org_id` | Apple Search Ads 组织 ID |
| `asa_keyword_match_type` | ASA 关键词匹配类型(`Exact`、`Broad`) |
| `asa_attribution` | ASA 归因数据(JSON 字符串) |
| `asa_conversion_type` | ASA 转化类型 |
| `asa_country_or_region` | ASA 国家或地区 |
| `asa_creative_set_name` | ASA 创意组名称 |
| `fbclid` | Facebook 点击 ID |
| `ttclid` | TikTok 点击 ID |
| `utm_source` | UTM source 参数 |
| `utm_medium` | UTM medium 参数 |
| `utm_campaign` | UTM campaign 参数 |
| `utm_term` | UTM term 参数 |
| `utm_content` | UTM content 参数 |
---
# File: adapty-mail
---
---
title: "Adapty Mail"
description: "AI 生成的邮件营销活动,将试用用户转化为付费订阅者。"
---
集成提供以下配置选项,这些选项会影响通过该集成发送的所有事件:
| 设置 | 描述 |
|:--------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Reporting Proceeds** | 选择收入值的呈现方式:扣除 App Store 和 Play Store 佣金后的净额,或扣除前的总额。勾选"Send sales as proceeds"复选框,可将销售额显示为扣除 App Store / Play Store 佣金后的收益。 |
| **Send Trial Price** | 若勾选,Adapty 将在 Trial Started 事件中传输订阅价格。 |
| **Exclude Historical Events** | 选择排除用户在安装含 Adapty SDK 的应用之前发生的事件。这可防止事件重复,并确保报告的准确性。例如,若用户在 1 月 10 日激活了月度订阅,并在 3 月 6 日更新了含 Adapty SDK 的应用,则 Adapty 将忽略 3 月 6 日之前的事件,并保留此后的事件。 |
| **Report User's Currency** | 选择以用户本地货币还是美元报告销售额。 |
| **Send User Attributes** | 若您希望发送用户特定属性(如语言偏好),且您的 OneSignal 计划支持超过 10 个标签,请选择此选项。启用后,可在默认 10 个标签之外包含附加信息。请注意,超出标签限制可能会导致错误。 |
| **Send Attributions** | 启用此选项以传输归因信息(例如 AppsFlyer 归因)并接收相关详情。 |
| **Send Play Store purchase token** | 启用此选项以接收在需要时用于重新验证购买的 Play Store 令牌。它将向事件添加 `play_store_purchase_token` 参数。 |
| **Delay events with future datetime** | **仅适用于 AppsFlyer 和自定义 Webhook**:启用后,续订和试用转化事件将在实际发生日期发送。禁用时(默认),这些事件会在检测到时立即发送,即使日期在未来也是如此。 |
| **Data residency** | **仅适用于 Mixpanel 和 Amplitude**:选择数据驻留地,以确定事件的处理和存储位置。 |
## 配置事件 \{#configure-the-events\}
在凭据下方,有三组事件可供您从 Adapty 发送到所选集成平台。您应启用所需的事件。
需要注意的是,某些集成支持自定义事件名称,而其他集成的事件名称是固定的,无法修改。此外,对于某些集成(例如 [Airbridge](airbridge#configure-events-and-tags)),您可以灵活地将多个事件名称关联到单个 Adapty 事件。点击[此处](events)查看 Adapty 提供的完整事件列表。
虽然我们建议使用 Adapty 的默认事件名称,但您也可以根据具体需求自由调整事件名称。
---
# File: events
---
---
title: "发送给第三方集成的事件"
description: "使用 Adapty 的分析工具跟踪关键订阅事件。"
---
Apple 和 Google 通过 [App Store Server Notifications](enable-app-store-server-notifications) 和 [Real-time Developer Notifications (RTDN)](enable-real-time-developer-notifications-rtdn) 将订阅事件直接发送到服务器。因此,移动应用无法可靠地将事件实时发送到分析系统。例如,如果用户订阅后再未打开应用,开发者在没有服务器的情况下将无法收到任何订阅状态更新。
Adapty 通过收集订阅数据并将其转化为易于理解的事件来弥补这一差距。这些集成事件以 JSON 格式发送。所有事件共享相同的结构,但字段会根据事件类型、商店及具体配置有所不同。您可以在各集成页面上找到每个事件所包含的具体字段。
如需了解如何判断事件是否已成功处理或是否出现问题,请查看[事件状态](event-statuses)页面。
## 事件类型 \{#event-types\}
大多数事件会在创建后发送到所有已配置的集成(前提是相应集成已启用)。但 **Access level updated** 事件仅在配置了 [webhook 集成](webhook) 且该事件已启用时才会触发。该事件会显示在 [Event Feed](https://app.adapty.io/event-feed) 中,并发送到 webhook,但不会共享给其他集成。
如果未配置 webhook 集成或未启用此事件类型,**Access level updated** 事件将不会被创建,也不会出现在 [Event Feed](https://app.adapty.io/event-feed) 中。
| 事件名称 | 描述 |
|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| subscription_started | 当用户激活没有试用期的付费订阅时触发,即立即扣款。 |
| subscription_renewed | 订阅续费并成功扣款时发生。该事件从第二次计费开始记录,无论是试用订阅还是非试用订阅。 |
| subscription_renewal_cancelled | 用户已关闭订阅自动续费。用户在付费订阅周期结束前仍可使用高级功能。 |
| subscription_renewal_reactivated | 当用户重新激活订阅自动续费时触发。 |
| subscription_expired | 当订阅取消后完全到期时触发。例如,用户在12月12日取消订阅,但订阅在12月31日到期,则该事件在12月31日记录。 |
| subscription_paused | 当用户激活[订阅暂停](https://developer.android.com/google/play/billing/lifecycle/subscriptions#pause)时发生(仅限 Android)。 |
| subscription_deferred | 当订阅购买被[延期](https://adapty.io/glossary/subscription-purchase-deferral/)时触发,允许用户延迟付款同时保留对高级功能的访问权限。此功能通过 Google Play Developer API 提供,可用于免费试用或帮助面临经济困难的用户。 |
| non_subscription_purchase | 任何非订阅购买,例如永久授权或消耗型商品(如游戏内货币)。 |
| trial_started | 当用户激活试用订阅时触发。 |
| trial_converted | 当试用期结束并成功向用户扣款(首次购买)时发生。例如,用户的试用期至1月14日,但在1月7日被扣款,则该事件在1月7日记录。 |
| trial_renewal_cancelled | 用户在试用期间关闭了订阅自动续费。用户在试用期结束前仍可使用高级功能,但不会被扣款或开始订阅。 |
| trial_renewal_reactivated | 当用户在试用期间重新激活订阅自动续费时发生。 |
| trial_expired | 当试用期结束且未转化为订阅时触发。 |
| entered_grace_period | 当付款尝试失败且用户进入宽限期(如已启用)时发生。用户在此期间保留高级访问权限。 |
| billing_issue_detected | 当扣款尝试中出现账单问题时触发(例如,卡余额不足)。 |
| subscription_refunded | 当订阅被退款时触发(例如,由 Apple 客服处理)。 |
| non_subscription_purchase_refunded | 当非订阅购买被退款时触发。 |
| access_level_updated | 当用户的访问等级更新时发生。 |
上述事件完整涵盖了用户的购买状态。下面来看一些示例。
### 示例 1 \{#example-1\}
_用户于 4 月 1 日激活了一个包含 7 天试用期的月度订阅。第 4 天,他取消了订阅。_
在这种情况下,将发送以下事件:
1. 4 月 1 日发送 `trial_started`
2. 4 月 4 日发送 `trial_renewal_cancelled`
3. 4 月 7 日发送 `trial_expired`
### 示例 2 \{#example-2\}
_用户于 4 月 1 日激活了一个包含 7 天试用期的月度订阅。第 10 天,他取消了订阅。_
在这种情况下,将发送以下事件:
1. 4 月 1 日发送 `trial_started`
2. 4 月 7 日发送 `trial_converted`
3. 4 月 10 日发送 `subscription_renewal_cancelled`
4. 5 月 1 日发送 `subscription_expired`
有关每种场景下触发哪些事件的详细说明,请查看[事件流程](event-flows)。
---
# File: event-flows
---
---
title: "事件流"
description: "了解 Adapty 中订阅事件流的详细方案。学习订阅事件如何生成并发送到各集成渠道,帮助您追踪客户旅程中的关键节点。"
---
在 Adapty 中,您将在用户使用应用的整个历程中收到各种订阅事件。以下订阅流程涵盖了常见场景,帮助您了解 Adapty 在用户订阅、取消或重新激活订阅时生成的事件。
请注意,Apple 会在实际开始/续订时间前数小时处理订阅付款。为保持图表简洁,以下流程图将订阅开始/续订与付款扣除显示为同时发生。
此外,与同一操作相关的事件会同时发生,在 **Event Feed** 中的显示顺序可能不固定,与我们图示中的顺序也可能有所不同。
## 订阅生命周期 \{#subscription-lifecycle\}
### 初次购买流程 \{#initial-purchase-flow\}
当用户首次购买订阅且没有试用期时,会触发以下事件:
- **Subscription started**
- **Access level updated**:授予用户访问权限
当订阅到达续期日期时,订阅将自动续期,并触发以下事件:
- **Subscription renewal**:开始新一个订阅周期
- **Access level updated**:更新订阅到期日期,将访问权限延长至下一个周期
付款失败或用户取消续订的情况分别在[账单问题结果流程](event-flows#billing-issue-outcome-flow)和[订阅取消流程](event-flows#subscription-cancellation-flow)中描述。
### 订阅取消流程 \{#subscription-cancellation-flow\}
当用户取消订阅时,系统会创建以下事件:
- **Subscription renewal canceled**:表示订阅在当前周期结束前仍保持有效,之后用户将失去访问权限
- **Access level updated**:用于禁用该访问等级的自动续费功能
订阅到期后,系统会触发 **Subscription expired (churned)** 事件,标志着订阅的结束。
如果退款申请获批,以下事件将替代 **Subscription expired (churned)**:
- **Subscription refunded**:终止订阅并提供退款详情
对于 Stripe,订阅可以立即取消,跳过剩余的订阅周期。在这种情况下,所有事件会同时创建:
- **Subscription renewal cancelled**
- **Subscription expired (churned)**
- **Access Level updated**(用于移除用户的访问等级)
如果退款申请获批,系统还会触发 **Subscription refunded** 事件。
### 订阅重新激活流程 \{#subscription-reactivation-flow\}
如果用户取消订阅后,订阅到期,之后又重新购买了同一订阅,系统将创建一个 **Subscription renewed** 事件。即使中间存在访问中断,Adapty 也会通过 `vendor_original_transaction_id` 将其视为同一交易链,因此此次重购被视为续订。
**Access level updated** 事件将被创建两次:
- 在订阅结束时,撤销用户的访问权限
- 在订阅重新购买时,授予访问权限
### 订阅暂停流程(仅限 Android)\{#subscription-pause-flow-android-only\}
此流程适用于用户在 Android 上暂停并随后恢复订阅的情况。
暂停订阅会产生延迟效果。如果用户在订阅续期前将其暂停,订阅仍保持有效,用户在当前计费周期剩余时间内继续享有付费访问权限。
1. 当用户暂停订阅时,会触发 **Subscription paused (Android only)** 事件。
2. 订阅周期结束时,Adapty 会触发 **Access level updated** 事件以撤销用户的访问权限。
3. 当用户恢复订阅时,将触发以下事件:
- **Subscription renewed**
- **Access level updated**(用于恢复用户的访问权限)
这些订阅将属于同一交易链,并通过相同的 **vendor_original_transaction_id** 关联。
## 试用流程 \{#trial-flows\}
如果您在应用中使用试用功能,您将收到额外的试用相关事件。
### 试用期成功转化流程 \{#trial-with-successful-conversion-flow\}
最常见的流程是:用户开始试用、绑定信用卡,并在试用期结束后成功转化为标准订阅。在此场景中,试用开始时会生成以下事件:
- **Trial started**:标记试用开始
- **Access level updated**:授予访问权限
当标准订阅正式生效时,系统会生成 **Trial converted** 事件。
### 试用未成功转化的流程 \{#trial-without-successful-conversion-flow\}
如果用户在试用期转化为订阅之前取消,系统会在取消时创建以下事件:
- **Trial renewal cancelled**:禁用试用期自动转化为订阅
- **Access level updated**:禁用访问等级续订
用户仍可使用至试用期结束,届时系统会创建 **Trial expired** 事件,标记试用期正式结束。
### 试用期到期后重新激活订阅的流程 \{#subscription-reactivation-after-expired-trial-flow\}
如果试用期因账单问题或取消而到期,用户后续购买订阅时,系统将创建以下事件:
- **访问等级已更新**,为用户授予访问权限
- **试用已转化**
即使试用期与订阅之间存在时间间隔,Adapty 也会通过 `vendor_original_transaction_id` 将两者关联起来。此次转化被视为一条连续交易链的一部分,该链从零价格的试用期开始。这就是系统创建 **试用已转化** 事件而非 **订阅已开始** 事件的原因。
## 产品变更 \{#product-changes\}
本节涵盖对活跃订阅所做的各类调整,例如升级、降级,或购买其他组合中的产品。
### 立即生效的产品变更流程 \{#immediate-product-change-flow\}
用户变更产品后,系统可以在订阅结束前立即完成切换(通常发生在升级或替换产品的情况下)。此时,在产品变更的瞬间:
- 访问等级发生变更,系统创建两个 **Access level updated** 事件:
1. 撤销第一个产品的访问权限。
2. 授予第二个产品的访问权限。
- 旧订阅结束,并退款(系统创建 **Subscription refunded** 事件,`cancellation_reason` = `upgraded`)。请注意,此时不会创建 **Subscription expired (churned)** 事件;**Subscription refunded** 事件将替代它。
- 新订阅开始(系统为新产品创建 **Subscription started** 事件)。
如果用户降级订阅,第一个订阅将持续到已付费周期结束,届时将被新的低级别订阅替换。在这种情况下,系统会立即创建 **Access level updated** 事件以禁用自动续订访问权限。所有其他事件将在订阅实际发生替换时创建:
- 另一个 **Access level updated** 事件被创建,以授予对第二个产品的访问权限。
- **Subscription expired (churned)** 事件被创建,以结束第一个产品的订阅。
- **Subscription started** 事件被创建,以开始新产品的新订阅。
### 延迟产品变更流程 \{#delayed-product-change-flow\}
还有一种情况:用户在订阅续费时更改产品。这种情况与前一种非常相似:系统会立即创建一个 **Access level updated** 事件,以禁用旧产品的访问等级自动续费。所有其他事件将在用户更改订阅且变更生效于系统时创建:
- 另一个 **Access level updated** 事件被创建,以授予对第二个产品的访问权限。
- **Subscription expired (churned)** 事件被创建,以结束第一个产品的订阅。
- **Subscription started** 事件被创建,以启动新产品的新订阅。
## 账单问题结果流程 \{#billing-issue-outcome-flow\}
如果试用转换或订阅续费因账单问题失败,后续流程取决于是否启用了宽限期。
启用宽限期时,若付款成功,试用将完成转换或订阅将完成续费。若付款失败,应用商店会继续尝试向用户收取订阅费用,如仍失败,则应用商店将自行终止试用或订阅。
因此,在账单问题发生时,Adapty 中会创建以下事件:
- **检测到账单问题**
- **已进入宽限期**(如果已启用宽限期)
- **访问等级已更新**,将访问权限延续至宽限期结束
如果后续付款成功,Adapty 会记录 **Trial converted** 或 **Subscription renewed** 事件,用户不会失去访问权限。
如果付款最终失败且应用商店取消了订阅,Adapty 将生成以下事件:
- **Trial expired** 或 **Subscription expired (churned)**,附带 `cancellation_reason: billing_error`
- **访问等级已更新**,撤销用户的访问权限
如果没有宽限期,账单重试期(应用商店持续尝试向用户收费的时段)将立即开始。
如果在宽限期结束前付款始终未能成功,流程相同:当应用商店自动终止订阅时,会生成相同的事件:
- **Trial expired** 或 **Subscription expired (churned)** 事件,其 `cancellation_reason` 为 `billing_error`
- **Access level updated** 事件,用于撤销用户的访问等级
## 跨用户账户共享购买的流程 \{#sharing-purchases-across-user-accounts-flows\}
当一个
以下是此场景中生成的事件里,与访问等级分配及转移相关字段的说明:
- **用户 A:访问等级已更新(当用户 A 在应用内购买订阅时发送)**
```json showLineNumbers
{
"profile_id": "00000000-0000-0000-0000-000000000000",
"customer_user_id": UserA,
"event_properties": {
"profile_has_access_level": true,
},
"profiles_sharing_access_level": null
}
```
- **用户 A:访问等级已更新(当应用重新安装并由用户 B 登录,撤销用户 A 的访问权限时发送)**
```json showLineNumbers
{
"profile_id": "00000000-0000-0000-0000-000000000000",
"customer_user_id": UserA,
"event_properties": {
"profile_has_access_level": false,
},
"profiles_sharing_access_level": null
}
```
- **用户 B:访问等级已更新(当用户 B 登录并获得访问权限时发送)**
```json showLineNumbers
{
"profile_id": "00000000-0000-0000-0000-000000000001",
"customer_user_id": UserB,
"event_properties": {
"profile_has_access_level": true,
},
"profiles_sharing_access_level": null
}
```
### 用户间共享访问流程 \{#shared-access-between-users-flow\}
此选项允许多个用户共享同一访问等级,前提是他们的设备登录了相同的 Apple/Google ID。当用户重新安装应用并使用不同的邮箱登录时,仍可访问之前的购买内容,此选项非常适合这种场景。启用此选项后,多个已识别用户可以共享同一访问等级。在共享访问等级期间,所有交易记录均归属于原始
以下是该场景中生成的事件里,与访问等级分配和共享相关的字段说明:
**用户 B:Access level updated(当用户 B 登录并获得访问权限时发送)**
```json showLineNumbers
{
"profile_id": "00000000-0000-0000-0000-000000000000",
"customer_user_id": UserA,
"event_properties": {
"profile_has_access_level": true,
},
"profiles_sharing_access_level": [
{
"profile_id": "00000000-0000-0000-0000-000000000001,
"customer_user_id": UserB
}
]
}
```
### 用户之间访问权限不共享的流程 \{#access-not-shared-between-users-flow\}
使用此选项时,只有第一个获得该访问等级的用户画像能永久保留它。如果购买需要绑定到唯一的
---
# File: event-statuses
---
---
title: "集成事件状态"
description: ""
---
Adapty 根据 HTTP 状态码判断是否成功送达,将 `200-399` 范围之外的所有响应视为失败。
您可以在 Adapty 看板的 **Event List** 中跟踪集成事件的状态。无论特定集成是否启用了某种事件类型,系统都会显示所有已启用集成的状态。
- 黑色:事件已成功发送。
- 灰色:该事件类型在此集成中已禁用。
- 红色:集成存在需要关注的问题。
如需查看失败事件的详细信息,请将鼠标悬停在集成名称上,即可看到包含具体错误信息的提示框。
**Event Feed** 仅显示过去两周的数据以优化性能。此限制可提升页面加载速度,使用户能够更高效地浏览和分析事件。
---
# File: adjust
---
---
title: "Adjust"
description: "将 Adjust 与 Adapty 连接,以更好地追踪订阅数据和分析。"
---
[Adjust](https://www.adjust.com/) 是领先的移动归因平台(MMP)之一,用于收集和呈现营销活动数据,帮助企业追踪广告投放效果。
Adapty 提供了一套完整的数据,让您可以在一个地方追踪来自各应用商店的[订阅事件](events)。借助 Adapty,您可以轻松了解订阅用户的行为规律、掌握他们的偏好,并据此进行精准有效的沟通。因此,本集成支持您在 Adjust 中追踪订阅事件,精确分析每个推广活动带来的收益。
Adapty 与 Adjust 的集成主要通过以下两种方式实现。
1. **Adapty 从 Adjust 接收归因数据**
完成 Adjust 集成配置后,Adapty 将开始从 Adjust 接收归因数据。你可以在用户的用户画像页面轻松查看这些数据。
2. **Adapty 将订阅事件发送至 Adjust**
Adapty 可以将所有在集成中配置的订阅事件发送至 Adjust,从而让你在 Adjust 看板中追踪这些事件。这一集成有助于评估广告活动的效果。
## 设置集成 \{#set-up-integration\}
### 将 Adapty 连接到 Adjust \{#connect-adapty-to-adjust\}
1. 打开 Adapty 看板,进入 [Integrations > Adjust](https://app.adapty.io/integrations/adjust)。
2. 将页面顶部的开关打开。
3. 填写各字段,并设置您的访问凭据。
3. 如果您在 Adjust 平台上启用了 OAuth 授权,则在集成 iOS 和 Android 应用时必须提供 **OAuth Token**。
4. 接下来,提供您 iOS 和 Android 应用的 **app tokens**。打开 Adjust 看板,即可看到您的应用。
:::note
您在 iOS 和 Android 上可能有不同的 Adjust 应用,因此在 Adapty 中为此提供了两个独立的配置区域。如果您只有一个 Adjust 应用,直接填写相同的信息即可。
:::
5. 从列表中选择您的应用,并复制 **App Token**。将该 token 粘贴到 Adapty 看板中对应的字段里。
### 配置事件和标签 \{#configure-events-and-tags\}
Adjust 的工作方式与其他平台略有不同。你需要在 Adjust 看板中手动创建事件,获取事件令牌,然后将其复制粘贴到 Adapty 中对应的事件里。
因此,第一步是找到你希望 Adapty 发送的所有事件的事件令牌。具体操作如下:
1. 在 Adjust 看板中,打开你的应用并切换到 **Events** 标签页。
1. 复制事件 token 并粘贴到 Adapty 中。在凭据下方,有三组事件可从 Adapty 发送到 Adjust。点击[此处](events)查看 Adapty 提供的完整事件列表。
Adapty 将通过服务器到服务器的集成方式向 Adjust 发送订阅事件,让你可以在 Adjust 看板中查看所有订阅事件,并将其与获客活动关联起来。
:::important
请注意以下几点:
- Adjust 不支持 58 天以前的事件。如果某个事件超过 58 天,Adapty 仍会将其发送给 Adjust,但事件时间戳会被替换为当前时间。
- Adjust 不支持 IPv6。如果你在 **App settings** 或 SDK 激活时禁用了 IP 收集,后端可能只会发送 IPv6,导致追踪失败——请保持 SDK 的 IP 收集功能开启,以确保使用 IPv4。
:::
### 将您的应用与 Adjust 连接 \{#connect-your-app-to-adjust\}
完成上述步骤后,在您的应用中添加以下两个方法,以建立应用与 Adjust 之间的通信:
1. **向 Adjust 发送订阅数据**:将 Adjust 设备 ID 传入 `setIntegrationIdentifier()` SDK 方法
2. **从 Adjust 接收归因数据**:通过 `updateAttribution()` SDK 方法更新归因数据
如使用 Adjust 5.0 或更高版本,请参考以下示例:
这两项信息均可在您的 Airbridge 看板的 [Third-party Integrations > Adapty](https://app.airbridge.io/app/testad/integrations/third-party/adapty) 部分找到。
Adapty API 令牌字段由 Adapty 后端预先生成。您需要复制 Adapty API 令牌的值,并将其粘贴到 Airbridge 看板的 Adapty Authorization Token 字段中。
### 配置事件和标签 \{#configure-events-and-tags\}
在凭据下方,有三组您可以从 Adapty 发送到 Airbridge 的事件。
只需开启您需要的事件即可。
### 将您的应用连接到 Airbridge \{#connect-your-app-to-airbridge\}
进行集成时,您需要将 `airbridge_device_id` 传递给 profile builder,并按照以下示例调用 `setIntegrationIdentifier`:
## 设置集成 \{#set-up-integration\}
### 将 Adapty 连接到 AdServices 框架 \{#connect-adapty-to-the-adservices-framework\}
通过 [AdServices](https://developer.apple.com/documentation/adservices) 使用 Apple Ads 需要在 Adapty 看板中进行一些配置,同时也需要在应用端启用该功能。按照以下步骤,通过 Adapty 使用 AdServices 框架完成 Apple Ads 的设置:
#### 步骤 1:获取公钥 \{#step-1-obtain-public-key\}
在 Adapty 看板中,前往 [Settings -> Apple Ads。](https://app.adapty.io/settings/apple-search-ads)
找到预先生成的公钥(Adapty 会为您提供一对密钥)并复制。
:::note
如果您使用其他服务或自有方案进行 Apple Ads 归因,可以上传您自己的私钥。
:::
#### 第二步:在 Apple Ads 上配置用户管理 \{#step-2-configure-user-management-on-apple-ads\}
在您的 [Apple Ads 账户](https://ads.apple.com/app-store)中,前往 **Settings > User Management** 页面。为使 Adapty 能够获取归因数据,您需要邀请另一个 Apple ID 账户并授予其 API Account Manager 访问权限。您可以使用任何有权限的账户,或专门创建一个新账户。重要的是,您必须能够使用该 Apple ID 登录 Apple Ads。
#### 步骤 3:生成 API 凭据 \{#step-3-generate-api-credentials\}
接下来,在 Apple Ads 中登录新添加的账户,进入 Apple Ads 界面中的 Settings -> API,将之前复制的公钥粘贴到指定字段中,然后生成新的 API 凭据。
#### 步骤 4:在 Adapty 中配置 Apple Ads 凭据 \{#step-4-configure-adapty-with-apple-ads-credentials\}
从 Apple Ads 设置中复制 Client ID、Team ID 和 Key ID 字段。在 Adapty 看板中,将这些凭据粘贴到对应字段中。
### 将您的应用连接到 AdServices 网络 \{#connect-your-app-to-the-adservices-network\}
完成 [AdServices 框架设置](#connect-the-adservices-framework)后,Adapty 会自动开始收集 Apple Search Ad 归因数据。您无需添加任何 SDK 代码。
对于 iOS 应用,此归因数据将**始终**优先于其他来源的数据。如果不需要此行为,请按照以下说明*禁用* ASA 归因。
## 禁用集成 \{#disable-integration\}
要关闭 Apple Search Ads 归因,请打开 [**App Settings** -> **Apple Search Ads** 标签页](https://app.adapty.io/settings/apple-search-ads),然后关闭 **Receive Apple Search Ads attribution** 开关。
:::warning
请注意,禁用此选项将完全停止接收 ASA 分析数据。因此,ASA 将不再用于数据分析,也不会发送至任何集成。此外,SplitMetrics Acquire 和 Asapty 也将停止运行,因为它们依赖 ASA 归因才能正常工作。
此更改之前已接收的归因数据不受影响。
:::
## 上传您自己的密钥 \{#uploading-your-own-keys\}
:::note
可选
这些步骤不是 Apple Ads 归因所必需的,仅用于与 Asapty 等其他服务或您自己的解决方案配合使用。
:::
如果您使用其他服务或自己的 ASA 归因解决方案,可以使用您自己的公私密钥对。
### 第 1 步 \{#step-1\}
在终端中生成私钥
```text showLineNumbers title="Text"
openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
```
在 Adapty Settings -> Apple Ads 中上传(点击 Upload private key 按钮)
### 第 2 步 \{#step-2\}
在终端中生成公钥
```text showLineNumbers title="Text"
openssl ec -in private-key.pem -pubout -out public-key.pem
```
您可以在具有 API Account Manager 角色的账户的 Apple Ads 设置中使用此公钥。这样您就可以将生成的 Client ID、Team ID 和 Key ID 值用于 Adapty 和其他服务。
---
# File: switch-from-appsflyer-s2s-api-2-to-3
---
---
title: "从 AppsFlyer S2S API 2 切换到 3"
description: "在 Adapty 中从 AppsFlyer S2S API 2 升级到 3。"
---
根据 [AppsFlyer 官方最新公告](https://support.appsflyer.com/hc/en-us/articles/20509378973457-Bulletin-Upgrading-the-AppsFlyer-S2S-API),为了提供更安全的 API 使用体验并减少欺诈行为,AppsFlyer 已对其应用内事件的服务器对服务器(S2S)API 进行了升级。现有端点将在未来被弃用,我们建议您开始规划切换工作。
Adapty 支持 AppsFlyer S2S API 3,并为您提供从 API 2 的无缝切换。请注意,此切换为单向操作,一旦完成切换,将无法回退到 API 2。
从 AppsFlyer S2S API 2 切换到 3 的步骤如下:
1. 打开 [AppsFlyer 网站](https://www.appsflyer.com/home) 并登录。
2. 点击看板左上角的 **Your account name** -> **Security Center**。
3. 在 **Manage your account security** 窗口中,点击 **Manage your AppsFlyer API and S2S tokens** 按钮。
4. 如果您没有 S2S 令牌,请点击 **New token** 按钮。如果已有令牌,请直接跳至第 8 步。
5. 在 **New token** 窗口中,输入令牌名称。此名称仅供您参考。
6. 在 **Choose type** 列表中选择 **S2S**。
7. 请务必点击 **Create new token** 按钮以保存新令牌。
8. 在 **Tokens** 窗口中,复制 S2S 令牌。
9. 在 Adapty 看板中打开 [**Integrations** -> **AppsFlyer**](https://app.adapty.io/integrations/appsflyer)。
10. 在 **AppsFlyer S2S API** 字段中,选择 **API 3**。
11. 将复制的 S2S 密钥粘贴到 **Dev key for iOS** 和 **Dev key for Android** 字段中。
12. 点击 **Save** 按钮确认切换。
完成以上操作后,您的集成将立即切换到 AppsFlyer S2S API 3,新事件将发送至新的 URL:`https://api3.appsflyer.com/inappevent`。
---
# File: asapty
---
---
title: "Asapty"
description: "了解 Asapty 及其在 Adapty 订阅生态系统中的角色。"
---
使用 [Asapty](https://asapty.com/) 集成,您可以优化搜索广告活动。Adapty 将订阅事件发送至 Asapty,让您可以基于 Apple Search Ads 归因在那里构建自定义看板。
此特定集成不会向 Adapty 添加任何归因数据,因为我们已直接从 [ASA](apple-search-ads) 获取了所需的全部数据。
## 设置集成 \{#set-up-integration\}
### 将 Adapty 连接到 Asapty \{#connect-adapty-to-asapty\}
要集成 Asapty,请在 Adapty 看板中导航至 [Integrations > Asapty](https://app.adapty.io/integrations/asapty),并填写 Asapty ID 字段值。
Asapty ID 可在您的 Asapty 账户的 Settings > General 部分找到。
### 配置事件和标签 \{#configure-events-and-tags\}
在凭据下方,有三组事件可从 Adapty 发送到 Asapty。只需开启您需要的事件即可。查看 Adapty 提供的完整事件列表,请点击[此处](events)。
我们建议使用 Asapty 提供的默认事件名称。但您也可以根据需要更改事件名称。
### 将您的应用连接到 Asapty \{#connect-your-app-to-asapty\}
完成上述步骤后,Adapty 会自动从 Asapty 接收归因数据。无需在应用代码中显式请求归因数据。为提高归因数据准确性,请配置 Asapty 在每个事件数据中共享 `customerUserId`。
## Asapty 事件结构 \{#asapty-event-structure\}
Adapty 通过 GET 请求使用查询参数将事件发送到 Asapty。每个事件 URL 格式如下:
```
https://asapty.com/_api/mmpEvents/?source=adapty&asaptyid=a1b2c3d4&keywordid=12345&adgroupid=67890&campaignid=11223&conversiondate=1709294400000&event_name=subscription_renewed&install_time=1709100000&app_name=MyApp&json=%7B%22af_revenue%22%3A%229.99%22%2C%22af_currency%22%3A%22USD%22...%7D
```
查询参数:
| 参数 | 类型 | 描述 |
|:-----------------|:-------|:-------------------------------------------------------------|
| `source` | String | 始终为 "adapty"。 |
| `asaptyid` | String | 您凭据中的 Asapty ID。 |
| `keywordid` | String | Apple Search Ads 关键词 ID(如可用)。 |
| `adgroupid` | String | Apple Search Ads 广告组 ID(如可用)。 |
| `campaignid` | String | Apple Search Ads 广告活动 ID(如可用)。 |
| `conversiondate` | Long | 事件时间戳,单位为**毫秒**。 |
| `event_name` | String | 事件名称(从 Adapty 事件映射而来)。 |
| `install_time` | Long | 安装时间戳,单位为秒。 |
| `app_name` | String | Adapty 中的应用名称(如可用)。 |
| `json` | String | URL 编码的 JSON 字符串,包含事件详情(见下文)。 |
`json` 参数是一个 URL 编码的 JSON 字符串,包含以下字段:
| 参数 | 类型 | 描述 |
|:--------------------------|:-------|:---------------------------------------|
| `af_revenue` | String | 收入金额(字符串形式)。 |
| `af_currency` | String | 货币代码(例如 "USD")。 |
| `transaction_id` | String | 商店交易 ID。 |
| `original_transaction_id` | String | 原始商店交易 ID。 |
| `purchase_date` | Long | 购买时间戳,单位为毫秒。 |
| `original_purchase_date` | Long | 原始购买时间戳,单位为毫秒。 |
| `environment` | String | `Production` 或 `Sandbox`。 |
| `vendor_product_id` | String | 商店中的产品 ID。 |
| `profile_country` | String | 基于用户 IP 的国家代码。 |
| `store_country` | String | 商店用户的国家代码。 |
## 故障排除 \{#troubleshooting\}
- 请确保您已在 Adapty 中配置 [Apple Search Ads](apple-search-ads) 并[上传凭据](https://app.adapty.io/settings/apple-search-ads),否则 Asapty 将无法正常工作。
- 只有具有详细非自然量 ASA 归因的用户画像才会将其事件传递至 Asapty。如果归因数据不足,您将看到"The user profile is missing the required integration data."。
- 在配置集成之前创建的用户画像将无法将其事件传递至 Asapty。
- 如果尽管设置正确但与 Adapty 的集成仍无法正常工作,请确保在 [**App Settings** -> **Apple Search Ads** 标签页](https://app.adapty.io/settings/apple-search-ads)中启用了 **Receive Apple Search Ads attribution in Adapty** 开关。
---
# File: branch
---
---
title: "Branch"
description: "将 Branch 与 Adapty 集成,以追踪深度链接和应用转化。"
---
[Branch](https://www.branch.io/) 帮助客户跨设备、渠道和平台触达用户、开展互动并评估效果。这是一个专注于提升移动端营收的易用平台,通过在所有设备、渠道和平台上无缝运作的专属链接来实现这一目标。
Adapty 提供完整的数据集,让你可以在一个地方追踪来自各大应用商店的[订阅事件](events)。借助 Adapty,你可以轻松了解订阅者的行为习惯和偏好,并以有针对性、高效率的方式与他们沟通。
Adapty 与 Branch 的集成主要通过两种方式运作。
1. **从 Branch 接收归因数据**
配置 Branch 集成后,Adapty 将开始从 Branch 接收归因数据。您可以在用户画像页面轻松查看这些数据。
2. **向 Branch 发送订阅事件**
Adapty 可以将集成中配置的所有订阅事件发送到 Branch,让你能够在 Branch 看板中追踪这些事件。
## 设置集成 \{#set-up-integration\}
### 将 Adapty 连接到 Branch \{#connect-adapty-to-branch\}
要集成 Branch,请在 Adapty 看板中前往 [Integrations > Branch](https://app.adapty.io/integrations/branch),将开关从关闭切换为开启,并填写相关字段。
要获取 **Branch Key** 的值,请打开 Branch 的[账户设置](https://dashboard.branch.io/account-settings/profile),找到 **Branch Key** 字段。将其填入 Adapty 看板中的 **Key test**(用于沙盒)或 **Key live**(用于生产环境)字段。在 Branch 中,可以切换 Live 和 Tests 环境来获取对应的密钥。
### 配置事件和标签 \{#configure-events-and-tags\}
在凭证下方,有三组事件可以从 Adapty 发送到 Branch。直接开启你需要的事件即可。点击[此处](events)查看 Adapty 提供的完整事件列表。
你可以发送包含净收入(扣除 Apple/Google 分成后)的事件,也可以仅发送原始收入。此外,还可以勾选按用户本地货币上报的选项。
我们建议使用 Adapty 提供的默认事件名称,但你也可以根据需要自行修改。
Adapty 将通过服务器到服务器的集成方式向 Branch 发送订阅事件,让你可以在 Branch 看板中查看所有订阅事件,并将其与获客广告系列关联起来。
### 将您的应用连接到 Branch \{#connect-your-app-to-branch\}
1. 调用 `.setIntegrationIdentifier()` SDK 方法来初始化连接。您可以将 Branch Identity ID 传递给 `customerUserId` 参数。
:::note
第三方 SDK 会异步生成用户 ID,在 `Adapty.activate()` 执行时该 ID 可能尚未就绪。如果你的 **Customer User ID** 来自此类 SDK,请先不带该 ID 调用 `Adapty.activate()`。待 ID 到位后,依次调用 `setIntegrationIdentifier()`,再用 CUID 调用 `identify()`。
:::
1. 要查找 App ID,请打开 [App Store Connect](https://appstoreconnect.apple.com/) 中的应用页面,进入 **General** 部分的 **App Information** 页面,在屏幕左下角找到 **Apple ID**。
2. 您需要在 [Meta for Developers](https://developers.facebook.com/) 平台上创建一个应用。登录您的应用后,进入高级设置,在页面顶部即可找到 **App ID**。
3. 在您的 Meta SDK 配置中禁用客户端追踪,以防止在 Meta Ads Manager 中重复计算收益。您可以在 Meta 开发者控制台的 **App Settings > Advanced Settings** 中找到此设置。将 **Log in-app events automatically** 设置为"No"。这将确保收益事件仅通过 Adapty 的集成进行追踪。
要追踪安装和使用事件,您需要在代码中激活 Meta SDK。您可以在以下 Meta SDK 文档中找到各平台的实现详情:
- [iOS SDK](https://developers.facebook.com/docs/ios/getting-started)
- [Android SDK](https://developers.facebook.com/docs/android/getting-started)
- [Unity SDK](https://developers.facebook.com/docs/unity/getting-started/canvas)
此集成同样适用于 Android 应用。如果您在 **App Settings** 中配置了 Android SDK,只需设置 **Facebook App ID** 即可。
### 配置事件和标签 \{#configure-events-and-tags\}
请注意,Facebook Ads 集成专为使用 Meta 投放广告并根据客户行为进行优化的公司而设计。它支持 Meta 的标准事件以实现优化目的。因此,Meta Ads 集成不支持修改事件名称。Adapty 会自动将您的客户事件映射到对应的 Meta 事件,以便进行准确分析。
| Adapty 事件 | Meta Ads 事件 |
| :---------------------------- | :-------------------------- |
| Subscription initial purchase | Subscribe |
| Subscription renewed | Subscribe |
| Subscription cancelled | CancelSubscription |
| Trial started | StartTrial |
| Trial converted | Subscribe |
| Trial cancelled | CancelTrial |
| Non subscription purchase | fb_mobile_purchase |
| Billing issue detected | billing_issue_detected |
| Entered grace period | entered_grace_period |
| Auto renew off | auto_renew_off |
| Auto renew on | auto_renew_on |
| Auto renew off subscription | auto_renew_off_subscription |
| Auto renew on subscription | auto_renew_on_subscription |
StartTrial、Subscribe、CancelSubscription 均为标准事件。
要启用特定事件,只需开启您所需的事件开关。如果选择了多个事件名称,Adapty 会将所有选定事件的数据合并到同一个 Adapty 事件名称下。
### 将您的应用连接到 Facebook Ads \{#connect-your-app-to-facebook-ads\}
按照上述步骤操作后,Facebook 将自动从 Adapty 接收订阅数据。
随着 iOS 14.5 对 IDFA 的政策变更,我们建议您向 Facebook 请求用户的 `facebookAnonymousId`。这样,即使用户的 IDFA 不可用,集成也能继续正常运行。请参阅
在凭据下方,有三组事件可供您从 Adapty 发送到 Singular。点击[此处](events)查看 Adapty 提供的完整事件列表。
我们建议使用 Adapty 提供的默认事件名称,但您也可以根据需要自定义事件名称。
Adapty 将通过服务器对服务器集成向 Singular 发送订阅事件,让您可以在 Singular 看板中查看所有订阅事件,并将其与您的获客活动关联起来。
:::warning
在配置集成之前创建的用户画像将无法向 Singular 传送其事件。
:::
### 将您的应用连接到 Singular \{#connect-your-app-to-singular\}
Adapty 与 Singular 之间的集成为服务器对服务器方式,因此无需在您的应用程序中添加任何额外代码。
## 事件结构 \{#event-structure\}
Adapty 通过带有查询参数的 GET 请求将事件发送给 Singular。每个事件的结构如下:
```json
{
"n": "subscription_renewed",
"a": "singular_sdk_key_123",
"p": "iOS",
"i": "com.example.app",
"ip": "192.168.100.1",
"idfa": "00000000-0000-0000-0000-000000000000",
"idfv": "00000000-0000-0000-0000-000000000000",
"ve": "17.0.1",
"att_authorization_status": 3,
"custom_user_id": "user_12345",
"utime": 1709294400,
"amt": 9.99,
"cur": "USD",
"purchase_product_id": "yearly.premium.6999",
"purchase_transaction_id": "GPA.3383...",
"e": "{\"is_revenue_event\":true,\"amt\":9.99,\"cur\":\"USD\",\"purchase_product_id\":\"yearly.premium.6999\",\"purchase_transaction_id\":\"GPA.3383...\"}"
}
```
Where:
| 参数 | 类型 | 描述 |
|:---------------------------|:--------|:-----------------------------------------------------|
| `n` | String | 事件名称(从 Adapty 事件映射而来)。 |
| `a` | String | 你的 Singular SDK Key。 |
| `p` | String | 平台("iOS" 或 "Android")。 |
| `i` | String | 应用商店 App ID(Bundle ID)。 |
| `ip` | String | 用户的 IP 地址。 |
| `idfa` | String | **仅 iOS**。广告标识符(大写)。 |
| `idfv` | String | **仅 iOS**。供应商标识符(大写)。 |
| `aifa` | String | **仅 Android**。Google 广告 ID(小写)。 |
| `andi` | String | **仅 Android**。Android ID(小写)。 |
| `asid` | String | **仅 Android**。App Set ID(小写)。 |
| `ve` | String | 操作系统版本。 |
| `att_authorization_status` | Integer | **仅 iOS**。ATT 状态(例如,`3` 表示已授权)。 |
| `custom_user_id` | String | 用户的 Customer User ID。 |
| `utime` | Long | 事件的 UNIX 时间戳(秒)。 |
| `amt` | Float | 收入金额。 |
| `cur` | String | 货币代码(例如,"USD")。 |
| `purchase_product_id` | String | 应用商店中的产品 ID。 |
| `purchase_transaction_id` | String | 原始交易 ID。 |
| `e` | String | 包含事件详情的 JSON 字符串(见下文)。 |
`e` 参数(自定义事件数据)是一个 JSON 编码的字符串,包含:
| 参数 | 类型 | 描述 |
|:--------------------------|:--------|:-------------------------|
| `is_revenue_event` | Boolean | 若事件包含收入则为 `true`。 |
| `amt` | Float | 收入金额。 |
| `cur` | String | 货币代码。 |
| `purchase_product_id` | String | 商店中的产品 ID。 |
| `purchase_transaction_id` | String | 原始交易 ID。 |
---
# File: tenjin
---
---
title: "Tenjin 集成"
description: ""
---
Tenjin 是一个面向应用开发者和营销人员的移动端归因与分析平台。它提供工具来衡量和优化用户获取活动,并对应用性能和用户行为提供深入洞察。凭借透明灵活的方式,Tenjin 汇聚来自广告网络和应用商店的数据,帮助团队分析 ROI、追踪转化并监控关键绩效指标。
通过将[订阅事件](events)转发到 Tenjin,您可以准确了解转化来自哪里,以及哪些营销活动在所有渠道、平台和设备上带来了最大价值。本质上,Tenjin 看板为营销活动提供了高级分析功能。
通过将 Tenjin 的归因数据转发到 Adapty,您可以用额外的筛选条件丰富 Adapty 的分析数据,并将其用于同期群分析和转化分析。
该集成以两种主要方式运作:
1. **从 Tenjin 获取归因数据**
集成完成后,Adapty 会从 Tenjin 收集归因数据。你可以在 Adapty 看板的用户画像页面查看这些信息。
2. **向 Tenjin 发送订阅事件**
Adapty 会实时将购买事件发送到 Tenjin,帮助你直接在 Tenjin 看板中评估广告活动的效果。
| 集成特性 | 描述 |
| -------------------------- | ------------------------------------------------------------ |
| 计划安排 | 实时 |
| 数据方向 | 双向传输:
3. 登录 [Tenjin 看板](https://tenjin.com/)。
4. 在导航菜单中,前往 **Configuration** -> **Apps**。
5. 选择对应平台(iOS 或 Android)的应用,然后切换到 **App and SDK** 标签页。
6. 在 **App and SDK** 标签页中,点击 **SDK Key** 列的 **Copy**。如果你还没有 SDK 密钥,请点击 **Generate SDK Key** 按钮创建一个。
7. 返回 Adapty 看板,将复制的 SDK Key 粘贴到对应平台的字段中:
- iOS 应用:粘贴到 **iOS SDK Key** 或 **iOS Sandbox SDK Key** 字段
- Android 应用:粘贴到 **Android SDK Key** 或 **Android Sandbox SDK Key** 字段
:::info
Tenjin 的服务端集成没有专门的沙盒模式。请使用单独的 Tenjin 应用,或对生产环境和沙盒事件使用同一个 Key。
:::
8. 如果你同时有两个平台的应用,请针对另一个平台重复步骤 5-7。
9. (可选)根据需要调整 **How the revenue data should be sent** 部分。有关其设置的详细说明,请参阅[集成设置](configuration#integration-settings)。
10. 点击 **Save** 完成设置。
Adapty 将向 Tenjin 发送购买事件并接收归因数据。你可以在 **Events names** 部分调整事件共享设置。
### 配置事件和标签 \{#configure-events-and-tags\}
Tenjin 仅接受购买事件和 **Trial started** 事件。在 **Events names** 部分,选择要与 Tenjin 共享的事件,以符合您的追踪目标。
### 将您的应用连接到 Tenjin \{#connect-your-app-to-tenjin\}
使用 `Adapty.updateAttribution()` SDK 方法从 Tenjin 获取归因数据,并将其传递给 Adapty。
2. 打开 **Amplitude integration** 开关以启用该集成。
3. 填写集成字段:
| 字段 | 说明 |
| ------------------------------------------ | ------------------------------------------------------------ |
| **Amplitude iOS/ Android/ Stripe API key** | 将 iOS/ Android/ Stripe 对应的 Amplitude **API Key** 填入 Adapty。可在 Amplitude 的 **Project settings** 中找到。如需帮助,请查阅 [Amplitude 文档](https://amplitude.com/docs/apis/authentication)。建议先使用 **Sandbox** 密钥进行测试,测试通过后再切换为 **Production** 密钥。 |
4. 可选设置,用于进一步自定义:
| 参数 | 说明 |
| --------------------------------------- | ------------------------------------------------------------ |
| **How the revenue data should be sent** | 选择发送含税含佣金的总收入,还是扣除税费和佣金后的净收入。详情请参阅[商店佣金与税费](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue)。 |
| **Exclude historical events** | 选择是否排除 Adapty SDK 安装前的事件,以防止数据重复。例如,如果用户在 1 月 10 日订阅,但在 3 月 6 日才安装 Adapty SDK,则 Adapty 仅会发送 3 月 6 日起的事件。 |
| **Send User Attributes** | 选择此选项以发送用户特定属性,例如语言偏好。 |
| **Always populate user_id** | Adapty 会自动将 `device_id` 作为 `amplitudeDeviceId` 发送。对于 `user_id`,此设置定义以下行为:
我们建议使用 Adapty 提供的默认事件名称。当然,你也可以根据需要修改事件名称。Adapty 将通过服务器到服务器的集成方式向 Amplitude 发送订阅事件,让你可以在 Amplitude 看板中查看所有订阅事件。
### SDK 配置 \{#sdk-configuration\}
使用 `setIntegrationIdentifier()` 方法设置 `amplitude_device_id` 参数,这是设置集成的必要步骤。
如果你有用户注册流程,也可以同时传入 `amplitude_user_id`。
:::note
第三方 SDK 会异步生成用户 ID,在 `Adapty.activate()` 执行时该 ID 可能尚未就绪。如果你的 **Customer User ID** 来自此类 SDK,请先不带该 ID 调用 `Adapty.activate()`。待 ID 到位后,依次调用 `setIntegrationIdentifier()`,再用 CUID 调用 `identify()`。
:::
4. 在 Adapty 看板中前往 [Integrations > AppMetrica](https://app.adapty.io/integrations/appmetrica)
5. 粘贴您的 AppMetrica 凭据。
### 事件与标签 \{#events-and-tags\}
Adapty 允许您向 AppMetrica 发送三组事件。您可以启用需要追踪的事件来监控应用表现。有关可用事件的完整列表,请参阅我们的[事件文档](events)。
:::note
AppMetrica 每 4 小时同步一次事件,因此事件出现在您的看板中可能会有延迟。
:::
:::tip
我们建议使用 Adapty 的默认事件名称以保持一致性,但您也可以自定义事件名称以匹配您现有的分析设置。
:::
### 收入设置 \{#revenue-settings\}
默认情况下,Adapty 将收入数据作为事件属性发送,这些数据会显示在 AppMetrica 的 Events 报告中。您可以配置收入数据的计算和显示方式:
- **Revenue calculation**(收入计算):选择收入值的计算方式,以符合您的财务报告需求:
- **Gross revenue**(总收入):显示扣除任何费用前的总收入,便于追踪客户支付的全额金额
- **Proceeds after store commission**(扣除应用商店佣金后的收入):显示扣除 App Store/Play Store 费用后的收入,帮助您追踪实际收益
- **Proceeds after store commission and taxes**(扣除应用商店佣金和税费后的收入):显示同时扣除商店费用和适用税费后的净收入,提供最准确的收益情况
- **Report user's currency**(报告用户货币):启用后,销售额将以用户本地货币报告,便于按地区分析收入。禁用后,所有销售额将转换为美元,以便在不同市场间进行一致的报告。
- **Send revenue events**(发送收入事件):启用此选项后,收入数据不仅会出现在 Events 报告中,还会出现在 AppMetrica 的[应用内及广告收入](https://appmetrica.yandex.com/docs/en/mobile-reports/revenue-report)报告中。请确保您没有从其他地方发送收入数据,否则可能导致数据重复。
- **Exclude historical events**(排除历史事件):启用后,Adapty 不会发送用户在安装带有 Adapty SDK 的应用之前发生的事件。如果您在集成 Adapty 之前已向分析工具发送事件,此选项有助于避免数据重复。
### SDK 配置 \{#sdk-configuration\}
要在应用中启用 AppMetrica 集成,您需要设置两个标识符:
1. `appmetrica_device_id`:基础集成所必需
2. `appmetrica_profile_id`:可选,但如果您的应用有用户注册功能则推荐设置
使用 `setIntegrationIdentifier()` 方法来设置这些值。以下是各平台的实现方式:
:::note
第三方 SDK 会异步生成用户 ID,在 `Adapty.activate()` 执行时该 ID 可能尚未就绪。如果你的 **Customer User ID** 来自此类 SDK,请先不带该 ID 调用 `Adapty.activate()`。待 ID 到位后,依次调用 `setIntegrationIdentifier()`,再用 CUID 调用 `identify()`。
:::
### 查找 Mixpanel Token \{#finding-your-mixpanel-token\}
获取 **Mixpanel Token** 的步骤:
1. 登录 [Mixpanel 看板](https://mixpanel.com/settings/project/)。
2. 打开 **Settings**,选择 **Organization Settings**。
3. 在左侧边栏中,进入 **Projects** 并选择你的项目。
## 集成工作原理 \{#how-the-integration-works\}
Adapty 会自动将相关事件属性(如用户 ID 和收入)映射到 [Mixpanel 原生属性](https://docs.mixpanel.com/docs/data-structure/user-profiles),确保订阅相关事件的追踪和报告准确无误。
此外,Adapty 会按用户累积收入数据,并更新其[用户画像属性](https://docs.mixpanel.com/docs/data-structure/user-profiles),包括 `subscription state` 和 `subscription product ID`。一旦收到事件,Mixpanel 将实时更新对应字段。
## 事件与标签 \{#events-and-tags\}
在凭据下方,有三组事件可以从 Adapty 发送到 Mixpanel。直接开启你需要的事件即可。点击[此处](events)查看 Adapty 提供的完整事件列表。
我们建议使用 Adapty 提供的默认事件名称。但您也可以根据需要修改事件名称。
## SDK 配置 \{#sdk-configuration\}
使用 `.setIntegrationIdentifier()` 方法设置 `mixpanelUserId`。如果未设置,Adapty 将使用您的用户 ID(`customerUserId`),若该值为 null,则使用 Adapty ID。请确保您在应用中向 Mixpanel 发送数据所用的用户 ID 与发送给 Adapty 的一致。
:::note
第三方 SDK 会异步生成用户 ID,在 `Adapty.activate()` 执行时该 ID 可能尚未就绪。如果你的 **Customer User ID** 来自此类 SDK,请先不带该 ID 调用 `Adapty.activate()`。待 ID 到位后,依次调用 `setIntegrationIdentifier()`,再用 CUID 调用 `identify()`。
:::
2. 登录 [PostHog 看板](https://posthog.com/)。
3. 导航至 **Settings -> Project**。
4. 在 **Project** 窗口中,向下滚动至 **Project ID** 部分,复制 **Project API key**。
5. 将 API key 粘贴到 Adapty 看板中的 **Project API key** 字段。PostHog 的服务端集成不支持专用的沙盒模式。
6. 选择您的 **PostHog Deployment**:
| 选项 | 描述 |
| ------ | ------------------------------------------------------------ |
| us/eu | 默认的 PostHog 托管部署。 |
| Custom | 适用于自托管实例。请在 **PostHog Instance URL** 字段中输入您的实例 URL。 |
7. (可选)如果您使用的是自托管的 PostHog 部署,请在 **PostHog Instance URL** 字段中输入您的部署地址。
8. (可选)调整 **Reporting Proceeds**、**Exclude Historical Events**、**Report User's Currency** 和 **Send Trial Price** 等设置。详情请参阅[集成设置](configuration#integration-settings)。
9. (可选)你还可以在 **Events names** 部分自定义发送至 PostHog 的事件。禁用不需要的事件或根据需要重命名它们。
10. 点击 **Save** 完成配置。
## SDK 配置 \{#sdk-configuration\}
要启用从 PostHog 接收归因数据,请按如下方式将 `distinctId` 值传递给 Adapty:
:::note
第三方 SDK 会异步生成用户 ID,在 `Adapty.activate()` 执行时该 ID 可能尚未就绪。如果你的 **Customer User ID** 来自此类 SDK,请先不带该 ID 调用 `Adapty.activate()`。待 ID 到位后,依次调用 `setIntegrationIdentifier()`,再用 CUID 调用 `identify()`。
:::
打开你的 SplitMetrics Acquire 账户,将鼠标悬停在某个 MMP 图标上,点击 **Settings** 按钮。在弹出的对话框中找到第 **5** 项下的 Client ID,复制后粘贴至 Adapty 的 **Client ID** 字段。
使用集成功能还需要设置 Apple App ID。要查找 App ID,请在 App Store Connect 中打开你的应用页面,进入 **General** 部分下的 **App Information page**,在屏幕左下角找到 **Apple ID**。
## 事件与标签 \{#events-and-tags\}
在凭证信息下方,有三组事件可从 Adapty 发送至 SplitMetrics Acquire,按需开启即可。完整的事件列表请参见[此处](events)。
建议使用 Adapty 提供的默认事件名称,当然你也可以根据需要自定义事件名称。Adapty 将通过服务端到服务端的集成方式向 SplitMetrics Acquire 发送订阅事件,你可以在 SplitMetrics 看板中查看所有订阅事件。
## SDK 配置 \{#sdk-configuration\}
SDK 端无需任何额外配置,但建议向 Adapty 传入 `customerUserId` 以提高数据准确性。
:::warning
请确保已在 Adapty 中配置 [Apple Search Ads](apple-search-ads) 并[上传凭证](https://app.adapty.io/settings/apple-search-ads),否则 SplitMetrics Acquire 将无法正常工作。
:::
## 故障排查 \{#troubleshooting\}
如果 SplitMetrics Acquire 集成在配置正确的情况下仍无法正常工作,请检查以下几点:
- 确保已在 [App Settings -> Apple Search Ads tab](https://app.adapty.io/settings/apple-search-ads) 中启用 **Receive Apple Search Ads attribution in Adapty** 开关,已在 Adapty 中配置 [Apple Search Ads](apple-search-ads),并已[上传凭证](https://app.adapty.io/settings/apple-search-ads),否则 SplitMetrics 将无法正常工作。
- 确认用户画像具有非自然流量的 ASA 归因数据。只有包含详细非自然流量 ASA 归因的用户画像才会将事件传递至 Adapty。
## SplitMetrics Acquire 事件结构 \{#splitmetrics-acquire-event-structure\}
Adapty 通过 GET 请求以查询参数的形式向 SplitMetrics Acquire 发送事件,每个事件的结构如下:
```json
{
"source": "Apple Search Ads",
"app_id": "123456789",
"name": "subscription_renewed",
"type": "subscription_renewed",
"revenue": 9.99,
"currency": "USD",
"tap_time": "2024-03-01 12:00:00",
"open_time": "2024-03-01 12:05:00",
"event_time": "2024-03-02 12:00:00",
"adaccount_id": "123456",
"campaign_id": "123456789",
"adgroup_id": "123456789",
"keyword_id": "123456789",
"creative_set_id": "123456789",
"Ad_id": "123456789",
"country_or_region": "US",
"conversion_type": "Download",
"user_id": "user_12345",
"att_status": "3",
"device_type": "iphone",
"app_version": "1.2.3",
"sdk_version": "2.10.0",
"ios_version": "17.2",
"event_value": "{\"vendor_product_id\":\"yearly.premium.6999\",\"original_transaction_id\":\"GPA.3383...\"}",
"event_id": "123e4567-e89b-12d3-a456-426614174000"
}
```
各字段说明:
| 参数 | 类型 | 说明 |
|:--------------------|:-------|:----------------------------------------------------------------------------------------------------------------|
| `source` | String | 固定值 "Apple Search Ads"。 |
| `app_id` | String | Apple App ID。 |
| `name` | String | 事件名称(由 Adapty 事件映射而来)。 |
| `type` | String | 事件类型(与 `name` 相同)。 |
| `revenue` | Float | 收入金额。 |
| `currency` | String | 货币代码。 |
| `tap_time` | String | 广告点击的日期和时间。 |
| `open_time` | String | 应用打开(安装)的日期和时间。 |
| `event_time` | String | 事件发生的日期和时间。 |
| `adaccount_id` | String | ASA 组织 ID。 |
| `campaign_id` | String | ASA 广告系列 ID。 |
| `adgroup_id` | String | ASA 广告组 ID。 |
| `keyword_id` | String | ASA 关键词 ID。 |
| `creative_set_id` | String | ASA 创意集 ID。 |
| `Ad_id` | String | ASA 广告 ID。 |
| `country_or_region` | String | 商店所在国家或地区。 |
| `conversion_type` | String | 转化类型(例如 "Download")。 |
| `user_id` | String | Customer User ID 或 Adapty 用户画像 ID。 |
| `att_status` | String | 追踪使用状态(0-3)。 |
| `device_type` | String | 设备类型(例如 "iphone"、"ipad")。 |
| `app_version` | String | 应用版本号。 |
| `sdk_version` | String | Adapty SDK 版本号。 |
| `ios_version` | String | iOS 版本号。 |
| `event_value` | String | 包含所有可用[事件详情](webhook-event-types-and-fields#for-most-event-types)的 JSON 字符串。 |
| `event_id` | String | 唯一事件 ID(UUID)。 |
---
# File: braze
---
---
title: "Braze"
description: "将 Braze 与 Adapty 集成,实现无缝的客户互动和推送通知。"
---
作为顶级客户互动解决方案之一,[Braze](https://www.braze.com/) 提供了一整套推送通知、电子邮件、短信和应用内消息工具。通过将 Adapty 与 Braze 集成,您可以在一个地方轻松访问所有订阅事件,并能够根据这些事件触发自动化通信。
Adapty 提供完整的数据集,让您可以在一个地方追踪来自所有应用商店的[订阅事件](events),并可用于更新 Braze 中的用户画像。借助 Adapty,您可以轻松了解订阅用户的行为,掌握他们的偏好,并利用这些信息以精准有效的方式与他们沟通。因此,该集成允许您在 Braze 看板中追踪订阅事件,并将其与您的[获客活动](https://www.braze.com/product/journey-orchestration)相关联。
Adapty 将订阅事件、用户属性和购买信息发送至 Braze,让您可以在完成简短、便捷的集成后,通过 Braze 推送通知与客户建立定向沟通。
## 如何设置 Braze 集成 \{#how-to-set-up-braze-integration\}
要集成 Braze,请前往 [Integrations -> Braze](https://app.adapty.io/integrations/braze),打开开关并填写相关字段。
集成过程的第一步是提供必要的凭据,以建立您的 Braze 与 Adapty 用户画像之间的连接。集成正常运行需要 **REST API Key**、您的 **Braze Instance ID** 以及 iOS 和 Android 的 **App IDs**:
1. **REST API Key** 可在 **Braze Dashboard** → **Settings** → **API Keys** 中创建。创建时请确保您的密钥具有 `users.track` 权限:
2. 要获取 **Braze Instance ID**,请记录您的 Braze 看板 URL,然后前往 [Braze 文档](https://www.braze.com/docs/api/basics/#endpoints)中指定实例 ID 的部分。它应具有区域格式,例如 US-03、EU-01 等。
3. iOS 和 Android App IDs 同样可在 Braze Dashboard → Settings → API Keys 中找到。从此处复制它们:
## 事件、用户属性和购买 \{#events-user-attributes-and-purchases\}
在凭据下方,有三组事件可以从 Adapty 发送到 Braze。只需打开您需要的事件即可。您也可以根据需要更改发送到 Braze 的事件名称。查看 Adapty 提供的完整事件列表,请点击[这里](events):
Adapty 将通过服务器对服务器集成的方式,将订阅事件和用户属性发送至 Braze,让您可以在 Braze 看板中查看这些信息,并据此配置营销活动。
对于包含收入的事件(例如试用转化和续订),Adapty 将以购买的形式将此信息发送至 Braze。
[这里](messaging#event-properties)是发送至 Braze 的事件属性的完整规格说明。
:::note
实用的用户属性
Adapty 默认会为 Braze 集成发送一些用户属性。您可以参考以下列表,确定哪些属性最适合您的需求。
:::
| 用户属性 | 类型 | 值 |
|--------------|----|-----|
| `adapty_customer_user_id` | String | 包含客户定义的用户唯一标识符的值。可在 Adapty [看板](profiles-crm)和 Braze 中找到。 |
| `adapty_profile_id` | String | 包含 Adapty 用户画像 ID 的唯一标识符值,可在 Adapty [看板](profiles-crm)中找到。 |
| `environment` | String | 指示用户是在沙盒环境还是生产环境中操作。
值为 `Sandbox` 或 `Production`
| | `store` | String |包含用于完成购买的商店名称。
可能的值:
`app_store` 或 `play_store`。
| | `vendor_product_id` | String |包含 Apple/Google 商店中产品 ID 的值。
例如:org.locals.12345
| | `subscription_expires_at` | String |包含最新订阅的到期日期。
值格式为:
YYYY-MM-DDTHH:mm:ss.SSS+TZ
例如:2023-02-15T17:22:03.000+0000
| | `active_subscription` | String | 在任何购买/续订事件时该值将设置为 `true`,若订阅已到期则设置为 `false`。 | | `period_type` | String |指示购买或续订的最新周期类型。
可能的值为
试用期为 `trial`,其余为 `normal`。
| 所有浮点数值将四舍五入为整数,字符串保持不变。 除了预定义的标签列表外,还可以使用标签发送[自定义属性](segments#custom-attributes)。这为标签中包含的数据类型提供了更大的灵活性,对于追踪与产品或服务相关的特定信息非常有用。如果用户在[集成页面](https://app.adapty.io/integrations/braze)勾选了 **Send user attributes** 复选框,所有自定义用户属性将自动发送至 Braze。 ## SDK 配置 \{#sdk-configuration\} 要在 Adapty 和 Braze 中关联用户画像,您需要使用与 Adapty 相同的客户用户 ID 配置 Braze SDK,或使用其 `.changeUser()` 方法:
2. 启用集成开关。
3. 输入您的 **OneSignal App ID**。
要设置与 OneSignal 的集成,请前往 Adapty 看板中的 [Integrations -> OneSignal](https://app.adapty.io/integrations/onesignal),开启开关并配置集成凭据。
## 获取您的 OneSignal App ID \{#retrieving-your-onesignal-app-id\}
在您的 [OneSignal 看板](https://dashboard.onesignal.com/login)中找到 **OneSignal App ID**:
1. 导航至 **Settings** → **Keys & IDs**。
2. 复制您的 **OneSignal App ID** 并将其粘贴到 Adapty 看板中的 **App ID** 字段。
您可以在[以下文档](https://documentation.onesignal.com/docs/en/keys-and-ids)中找到有关 OneSignal ID 的更多信息。
### 配置事件 \{#configuring-events\}
Adapty 允许您向 OneSignal 发送三组事件。在 Adapty 看板中开启您需要的事件。您可以在[此处](events)查看所有可用事件的完整列表及详细说明。
Adapty 通过服务器到服务器集成将订阅事件发送到 OneSignal,让您能够在 OneSignal 中追踪所有与订阅相关的活动。
:::warning
从 2023 年 4 月 17 日起,OneSignal 的免费套餐不再支持此集成。该功能仅适用于 **Growth**、**Professional** 及更高级别的套餐。详情请参阅 [OneSignal 定价](https://onesignal.com/pricing)。
:::
## 自定义标签 \{#custom-tags\}
此集成会将各种属性作为标签更新并分配给您的 Adapty 用户,然后将其发送到 OneSignal。请参阅以下标签列表,找到最适合您需求的标签。
:::warning
OneSignal 对标签数量有限制。这包括 Adapty 生成的标签和 OneSignal 中已有的所有标签。超出限制可能会在发送事件时导致错误。
:::
| 标签 | 类型 | 说明 |
|---|----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `adapty_customer_user_id` | String | 用户在您应用中的唯一标识符。必须在您的系统、Adapty 和 OneSignal 中保持一致。 |
| `adapty_profile_id` | String | Adapty 用户画像 ID,可在您的 [Adapty 看板](profiles-crm)中找到。 |
| `environment` | String | `Sandbox` 或 `Production`,表示用户当前所处的环境。 |
| `store` | String | 购买产品的商店。选项:**app_store**、**play_store**、**stripe**,或您的[自定义商店](custom-store)名称。 |
| `vendor_product_id` | String | 应用商店中的产品 ID(例如 `org.locals.12345`)。 |
| `subscription_expires_at` | String | 最新订阅的到期日期(`YYYY-MM-DDTHH:MM:SS+0000`,例如 `2023-02-10T17:22:03.000000+0000`)。 |
| `last_event_type` | String | 来自 [Adapty 事件列表](events)的最新事件类型。
1. **App ID** 可在您的 Pushwoosh 看板中找到。
2. **Auth token** 可在 Pushwoosh 设置的 API Access 部分找到。
## 事件与标签 \{#events-and-tags\}
在凭据下方,有三组事件可从 Adapty 发送到 Pushwoosh。只需开启您需要的事件即可。您也可以根据需要更改发送到 Pushwoosh 的事件名称。在[此处](events)查看 Adapty 提供的完整事件列表。
Adapty 将通过服务器到服务器的集成向 Pushwoosh 发送订阅事件,使您能够在 Pushwoosh 看板中查看所有订阅事件。
:::note
自定义标签
使用 Adapty,您还可以为 Pushwoosh 集成使用自定义标签。您可以参考下方提供的标签列表,确定最适合您需求的标签。
:::
| 标签 | 类型 | 值 |
|---|----|-----|
| `adapty_customer_user_id` | String | 包含用户唯一标识符的值,可在 Pushwoosh 端找到。 |
| `adapty_profile_id` | String | 包含用户的 Adapty 用户画像 ID 唯一标识符的值,可在您的 Adapty [看板](profiles-crm)中找到。 |
| `environment` | String | 指示用户是在沙盒环境还是生产环境中运行。
值为 `Sandbox` 或 `Production`
| | `store` | String |包含用于购买的商店名称。
可能的值:
`app_store` 或 `play_store`。
| | `vendor_product_id` | String |包含 Apple/Google 商店中产品 ID 的值。
例如:org.locals.12345
| | `subscription_expires_at` | String |包含最新订阅的到期日期。
值格式为:
年-月-日T时:分:秒
例如:2023-02-10T17:22:03.000000+0000
| | `last_event_type` | String | 指示您为集成启用的标准 [Adapty 事件](events)列表中最后接收到的事件类型。 | | `purchase_date` | String |包含最后一次交易(原始购买或续订)的日期。
值格式为:
年-月-日T时:分:秒
例如:2023-02-10T17:22:03.000000+0000
| | `original_purchase_date` | String |包含根据交易记录的首次购买日期。
值格式为:
年-月-日T时:分:秒
例如:2023-02-10T17:22:03.000000+0000
| | `active_subscription` | String | 在任何购买/续订事件时该值将设置为 `true`,如果订阅已过期则设置为 `false`。 | | `period_type` | String |指示购买或续订的最新周期类型。
可能的值为:
`trial` 表示试用期,`normal` 表示其他情况。
| 所有浮点值将被四舍五入为整数。字符串保持不变。 除了预定义的标签列表外,还可以使用标签发送[自定义属性](segments#custom-attributes)。这为标签中包含的数据类型提供了更大的灵活性,可用于追踪与产品或服务相关的特定信息。如果用户在[集成页面](https://app.adapty.io/integrations/pushwoosh)勾选了 **Send user custom attributes** 复选框,所有自定义用户属性将自动发送到 Pushwoosh。 ## SDK 配置 \{#sdk-configuration\} 要将 Adapty 与 Pushwoosh 关联,您需要向我们发送 `HWID` 值:
2. 为其命名(例如 `Adapty`)并将其添加到您的工作区:
### 2\. 授予发帖权限并获取应用 Token \{#2-give-permission-to-post-and-get-a-token-for-your-app\}
您将被重定向到 Slack 中您的应用页面。
1. 向下滚动并点击 **Permissions**:
2. 重定向后,向下滚动至 **Scopes** 并点击 **Add an OAuth Scope**:
3. 授予 `chat:write`、`chat:write.public` 和 `chat:write.customize` 权限。这些权限用于在您的频道中发布消息并自定义消息内容:
4. 滚动回页面顶部,点击 **Install to Workspace**:
5. 点击 **Allow**:
完成后,您将被重定向到同一页面,但此时将显示可用的 OAuth Token(`xoxb-...`)。这正是完成设置所需的内容:
### 3\. 在 Adapty 中配置集成 \{#3-configure-the-integration-in-adapty\}
1. 前往 [**Integrations** → **Slack**](https://app.adapty.io/integrations/slack):
2. 粘贴上一步中的 `xoxb-...` Token,并选择应用将发帖的频道。您可以设置集成仅接收生产环境、沙盒环境或两者的事件。您还可以选择发帖时使用的货币(原始货币或转换为美元)。
:::note
请注意,如果您希望 Adapty 在私有频道中发布消息,您需要手动将您在 Slack 中创建的 `Adapty` 应用添加到该频道,否则将无法正常工作。
:::
3. 最后,您可以在 **Events** 下选择希望接收的事件:
设置完成!
事件将被发送到您指定的频道。您可以在适用情况下查看收入,并在 Adapty 中查看客户的用户画像:
---
# File: s3-exports
---
---
title: "Amazon S3"
description: "将订阅数据导出到 S3,用于高级分析和报告。"
---
Adapty 与 Amazon S3 的集成允许你将事件和付费墙访问数据安全地存储在一个集中位置。你可以将[订阅事件](events)以 .csv 文件的形式保存到 Amazon S3 存储桶中。
要设置此集成,你需要在 AWS 控制台和 Adapty 看板中按照几个简单的步骤进行操作。
:::note
计划
Adapty 每 **24h** 在 UTC 时间 4:00 发送一次数据。
每个文件将包含整个上一个日历日(UTC 时间)内所创建事件的数据。例如,北京时间 3 月 8 日 4:00 UTC 自动导出的数据,将包含 3 月 7 日 00:00:00 至 23:59:59(UTC)期间创建的所有事件。
:::
## 如何配置 Amazon S3 集成 \{#how-to-set-up-amazon-s3-integration\}
要开始接收数据,您需要以下凭证:
1. Access key ID
2. Secret access key
3. S3 bucket name
4. S3 存储桶内的文件夹名称
:::note
嵌套目录
您可以在 Amazon S3 bucket name 字段中指定嵌套目录,例如:adapty-events/com.sample-app
:::
要集成 Amazon S3,请前往 [**Integrations** -> **Amazon S3**](https://app.adapty.io/integrations/s3),将开关从关闭切换为开启,并填写相关字段。
首先,设置凭证以建立 Amazon S3 与 Adapty 用户画像之间的连接。
在 Adapty 看板中,需要填写以下字段来完成连接配置:
| 字段 | 描述 |
| :--------------------------- | :----------------------------------------------------------- |
| **Access Key ID** | 用于验证用户或应用程序访问 AWS 服务的唯一标识符。请在下载的 [csv 文件](s3-exports#how-to-create-amazon-s3-credentials) 中查找此 ID。 |
| **Secret Access Key** | 与 Access Key ID 配合使用,用于验证用户或应用程序访问 AWS 服务的私钥。请在下载的 [csv 文件](s3-exports#how-to-create-amazon-s3-credentials) 中查找此密钥。 |
| **S3 Bucket Name** | 用于在 AWS 云中标识特定 S3 存储桶的全局唯一名称。S3 存储桶是一种简单的存储服务,允许用户在云中存储和检索文件、图片等数据对象。 |
| **Folder Inside the Bucker** | 您希望在所选 S3 存储桶中创建的文件夹名称。请注意,S3 通过对象键前缀来模拟文件夹,这些前缀本质上就是文件夹名称。 |
## 如何创建 Amazon S3 凭证 \{#how-to-create-amazon-s3-credentials\}
本指南将帮助您在 AWS 控制台中创建必要的凭证。
### 1\. 创建访问策略 \{#1-create-access-policy\}
首先,前往 AWS 控制台中的 [IAM Policy Dashboard](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/policies),然后选择 **Create Policy** 选项。
在 Policy 编辑器中,粘贴以下 JSON,并将 `adapty-s3-integration-test` 替换为你的存储桶名称:
```json showLineNumbers title="Json"
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListObjectsInBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::adapty-s3-integration-test"
},
{
"Sid": "AllowAllObjectActions",
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": [
"arn:aws:s3:::adapty-s3-integration-test/*",
"arn:aws:s3:::adapty-s3-integration-test"
]
},
{
"Sid": "AllowBucketLocation",
"Effect": "Allow",
"Action": "s3:GetBucketLocation",
"Resource": "arn:aws:s3:::adapty-s3-integration-test"
}
]
}
```
完成策略配置后,您可以选择添加标签(可选),然后点击 **Next** 进入最后一步。在此步骤中,为您的策略命名,然后点击 **Create policy** 按钮即可完成创建。
### 2\. 创建 IAM 用户 \{#2-create-iam-user\}
要让 Adapty 能够将原始数据报告上传至您的存储桶,您需要为其提供一个具有该存储桶写入权限的用户的 Access Key ID 和 Secret Access Key。
请前往 IAM 控制台,选择 [Users 部分](https://console.aws.amazon.com/iamv2/home#/users),然后点击 **Add users** 按钮。
为用户命名,选择 **Access key – Programmatic access**,然后继续进行权限设置。
下一步,请选择 **Add user to group** 选项,然后点击 **Create group** 按钮。
接下来,你需要为用户组命名,并选择之前创建的策略。选择策略后,点击 **Create group** 按钮完成操作。
成功创建群组后,请**选择它**并继续下一步。
这是本节的最后一步,直接点击 **Create User** 按钮即可。
最后,您可以选择**以 .csv 格式下载凭据**,或者直接从看板中复制并粘贴凭据。
## 手动数据导出 \{#manual-data-export\}
除了自动将事件数据导出到 Amazon S3 之外,Adapty 还提供了手动文件导出功能。通过此功能,你可以选择特定的时间范围来导出事件数据,并手动将其导出到你的 S3 存储桶。这让你能够更灵活地控制导出的数据内容和导出时机。
指定的日期范围将用于导出从日期 A 00:00:00 UTC 到日期 B 23:59:59 UTC 期间创建的事件。
## 表格结构 \{#table-structure\}
在 AWS S3 集成中,Adapty 提供了一张表来存储交易事件和付费墙访问的历史数据。该表包含用户画像、收入与净收益、来源商店等多项数据。这些表本质上记录了应用在特定时间段内产生的所有交易。
:::warning
请注意,此结构可能会随时间增长——我们或与我们合作的第三方可能会引入新数据。请确保处理该结构的代码足够健壮,依赖于特定字段,而不是整体结构。
:::
以下是事件的表结构:
:::note
Adapty 使用 [currencylayer.com](https://currencylayer.com/) 的汇率(每 8 小时更新一次)将其他货币换算为美元。汇率**在交易发生时锁定**——后续汇率变动不会影响已换算的结果。
:::
| 列名 | 描述 |
|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **profile_id** | Adapty 用户 ID。 |
| **event_type** | 小写的事件名称。请参阅[事件](events)章节了解事件类型。 |
| **event_datetime** | ISO 8601 日期。 |
| **transaction_id** | 交易(如购买或续订)的唯一标识符。 |
| **original_transaction_id** | 原始购买的交易标识符。 |
| **subscription_expires_at** | 订阅的到期日期,通常为未来时间。 |
| **environment** | 可为沙盒或生产环境。 |
| **revenue_usd** | 以美元计的收入,可为空。 |
| **proceeds_usd** | 以美元计的收益,可为空。 |
| **net_revenue_usd** | 以美元计的净收入(税后收入),可为空。 |
| **tax_amount_usd** | 以美元计的税款扣除金额,可为空。 |
| **revenue_local** | 以本地货币计的收入,可为空。 |
| **proceeds_local** | 以本地货币计的收益,可为空。 |
| **net_revenue_local** | 以本地货币计的净收入(税后收入),可为空。 |
| **tax_amount_local** | 以本地货币计的税款扣除金额,可为空。 |
| **customer_user_id** | 开发者用户 ID,例如可以是用户的 UUID、邮箱或其他任意 ID。如未设置则为 Null。 |
| **store** | 可为 _app_store_ 或 _play_store_。 |
| **product_id** | Apple App Store、Google Play Store 或 Stripe 中的产品 ID。 |
| **base_plan_id** | Google Play Store 中的[基础方案 ID](https://support.google.com/googleplay/android-developer/answer/12154973) 或 Stripe 中的[价格 ID](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices)。 |
| **developer_id** | 交易来源付费墙的开发者(SDK)ID。 |
| **ab_test_name** | 交易来源 A/B 测试的名称。 |
| **ab_test_revision** | 交易来源 A/B 测试的版本号。 |
| **paywall_name** | 交易来源付费墙的名称。 |
| **paywall_revision** | 交易来源付费墙的版本号。 |
| **profile_county** | Adapty 根据 IP 地址确定的用户画像所在国家。 |
| **install_date** | 安装发生时的 ISO 8601 日期。 |
| **idfv** | iOS 设备上的 [identifierForVendor](https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor)。 |
| **idfa** | iOS 设备上的 [advertisingIdentifier](https://developer.apple.com/documentation/adsupport/asidentifiermanager/advertisingidentifier)。 |
| **advertising_id** | Android 操作系统分配的唯一代码,广告商可用其唯一标识用户设备。 |
| **ip_address** | 设备 IP(可为 IPv4 或 IPv6,优先使用 IPv4)。每次设备 IP 变更时更新。 |
| **cancellation_reason** | 用户取消订阅的原因。
可为:
**iOS & Android** _voluntarily_cancelled_, _billing_error_, _refund_
**iOS** _price_increase_, _product_was_not_available_, _unknown_, _upgraded_
**Android** _new_subscription_replace_, _cancelled_by_developer_
| | **android_app_set_id** | [AppSetId](https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/appsetid/AppSetId) — 每台设备、每个开发者账号唯一且可由用户重置的 ID,用于非变现广告场景。 | | **android_id** | 在 Android 8.0(API 级别 26)及更高版本中,该值为 64 位数字(以十六进制字符串表示),对应用签名密钥、用户和设备的每种组合唯一。详情请参阅 [Android 开发者文档](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID)。 | | **device** | 面向终端用户显示的设备型号名称。 | | **currency** | 交易的三字母货币代码(ISO-4217)。 | | **store_country** | 由 Apple/Google 应用商店确定的用户画像所在国家。 | | **attribution_source** | 归因来源。 | | **attribution_network_user_id** | 归因来源分配给用户的 ID。 | | **attribution_status** | 可为 organic、non_organic 或 unknown。 | | **attribution_channel** | 营销渠道名称。 | | **attribution_campaign** | 营销活动名称。 | | **attribution_ad_group** | 归因广告组。 | | **attribution_ad_set** | 归因广告集。 | | **attribution_creative** | 归因创意关键词。 | | **attributes** | [自定义用户属性](setting-user-attributes#custom-user-attributes)的 JSON 数据,包含你在移动应用中配置发送的所有自定义属性。如需发送,请在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面启用 **Send User Attributes** 选项。 | | **integration_ids** | 与用户画像关联的所有集成 ID,为字典格式。示例:{'mixpanel_user_id': 'mixpanelUserId-test', 'facebook_anonymous_id': 'facebookAnonymousId-test'} | Here is the table structure for the paywall visits: | 列名 | 描述 | | :-------------------- | :----------------------------------------------------------------------------------------------------------- | | **profile_id** | Adapty 用户 ID。 | | **customer_user_id** | 开发者用户 ID。例如,可以是您的用户 UUID、邮箱或其他任意 ID。如未设置则为空。 | | **profile_country** | 由 Apple/Google 应用商店确定的用户画像所在国家/地区。 | | **install_date** | ISO 8601 格式的安装日期。 | | **store** | 值为 _app_store_ 或 _play_store_。 | | **paywall_showed_at** | 付费墙向用户展示的日期。 | | **developer_id** | 交易来源付费墙的开发者(SDK)ID。 | | **ab_test_name** | 交易来源 A/B 测试的名称。 | | **ab_test_revision** | 交易来源 A/B 测试的版本号。 | | **paywall_name** | 交易来源付费墙的名称。 | | **paywall_revision** | 交易来源付费墙的版本号。 | ## 事件与标签 \{#events-and-tags\} 您可以管理集成所传递的数据。该集成提供以下配置选项: | 设置 | 描述 | | :--------------------------------- | :----------------------------------------------------------- | | **Exclude Historical Events** | 选择排除用户在安装含有 Adapty SDK 的应用之前发生的事件。这可以防止事件重复,确保报告准确。例如,如果某用户在 1 月 10 日激活了月度订阅,并在 3 月 6 日更新了包含 Adapty SDK 的应用,则 Adapty 将忽略 3 月 6 日之前的事件,并保留此后的事件。 | | **Include events without profile** | 选择包含未关联到 Adapty 用户画像的交易。这些交易可能包括在安装 Adapty SDK 之前发生的购买,或从应用商店服务器通知中收到的、暂时无法关联到特定用户的交易。 | | **Send User Attributes** | 如果您希望发送用户特定属性(例如语言偏好),且您的 OneSignal 套餐支持超过 10 个标签,请选择此选项。启用后,可在默认 10 个标签之外包含额外信息。请注意,超出标签限制可能会导致错误。 |
在集成设置下方,有三组事件可供您从 Adapty 导出、发送并存储到 Amazon S3。只需开启您需要的事件即可。点击[此处](events)查看 Adapty 提供的完整事件列表。
---
# File: google-cloud-storage
---
---
title: "Google Cloud Storage"
description: "将 Google Cloud Storage 与 Adapty 集成,实现安全的数据存储。"
---
启用 Google Cloud Storage 集成,将[订阅事件](events)和[付费墙访问数据](paywall-metrics)安全存储在一个统一位置:你的 Google Cloud Storage 存储桶中。
每天 UTC 时间凌晨 4 点,Adapty 会将前一天的数据以 .csv 文件形式上传到您的存储桶。您可以选择接收**事件**数据、**付费墙访问**数据,或**两者都要**。您也可以随时[手动导出](#manual-data-export)任意时间段的数据。
要设置集成,请先在 Google Cloud 控制台中[生成存储桶访问密钥](#create-google-cloud-storage-credentials),然后[将其添加到 Adapty 设置中](#set-up-google-cloud-storage-integration)。
## 上传计划与时长 \{#upload-schedule-and-duration\}
Adapty 每 24 小时在 UTC 时间 04:00 向 Google Cloud Storage 上传数据。
文件包含在前一个日历日(UTC)内创建的事件数据。3 月 8 日上传的文件将包含 3 月 7 日 00:00:00 至 23:59:59 UTC 期间创建的所有事件。
该过程可能需要数小时,具体取决于队列中的文件总数以及您个人请求的数据量。如果 Adapty 在首次上传时包含历史数据,所需时间将长于后续的每日上传。
## 设置 Google Cloud 存储集成 \{#set-up-google-cloud-storage-integration\}
您需要一个具有**写入权限**的有效 Google Cloud 服务账号密钥。如需生成密钥,请按照[创建凭证](#create-google-cloud-storage-credentials)部分的步骤操作。
:::warning
您可以为事件和付费墙访问分别使用不同的存储桶和凭证。但是,如果**任意一方**的凭证无效,[**两项上传均会失败**](#troubleshooting)。
:::
前往 [**Integrations** -> **Google Cloud Storage**](https://app.adapty.io/integrations/google-cloud-storage),打开所需的标签页(**Events** 或 **Paywall visits**),然后启用该集成。
上传包含 **Google Cloud service account key** 的文件,指定目标 **bucket** 和 **folder**,保存更改。
### 事件数据的可选设置 \{#optional-settings-for-event-data\}
你可以指定要包含在报告中的事件,并为事件设置自定义名称。完整的可用事件列表请参阅 [events](events) 文章。
| 名称 | 默认值 | 描述 |
| ------------------------------ | ----------------- | ----------- |
| Exclude historical events | true | 排除在您将 Adapty SDK 集成到应用之前发生的事件的相关信息。某用户于 1 月 10 日购买了月度订阅。您的应用在 3 月 1 日发布的更新中首次集成了 Adapty SDK。
如果此设置**开启**,报告将不包含 1 月份的"订阅开始"事件,也不包含 2 月份的"订阅续费"事件。但**会**包含 3 月 10 日的"订阅续费"事件。
用户取消订阅的原因。
可能的值:
**iOS & Android** — *voluntarily_cancelled*、*billing_error*、*refund*
**仅 iOS** — *price_increase*、*product_was_not_available*、*unknown*、*upgraded*
**仅 Android** — *new_subscription_replace*、*cancelled_by_developer*
| | **android_app_set_id** | [AppSetId](https://developer.android.com/design-for-safety/privacy-sandbox/reference/adservices/appsetid/AppSetId) — 每台设备、每个开发者账号唯一、用户可重置的 ID,用于非变现广告场景。 | | **android_id** | 在 Android 8.0(API level 26)及更高版本上,该字段为一个 64 位数字(以十六进制字符串表示),对应用签名密钥、用户和设备的每种组合唯一。详情请参阅 [Android 开发者文档](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID)。 | | **device** | 对终端用户可见的设备型号名称。 | | **currency** | 交易的三字母货币代码(ISO-4217)。 | | **store_country** | Apple/Google 应用商店判断的用户画像所在国家/地区。 | | **attribution_source** | 归因来源。 | | **attribution_network_user_id** | 归因来源分配给用户的 ID。 | | **attribution_status** | 可为 organic、non_organic 或 unknown。 | | **attribution_channel** | 营销渠道名称。 | | **attribution_campaign** | 营销活动名称。 | | **attribution_ad_group** | 归因广告组。 | | **attribution_ad_set** | 归因广告集。 | | **attribution_creative** | 归因创意关键词。 | | **attributes** | [自定义用户属性](setting-user-attributes#custom-user-attributes)的 JSON 数据,包含你在移动端应用中设置并发送的所有自定义属性。如需发送,请在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面中启用 **Send User Attributes** 选项。 | | **integration_ids** | 与用户画像关联的所有集成 ID,以字典形式呈现。示例:{'mixpanel_user_id': 'mixpanelUserId-test', 'facebook_anonymous_id': 'facebookAnonymousId-test'} | ### 付费墙访问次数 \{#paywall-visits\} | 字段 | 描述 | | :-------------------- | :----------------------------------------------------------------------------------------------------------- | | **profile_id** | Adapty 用户 ID。 | | **customer_user_id** | 开发者用户 ID。例如,可以是您的用户 UUID、邮箱或其他任意 ID。如果未设置则为 Null。 | | **profile_country** | 由 Apple/Google 应用商店确定的用户画像所在国家/地区。 | | **install_date** | ISO 8601 格式的安装日期。 | | **store** | 可为 *app_store* 或 *play_store*。 | | **paywall_showed_at** | 付费墙向用户展示的日期。 | | **developer_id** | 交易来源付费墙的开发者(SDK)ID。 | | **ab_test_name** | 交易来源 A/B 测试的名称。 | | **ab_test_revision** | 交易来源 A/B 测试的修订版本号。 | | **paywall_name** | 交易来源付费墙的名称。 | | **paywall_revision** | 交易来源付费墙的修订版本号。 | ## 故障排查 \{#troubleshooting\} Adapty 在开始上传**之前**会检查您的访问密钥的有效性。即使只有一个 Google Cloud Storage 密钥无效,Adapty 也会**中止上传**并抛出错误。 为确保上传不中断,请在密钥过期之前替换它们。如果您更新了**事件**的密钥,请不要忘记同时更新**付费墙访问**的密钥,反之亦然。 --- # File: webhook-event-types-and-fields --- --- title: "Webhook 事件类型与字段" description: "" --- Adapty 会在订阅事件发生时发送 webhook。本节介绍这些事件类型及每个 webhook 包含的数据字段。 ## Webhook 事件类型 \{#webhook-event-types\} 您可以将所有事件类型发送到 Webhook,也可以只选择其中部分类型。您可以参考我们的[事件流程](event-flows),了解预期接收的数据格式以及如何围绕它构建业务逻辑。在[设置 Webhook 集成](set-up-webhook-integration#configure-webhook-integration-in-the-adapty-dashboard)时,您可以禁用不需要的事件类型,也可以在那里用自定义 ID 替换 Adapty 默认的事件 ID。 | 事件名称 | 描述 | |:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | subscription_started | 当用户激活没有试用期的付费订阅时触发,即立即扣款。 | | subscription_renewed | 订阅续费并成功扣款时发生。该事件从第二次计费开始记录,无论是试用订阅还是非试用订阅。 | | subscription_renewal_cancelled | 用户已关闭订阅自动续费。用户在付费订阅周期结束前仍可使用高级功能。 | | subscription_renewal_reactivated | 当用户重新激活订阅自动续费时触发。 | | subscription_expired | 当订阅取消后完全到期时触发。例如,用户在12月12日取消订阅,但订阅在12月31日到期,则该事件在12月31日记录。 | | subscription_paused | 当用户激活[订阅暂停](https://developer.android.com/google/play/billing/lifecycle/subscriptions#pause)时发生(仅限 Android)。 | | subscription_deferred | 当订阅购买被[延期](https://adapty.io/glossary/subscription-purchase-deferral/)时触发,允许用户延迟付款同时保留对高级功能的访问权限。此功能通过 Google Play Developer API 提供,可用于免费试用或帮助面临经济困难的用户。 | | non_subscription_purchase | 任何非订阅购买,例如永久授权或消耗型商品(如游戏内货币)。 | | trial_started | 当用户激活试用订阅时触发。 | | trial_converted | 当试用期结束并成功向用户扣款(首次购买)时发生。例如,用户的试用期至1月14日,但在1月7日被扣款,则该事件在1月7日记录。 | | trial_renewal_cancelled | 用户在试用期间关闭了订阅自动续费。用户在试用期结束前仍可使用高级功能,但不会被扣款或开始订阅。 | | trial_renewal_reactivated | 当用户在试用期间重新激活订阅自动续费时发生。 | | trial_expired | 当试用期结束且未转化为订阅时触发。 | | entered_grace_period | 当付款尝试失败且用户进入宽限期(如已启用)时发生。用户在此期间保留高级访问权限。 | | billing_issue_detected | 当扣款尝试中出现账单问题时触发(例如,卡余额不足)。 | | subscription_refunded | 当订阅被退款时触发(例如,由 Apple 客服处理)。 | | non_subscription_purchase_refunded | 当非订阅购买被退款时触发。 | | access_level_updated | 当用户的访问等级更新时发生。 | :::note `subscription_renewal_reactivated` 携带的是**之前**的产品 ID——即用户取消订阅时处于活跃状态的产品 ID——即使用户后来通过购买其他产品重新激活了订阅也是如此。Apple 在整个取消 → 重新激活链路中保持相同的 `original_transaction_id`,因此该事件反映的是原始产品。新产品将在下一个 `subscription_renewed` 事件中体现,届时新产品的计费正式开始。 ::: ## Webhook 事件结构 \{#webhook-event-structure\} Adapty 只会发送你在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面 **Events names** 部分所选择的事件。 Webhook 事件以 JSON 格式序列化。发送到您服务器的 `POST` 请求体将包含序列化事件,并封装在以下结构中。所有事件遵循相同的结构,但其字段会根据事件类型、商店以及您的具体配置有所不同。用户属性是您设置的[自定义用户属性](setting-user-attributes#custom-user-attributes),因此其内容取决于您的配置。归因数据字段在所有事件类型中保持一致,但归因列表取决于您的移动应用中使用了哪些归因来源。以下是一个事件示例: ```json title="Json" showLineNumbers { "profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": "UserIdInYourSystem", "idfv": "00000000-0000-0000-0000-000000000000", "idfa": "00000000-0000-0000-0000-000000000000", "advertising_id": "00000000-0000-0000-0000-000000000000", "profile_install_datetime": "2000-01-31T00:00:00.000000+0000", "user_agent": "ExampleUserAgent/1.0 (Device; OS Version) Browser/Engine", "email": "john.doe@company.com", "event_type": "subscription_started", "event_datetime": "2000-01-31T00:00:00.000000+0000", "event_properties": { "store": "play_store", "currency": "USD", "price_usd": 4.99, "profile_id": "00000000-0000-0000-0000-000000000000", "cohort_name": "All Users", "environment": "Production", "price_local": 4.99, "original_price_usd": 4.99, "original_price_local": 4.99, "discount_amount_usd": 0, "discount_amount_local": 0, "base_plan_id": "b1", "developer_id": "onboarding_placement", "ab_test_name": "onboarding_ab_test", "ab_test_revision": 1, "paywall_name": "UsedPaywall", "proceeds_usd": 4.2315, "variation_id": "00000000-0000-0000-0000-000000000000", "purchase_date": "2024-11-15T10:45:36.181000+0000", "store_country": "AR", "event_datetime": "2000-01-31T00:00:00.000000+0000", "proceeds_local": 4.2415, "tax_amount_usd": 0, "transaction_id": "0000000000000000", "net_revenue_usd": 4.2415, "profile_country": "AR", "paywall_revision": "1", "profile_event_id": "00000000-0000-0000-0000-000000000000", "tax_amount_local": 0, "net_revenue_local": 4.2415, "vendor_product_id": "onemonth_no_trial", "profile_ip_address": "10.10.1.1", "consecutive_payments": 1, "rate_after_first_year": false, "original_purchase_date": "2000-01-31T00:00:00.000000+0000", "original_transaction_id": "0000000000000000", "subscription_expires_at": "2000-01-31T00:00:00.000000+0000", "profile_has_access_level": true, "profile_total_revenue_usd": 4.99, "promotional_offer_id": null, "store_offer_category": null, "store_offer_discount_type": null }, "event_api_version": 1, "profiles_sharing_access_level": [{"profile_id": "00000000-0000-0000-0000-000000000000", "customer_user_id": "UserIdInYourSystem"}], "attributions": { "appsflyer": { "ad_set": "Keywords 1.12", "status": "non_organic", "channel": "Google Ads", "ad_group": null, "campaign": "Social media influencers - Rest of the world", "creative": null, "created_at": "2000-01-31T00:00:00.000000+0000" } }, "user_attributes": {"Favourite_color": "Violet", "Pet_name": "Fluffy"}, "integration_ids": {"firebase_app_instance_id": "val1", "branch_id": "val2", "one_signal_player_id": "val3"}, "play_store_purchase_token": { "product_id": "product_123", "purchase_token": "token_abc_123", "is_subscription": true } } ``` ### 事件字段 \{#event-fields\} 各类事件的事件参数均相同。 | **字段** | **类型** | **描述** | |---|---|---| | **advertising_id** | UUID | 广告 ID(仅限 Android)。 | | **attributions** | JSON | [归因数据](webhook-event-types-and-fields#attributions)。在 [Webhook 设置](https://app.adapty.io/integrations/customwebhook)中启用 **Send Attribution** 后包含此字段。 | | **customer_user_id** | String | 您应用中的用户 ID(UUID、邮箱或其他 ID),需在应用代码中[识别用户](ios-quickstart-identify)时设置。若未在应用代码中识别用户,或该用户为匿名用户(未登录),则此字段为 `null`。 | | **email** | String | 用户邮箱,需通过 Adapty SDK 中的 [`updateProfile`](setting-user-attributes) 方法,或通过服务端 API 创建/更新用户画像时设置。若未向 SDK 或 API 方法传入 `email` 值,则此字段为 `null`。 | | **event_api_version** | Integer | Adapty API 版本(当前版本:`1`)。 | | **event_datetime** | ISO 8601 | 事件的业务发生时间,例如购买事件对应购买日期,到期事件对应到期日期,而非 Adapty 接收或发送事件的时间。采用 [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) 格式(例如 `2020-07-10T15:00:00.000000+0000`)。有关排序的说明请参见下方注意事项。 | | **event_properties** | JSON | [事件属性](webhook-event-types-and-fields#event-properties)。 | | **event_type** | String | Adapty 格式的事件名称。完整列表请参见 [Webhook 事件类型](webhook-event-types-and-fields#webhook-event-types)。 | | **idfa** | UUID | 广告标识符(仅限 Apple)。对应 [Adapty 看板](https://app.adapty.io/profiles/users)用户画像中的 **IDFA**。若因追踪限制、儿童模式或隐私设置而不可用,则可能为 `null`。 | | **idfv** | UUID | 供应商标识符(IDFV),每位开发者唯一。对应 [Adapty 看板](https://app.adapty.io/profiles/users)用户画像中的 **IDFV**。 | | **integration_ids** | JSON | 用户集成 ID,需通过 Adapty SDK 中的 `setIntegrationIdentifier` 方法,或通过服务端 API 创建/更新用户画像时设置。不可用或集成已禁用时为 `null`。 | | **play_store_purchase_token** | JSON | [Play Store 购买令牌](webhook-event-types-and-fields#play-store-purchase-token),在 [Webhook 设置](https://app.adapty.io/integrations/customwebhook)中启用 **Send Play Store purchase token** 后包含此字段。 | | **profile_id** | UUID | Adapty 为每个用户画像自动生成的用户画像 ID。若未识别用户或允许购买发生在登录之前,同一 Apple/Google ID 可能关联多个不同的用户画像 ID。详情请参阅 [Adapty 如何处理父/继承用户画像](how-profiles-work#parent-and-inheritor-profiles)。 | | **profile_install_datetime** | ISO 8601 | 安装时间戳,采用 [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) 格式(例如 `2020-07-10T15:00:00.000000+0000`)。 | | **profiles_sharing_access_level** | JSON | 与当前用户画像共享访问等级的其他用户列表(不含当前用户)。若您的应用启用了访问等级共享,此列表将包含使用同一 Apple/Google ID 的其他用户画像。虽然移动应用代码中的自定义属性值可设为浮点数或字符串,但通过服务端 API 或历史数据导入的属性可能以不同格式传入,此时布尔值和整数值将转换为浮点数。
| :::note `event_datetime` 反映的是订阅生命周期中事件发生的时间,而非 Adapty 处理或推送该事件的时间。因此,多个事件可能共享相同的 `event_datetime`,或以非时间顺序到达。例如,`subscription_expired` 事件的 `event_datetime` 可能早于 Adapty 先行推送的 `subscription_renewal_cancelled` 事件。请勿依赖 `event_datetime` 对事件排序。应改用自行记录的接收时间排序,并通过 `profile_event_id` 或交易 ID 进行去重。 ::: ### 归因 \{#attributions\} 若要发送归因数据,请在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面中启用 **Send Attribution** 选项。启用后,若你已配置[归因集成](attribution-integration),以下数据将随每个来源的事件一并发送。所有事件类型均会收到相同的归因数据。 ```json title="Json" showLineNumbers { "attributions": { "appsflyer": { "ad_set": "sample_ad_set_123", "status": "non_organic", "channel": "sample_channel", "ad_group": "sample_ad_group_456", "campaign": "sample_ios_campaign", "creative": "sample_creative_789", "created_at": "2000-01-31T00:00:00.000000+0000", "network_user_id": "0000000000000-0000000" } } } ``` | 字段名 | 字段类型 | 描述 | | :------------------ | :------------ | :------------------------------------------------- | | **ad_set** | String | 归因广告组。 | | **status** | String | 可为 `organic`、`non_organic,` 或 `unknown`。 | | **channel** | String | 营销渠道名称。 | | **ad_group** | String | 归因广告集。 | | **campaign** | String | 营销活动名称。 | | **creative** | String | 归因创意关键词。 | | **created_at** | ISO 8601 date | 归因记录的创建日期和时间。 | | **network_user_id** | String | 归因来源为用户分配的 ID。 | ### 集成 ID \{#integration-ids\} 以下集成 ID 目前在事件中使用: - `adjust_device_id` - `airbridge_device_id` - `amplitude_device_id` - `amplitude_user_id` - `appmetrica_device_id` - `appmetrica_profile_id` - `appsflyer_id` - `branch_id` - `facebook_anonymous_id` - `firebase_app_instance_id` - `mixpanel_user_id` - `pushwoosh_hwid` - `one_signal_player_id` - `one_signal_subscription_id` - `tenjin_analytics_installation_id` - `posthog_distinct_user_id` ### Play Store 购买令牌 \{#play-store-purchase-token\} 此字段包含重新验证购买所需的全部数据(如有需要)。只有在 [Webhook 集成设置](https://app.adapty.io/integrations/customwebhook)中启用了 **Send Play Store purchase token** 选项后,才会发送该字段。 | 字段 | 类型 | 描述 | | :------------------ | :------ | :----------------------------------------------------------- | | **product_id** | String | 在 Play Store 中购买的产品唯一标识符(SKU)。 | | **purchase_token** | String | Google Play 生成的令牌,用于唯一标识本次购买交易。 | | **is_subscription** | Boolean | 表示所购产品是否为订阅(`true`)或一次性购买(`false`)。 | ### 事件属性 \{#event-properties\} 事件属性因事件类型而异,即使是同类事件也可能有所不同。例如,来自 App Store 的事件不会包含 `base_plan_id` 等 Android 专属属性。 [访问等级更新](webhook-event-types-and-fields#for-access-level-updated-event)事件具有独特的属性,因此我们为其单独开辟了一个章节。同样,[附加税务和收入事件属性](webhook-event-types-and-fields#additional-tax-and-revenue-event-properties)也被单独列出,因为它们仅适用于特定的事件类型。 #### 适用于大多数事件类型 \{#for-most-event-types\} 大多数事件类型的事件属性是一致的(**Access Level Updated** 事件除外,该事件在其专属章节中单独说明)。下表列出了所有属性,并标注了各属性所属的具体事件。 :::note Adapty 使用 [currencylayer.com](https://currencylayer.com/) 的汇率(每 8 小时更新一次)将其他货币换算为美元。汇率**在交易发生时锁定**——后续汇率变动不会影响已换算的结果。 ::: | 字段 | 类型 | 描述 | |:------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **ab_test_name** | String | 交易来源的 [Adapty A/B 测试](ab-tests)名称。 | | **ab_test_revision** | Integer | 交易来源的 A/B 测试版本号。 | | **base_plan_id** | String | Google Play Store 中的[基础方案 ID](https://support.google.com/googleplay/android-developer/answer/12154973),或 Stripe 中的[价格 ID](https://docs.stripe.com/products-prices/how-products-and-prices-work#use-products-and-prices)。 | | **cancellation_reason** | String |可能的取消原因:`voluntarily_cancelled`、`billing_error`、`price_increase`、`product_was_not_available`、`refund`、`cancelled_by_developer`、`new_subscription_replace`、`upgraded`、`unknown`、`adapty_revoked`。
出现在以下事件类型中:
`subscription_cancelled`、`subscription_refunded` 和 `trial_cancelled`。 | | **cohort_name** | String | 决定用户看到哪个付费墙的[目标受众](audience)名称。 | | **consecutive_payments** | Integer | 用户连续订阅的周期数(无中断),包含当前周期。 | | **currency** | String | 本地货币。 | | **developer_id** | String | 交易来源的[版位](placements) ID。 | | **discount_amount_local** | Float | 交易中应用的折扣金额:标准价格减去实际收取金额(Apple/Google 抽成前),以本地货币计。全价购买时为 `0`。免费试用时等于完整标准价格(`original_price_local`),因为实际未收取任何费用。若已应用优惠但标准价格未知,则为 `null`(详见 `original_price_local`)。App Store 预付费优惠始终为 `null`:因为单次预付金额涵盖多个计费周期,无法与每周期标准价格进行比较。 | | **discount_amount_usd** | Float | `discount_amount_local` 对应的 USD 金额。 | | **environment** | String | 可能的值为 `Sandbox` 或 `Production`。 | | **event_datetime** | ISO 8601 date | 事件发生的日期和时间,与事件根层级的值相同。 | | **original_price_local** | Float | 产品在 Apple/Google 抽成前的标准非折扣价格,以本地货币计。对于订阅,这是续订价格。全价购买时等于 `price_local`;一次性购买时始终等于 `price_local`,因为应用商店不会单独报告一次性购买的标准价格。当应用商店无法提供可靠标准价格(例如自动续订已关闭、续订仍带有优惠,或正在进行产品变更)时,折扣购买的值为 `null`。 | | **original_price_usd** | Float | 与 `original_price_local` 相同,以 USD 计。 | | **original_purchase_date** | ISO 8601 date | 对于定期订阅,原始购买是链中的第一笔交易,其 ID 称为原始交易 ID,用于关联一系列续订;后续交易均为其延续。原始购买日期即第一笔交易的日期和时间。 | | **original_transaction_id** | String |对于定期订阅,这是关联一系列续订的原始交易 ID。原始交易是链中的第一笔;后续交易均为其延续。
若无延续交易,则 `original_transaction_id` 与 store_transaction_id 相同。
| | **paywall_name** | String | 交易来源的付费墙名称。 | | **paywall_revision** | String | 交易来源的付费墙版本号,默认值为 1。 | | **price_local** | Float | 交易中实际收取的金额(Apple/Google 抽成前),以本地货币计。免费试用时为 `null`,因为未收取任何费用。 | | **price_usd** | Float | 交易中实际收取的金额(Apple/Google 抽成前),以 USD 计。免费试用时为 `null`,因为未收取任何费用。 | | **profile_country** | String | 由 Adapty 根据用户画像 IP 地址确定。 | | **profile_event_id** | UUID | 可用于去重的唯一事件 ID。 | | **profile_has_access_level** | Boolean | 布尔值,表示该用户画像是否拥有有效的访问等级。 | | **profile_id** | UUID | Adapty 生成的用户画像 ID,与事件根层级的值相同。 | | **profile_ip_address** | String | 用户画像 IP(可以是 IPv4 或 IPv6,优先使用 IPv4)。若在[应用设置](https://app.adapty.io/settings/general)中禁用了 **Collect users' IP addresses**,则为 `null`。 | | **profile_total_revenue_usd** | Float | 该用户画像的总收入(已扣除退款金额),以 USD 计。 | | **promotional_offer_id** | String | 所使用的[促销活动](offers)的 Adapty ID,在看板中创建优惠时由您设置。 | | **purchase_date** | ISO 8601 date | 产品购买的日期和时间。 | | **rate_after_first_year** | Boolean | 布尔值,表示该订阅是否在连续续订满一年后符合降低佣金率(通常为 15%)的条件。佣金率因计划资格和国家/地区而异。详情请参阅[商店佣金与税费](controls-filters-grouping-compare-proceeds#display-gross-or-net-revenue)。 | | **store** | String | 购买产品的应用商店。标准值:**app_store**、**play_store**、**stripe**、**paddle**。Apple App Store、Google Play Store 或 Stripe 中的产品 ID。
若访问权限是在无真实商店交易的情况下授予的,则 `vendor_product_id` 将为以下之一:
对于自动续订订阅,这是关联续订链的原始交易 ID。原始交易是链中的第一笔;后续交易均为其延续。
如果没有延续交易,`original_transaction_id` 与 store_transaction_id 相同。
原始购买的交易标识符。 | | **paywall_name** | String | 交易来源付费墙的名称。 | | **paywall_revision** | String | 交易来源付费墙的版本号。默认值为 1。 | | **profile_country** | String | 由 Adapty 根据用户画像 IP 判断。 | | **profile_event_id** | UUID | 唯一事件 ID,可用于去重。 | | **profile_has_access_level** | Boolean | 布尔值,表示用户画像是否拥有有效的访问等级。 | | **profile_id** | UUID | Adapty 内部用户画像 ID。 | | **profile_ip_address** | String | 用户画像的 IP 地址(可为 IPv4 或 IPv6,优先使用 IPv4)。若在[应用设置](https://app.adapty.io/settings/general)中禁用了 **Collect users' IP addresses**,则为 `null`。 | | **profile_total_revenue_usd** | Float | 用户画像的总收入,含退款。 | | **purchase_date** | ISO 8601 date | 购买产品的日期和时间。 | | **renewed_at** | ISO 8601 date | 访问权限将续订的日期和时间。 | | **starts_at** | ISO 8601 date | 访问等级开始生效的日期和时间。 | | **store** | String | 购买产品的商店。标准值:**app_store**、**play_store**、**stripe**、**paddle**。商店(Apple/Google/Stripe)中的产品 ID。
如果访问权限是在没有真实商店交易的情况下授予的,`vendor_product_id` 将为以下之一:
1. **您设置好端点:** 1. 确保您的服务器能够处理 Adapty 请求,并将 **Content-Type** 请求头设置为 `application/json`。 2. 配置您的服务器以接收 Adapty 的验证请求,并返回任意 `2xx` 状态码和 JSON 响应体。 3. 连接验证通过后,[处理订阅事件](#subscription-events)。 2. **您在 [Adapty 看板](#configure-webhook-integration-in-the-adapty-dashboard)中配置并启用 Webhook 集成。** 您也可以[将 Adapty 事件映射到自定义事件名称](#configure-webhook-integration-in-the-adapty-dashboard)。建议先在 **Sandbox environment** 中测试,再切换到生产环境。 3. **Adapty 向您的服务器发送验证请求。** 4. **您的服务器返回** `2XX` 状态码和 JSON 响应体。 5. **Adapty 收到有效响应后,即开始发送订阅事件。** ## 设置服务器以处理 Adapty 请求 \{#set-up-your-server-to-process-adapty-requests\} Adapty 会向你的 webhook 端点发送 2 种类型的请求: 1. [验证请求](#verification-request):用于验证连接是否正确建立的初始请求。该请求不包含任何事件,将在您点击 Adapty 看板 Webhook 集成中的 **Save** 按钮时立即发送。为确认您的端点成功接收到验证请求,您的端点应返回验证响应。 2. [订阅事件](#subscription-events):Adapty 服务器在每次创建事件时发送的标准请求。您的服务器无需返回任何特定响应,Adapty 服务器唯一需要的是在成功接收消息后收到标准的 HTTP 200 响应码。 ### 验证请求 \{#verification-request\} 在 Adapty 看板中启用 webhook 集成后,Adapty 会发送一个 POST 验证请求,请求体为空 JSON 对象 `{}`。 请将你的端点配置为使用 **Content-Type header** `application/json`,即你的服务器端点应接受以 JSON 格式传入的 webhook 请求。 你的服务器必须返回 2xx 状态码,并发送任意有效的 JSON 响应,例如: ```json title="Json" {} ``` 一旦 Adapty 收到格式正确且状态码为 2xx 的验证响应,您的 Adapty webhook 集成即配置完成。 ### 订阅事件 \{#subscription-events\} 订阅事件在发送时,**Content-Type** 请求头设置为 `application/json`,并以 JSON 格式包含事件数据。有关可能的事件类型和请求结构,请参阅 [Webhook 事件类型与字段](webhook-event-types-and-fields)。 ## 在 Adapty 看板中配置 Webhook 集成 \{#configure-webhook-integration-in-the-adapty-dashboard\} 在 Adapty 中,你可以为正式环境事件和测试事件(来自 Apple 或 Stripe 沙盒环境,或 Google 测试账号)分别配置独立的流程。 :::tip Adapty 每个环境(正式环境和沙盒环境)仅支持一个 Webhook URL。如需将事件推送至多个服务,请将 Webhook 指向你自己的后端,再由后端进行分发。 ::: 对于生产环境事件,请使用 **Production endpoint URL** 字段填写回调发送的目标 URL。同时配置 **Authorization header value for production endpoint** 字段——该字段用于您的服务器验证 Adapty 事件。请注意,我们会将 **Authorization header value for production endpoint** 字段中填写的值原样作为 `Authorization` 请求头发送,不做任何修改或添加。 对于测试事件,请相应地使用 **Sandbox endpoint URL** 和 **Authorization header value for sandbox endpoint** 字段。 要设置 webhook 集成: 1. 在 Adapty 看板中打开 [Integrations -> Webhook](https://app.adapty.io/integrations/customwebhook)。
2. 打开开关以启动集成。
4. 填写集成字段:
| 字段 | 描述 |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| **Production endpoint URL** | Adapty 用于在生产环境中发送事件 HTTP POST 请求的 URL。 |
| **Authorization header value for production endpoint** | 您的服务器用于验证来自 Adapty 的生产环境请求的请求头。请注意,我们将使用此字段中指定的值作为 `Authorization` 请求头,不会进行任何修改或添加。
虽然不是必填项,但强烈建议配置以提升安全性。
| 此外,为了满足您在沙盒环境中的测试需求,还提供了另外两个字段: | 测试字段 | 说明 | | --------------------------------------------------- | ------------------------------------------------------------ | | **Sandbox endpoint URL** | Adapty 在沙盒环境中发送事件 HTTP POST 请求时所使用的 URL。 | | **Authorization header value for sandbox endpoint** |您的服务器在沙盒环境测试期间,用于验证 Adapty 请求的请求头。请注意,我们会将该字段中指定的值原样作为 `Authorization` 请求头使用,不做任何修改或补充。
虽然非强制要求,但强烈建议配置此项以提升安全性。
| 4. (可选)选择您希望接收的事件并映射其名称。请查阅[事件流程](event-flows),了解在不同情况下会触发哪些事件。 如果您系统中的事件 ID 与 Adapty 中使用的 ID 不同,请保留您系统中的 ID,并在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面的 **Events names** 部分,将 Adapty 默认事件 ID 替换为您自己的 ID。 事件 ID 可以是任意字符串;只需确保 Webhook 处理服务器中的事件 ID 与您在 Adapty 看板中输入的一致。已启用的事件不能将事件 ID 留空。
5. 其他字段和选项并非必填,请按需使用:
| 设置 | 描述 |
| :--------------------------------- | :----------------------------------------------------------- |
| **Send Trial Price** | 启用后,Adapty 将在 **Trial Started** 事件的 `price_local` 和 `price_usd` 字段中包含订阅价格。 |
| **Exclude Historical Events** | 选择排除用户在安装含 Adapty SDK 的应用之前发生的事件。这可以防止事件重复,并确保报告准确。例如,若用户于 1 月 10 日激活了月度订阅,并于 3 月 6 日更新了含 Adapty SDK 的应用,则 Adapty 将忽略 3 月 6 日之前的事件,并保留此后的事件。 |
| **Send user attributes** | 启用此选项以发送用户特定属性,例如语言偏好。这些属性将显示在 `user_attributes` 字段中。详见[事件字段](webhook-event-types-and-fields#event-fields)。 |
| **Send attribution** | 启用此选项以在 `attributions` 字段中包含归因信息(例如 AppsFlyer 数据)。详见[归因数据](webhook-event-types-and-fields#attributions)部分。 |
| **Send Play Store purchase token** | 启用此选项以接收购买重新验证所需的 Play Store 令牌(如有需要)。启用后将在事件中添加 `play_store_purchase_token` 参数。有关其内容的详细信息,请参阅 [Play Store 购买令牌](webhook-event-types-and-fields#play-store-purchase-token)部分。 |
6. 记得点击 **Save** 按钮确认更改。
点击 **Save** 按钮后,Adapty 将立即发送验证请求,并等待您的服务器返回验证响应。
### 选择要发送的事件并映射事件名称 \{#choose-events-to-send-and-map-event-names\}
通过启用事件旁边的开关,选择您希望服务器接收的事件。如果您的事件名称与 Adapty 中使用的名称不同,且需要保留自定义名称,可以在 [Integrations -> Webhooks](https://app.adapty.io/integrations/customwebhook) 页面的 **Events names** 部分,将默认的 Adapty 事件名称替换为您自己的名称,从而完成映射配置。
事件名称可以是任意字符串。已启用的事件对应的字段不能留空。如果您不小心删除了 Adapty 事件名称,可以随时从[发送至第三方集成的事件](events)文档中复制。
## 处理 Webhook 事件 \{#handle-webhook-events\}
Webhook 通常在事件发生后 5 到 60 秒内送达。但取消事件可能在用户取消订阅后最长 2 小时才会送达。
如果您服务器的响应状态码不在 200-404 范围内,Adapty 会以指数退避方式重试。首次重试大约在初次失败后 **1 分钟**发生,此后每次间隔翻倍——最多重试 9 次,分布在 24 小时内。建议您将 Webhook 配置为仅对 Adapty 发来的事件体做基本校验后再响应。如果您的服务器无法处理该事件且不希望 Adapty 重试,请使用 200-404 范围内的状态码。此外,请将耗时任务改为异步处理,并尽快向 Adapty 返回响应。若 Adapty 在 10 秒内未收到响应,则视为本次尝试失败并将进行重试。
---
# File: test-webhook
---
---
title: "测试 webhook 集成"
description: "在 Adapty 中测试 webhook 集成,以自动化订阅事件追踪。"
---
完成集成设置后,就可以开始测试了。您可以测试沙盒环境和生产环境的集成。我们建议先从沙盒环境开始,并在其中进行充分验证:
- 事件已发送并成功送达。
- 您已正确配置历史事件、**Trial started** 事件的订阅价格、归因、用户属性以及 Google Play Store 购买令牌的发送选项。
- 您正确映射了事件名称,且您的服务器能够正常处理这些事件。
## 如何测试 \{#how-to-test\}
在开始测试集成之前,请确保您已完成以下操作:
1. 按照 [设置 webhook 集成](set-up-webhook-integration) 主题中的说明完成 webhook 集成配置。
2. 按照 [在 Apple App Store 中测试应用内购买](test-purchases-in-sandbox) 和 [在 Google Play Store 中测试应用内购买](testing-on-android) 主题中的说明配置好测试环境。请确保您在沙盒环境而非生产环境中构建了测试应用。
3. 进行购买/开始试用/发起退款等操作,以触发您选择发送到 webhook 的事件。例如,要获取 **Subscription started** 事件,请购买一个新的订阅。
## 验证结果 \{#validation-of-the-result\}
### 事件发送成功的结果 \{#successful-sending-events-result\}
集成成功后,事件将出现在集成的 **Last sent events** 部分,并显示 **Success** 状态。
### 事件发送失败的结果 \{#unsuccessful-sending-events-result\}
| 问题 | 解决方案 |
|-----|--------|
| 事件未出现 | 您的购买未成功,因此未创建事件。请参阅 [测试购买故障排查](troubleshooting-test-purchases) 主题以获取解决方案。 |
| 事件已出现但显示 **Sending failed** 状态 | 我们根据 HTTP 状态码判断是否送达,**200-399 范围以外**的状态码均视为失败。
如需了解更多问题详情,请将鼠标悬停在失败事件的 **Sending failed** 状态上,如下图所示。
|
---
# File: handle-integration-errors
---
---
title: "处理集成中的错误"
description: "处理集成中的错误"
---
使用任何归因、消息推送或分析集成时,您可能会遇到一些常见错误。请参阅本指南了解故障排除方案。
## 数据差异 \{#data-discrepancy\}
**原因**:这可能是因为并非所有用户都在使用包含 Adapty SDK 的应用版本。
**解决方案**:为确保数据一致性,您可以强制用户将应用更新至包含 Adapty SDK 的版本。
## 网络错误 \{#network-errors\}
**原因**:这很可能是因为 Adapty 服务器与集成服务器之间的网络连接中断。
**解决方案**:此类问题通常不会持续太久,且仅影响少量事件。
## 集成服务器处理事件失败 \{#integration-server-failed-to-process-the-event\}
**原因**:集成配置不正确。
**解决方案**:请参阅我们文档中关于该集成的文章,确保您已在 Adapty 看板、第三方工具端以及应用代码中完成所有配置步骤。
## 缺少集成数据 \{#missing-integration-data\}
**原因**:用户画像缺少某些集成专用 ID。这可能发生在应用代码中集成未正确配置的情况下。
**解决方案**:请参阅我们文档中关于该集成的文章,确保您已在应用代码中实现代码片段中的方法,并且这些方法确实与您的用户画像进行了交互。
## 缺少集成凭据 \{#missing-integration-credentials\}
**原因**:某些集成凭据缺失或不正确。
**解决方案**:请在 Adapty 看板上检查该集成的所有凭据。此问题可能由版本或环境不匹配引起。
## 事件已过期 \{#the-event-has-expired\}
**原因**:集成设置中启用了 **Exclude historical events** 选项,且事件的创建日期早于我们系统中该用户画像的创建日期。
如果一条从多年前开始的交易链通过收据验证传入 Adapty,而对应的用户画像是最近才创建的,则可能发生这种情况。
**解决方案**:确保新事件不会出现此情况。如果您希望将历史事件发送至集成,请禁用 **Exclude historical events**。
## 已禁用/不支持的事件类型 \{#disabledunsupported-event-type\}
**原因**:该集成不支持此事件类型,或者您在配置集成时将其禁用。例如,大多数集成不支持 `access_level_updated` 事件。
**解决方案**:请查阅集成文档,确认该集成是否支持此事件类型。如果支持,请在 Adapty 看板中确认该事件类型在集成设置中已启用。
---
# File: manage-adapty-with-ai
---
---
title: "使用 AI 智能体和编程工具管理 Adapty"
description: "使用 Adapty 与 AI 协作的各种方式——通过编程智能体集成 SDK、借助大语言模型查询分析数据,并将 Adapty 文档提供给 AI 工具。"
---
Adapty 可与 AI 编程工具和智能体配合使用。无需离开编辑器,即可用它们集成 SDK、查询分析数据或查阅 Adapty 文档。本页列出了所有可用方式及其适用场景。
## 使用 AI 集成 Adapty SDK \{#integrate-the-adapty-sdk-with-ai\}
有两种方式可通过 AI 编程工具将 Adapty SDK 接入你的应用,均支持 Cursor、Claude 及其他 AI 助手。
### 技能式集成 \{#skill-based-integration\}
Adapty SDK 集成技能可在 AI 编程工具中通过一条命令完成整个集成流程。适合希望获得引导式自动化配置的用户。
选择你的平台:[iOS](adapty-sdk-integration-skill) · [Android](adapty-sdk-integration-skill-android) · [React Native](adapty-sdk-integration-skill-react-native) · [Flutter](adapty-sdk-integration-skill-flutter) · [Unity](adapty-sdk-integration-skill-unity) · [Kotlin Multiplatform](adapty-sdk-integration-skill-kmp) · [Capacitor](adapty-sdk-integration-skill-capacitor)
### 分步集成 \{#step-by-step-integration\}
按阶段引导 AI 工具完成集成,依次提供对应文档。适合希望逐步审查每个步骤的用户。
选择你的平台:[iOS](adapty-cursor) · [Android](adapty-cursor-android) · [React Native](adapty-cursor-react-native) · [Flutter](adapty-cursor-flutter) · [Unity](adapty-cursor-unity) · [Kotlin Multiplatform](adapty-cursor-kmp) · [Capacitor](adapty-cursor-capacitor)
## 从命令行管理 Adapty \{#manage-adapty-from-the-command-line\}
[Adapty Developer CLI](developer-cli-quickstart) 让你可以在终端中管理 Adapty 的各类实体——应用、访问等级、产品、付费墙和版位——无需打开看板。由于它是命令行工具,AI 编程智能体可以直接调用它。
## 查询你的数据 \{#ask-about-your-data\}
将 AI 编程智能体指向 Export Analytics API,即可用自然语言查询你的数据指标——包括收入、留存率、LTV 等,无需 MCP 服务器。
[向 AI 查询你的分析数据](export-analytics-with-ai)
## 将 Adapty 文档提供给 AI 工具 \{#give-your-ai-tool-the-adapty-docs\}
### 纯文本文档 \{#plain-text-docs\}
所有 Adapty 文档均提供 Markdown 格式——在页面 URL 后添加 `.md`,或点击标题下方的 **Copy for LLM**。如需更广泛的上下文,可将 [`llms.txt`](https://adapty.io/docs/zh/llms.txt) 索引或特定平台的子集(如 [`ios-llms.txt`](https://adapty.io/docs/zh/ios-llms.txt))提供给你的工具。
### Context7 \{#context7\}
[Context7](https://context7.com/adaptyteam/adapty-docs) 是一个 MCP 服务器,可将 Adapty 文档提供给你的 AI 工具,但它仅索引代码片段,而非完整正文。如需快速获取代码示例可使用它;如需完整指引,请使用上述纯文本文档。Context7 支持 Cursor、Claude Code、Windsurf 及其他兼容 MCP 的工具。
---
# File: export-analytics-with-ai
---
---
title: "向 AI 询问您的分析数据"
description: "使用导出分析 API,通过 AI 编码代理以自然语言查询您的 Adapty 分析数据。"
---
Ask an AI coding agent about your Adapty analytics in plain language — revenue, conversions, retention, LTV — and let it pull the numbers for you. Point a tool that can make API calls at the [Export Analytics API](https://adapty.io/docs/zh/export-analytics-api.md), and it queries your metrics on demand.
## 可查询的内容 \{#what-you-can-ask-about\}
Export Analytics API 返回的数据与 Adapty 看板数据图表中展示的内容一致。每个数据图表对应一个独立的操作:
| 数据图表 | 涵盖内容 | 操作 |
| --- | --- | --- |
| 收入、MRR、ARR、ARPU | 按时间段、国家或推广活动分组的收入情况 | [retrieveAnalyticsData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveAnalyticsData.md) |
| 同期群留存 | 某一同期群的订阅用户持续付费的时长 | [retrieveCohortData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveCohortData.md) |
| 转化率 | 用户从某一步骤或渠道进入下一步骤的比例 | [retrieveConversionData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveConversionData.md) |
| 流失与漏斗 | 用户在哪些环节流失以及退订速度 | [retrieveFunnelData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveFunnelData.md) |
| 用户生命周期价值 (LTV) | 各用户市场细分的平均收入随时间的变化 | [retrieveLTVData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveLTVData.md) |
| 留存率 | 若干天后仍活跃的用户占比 | [retrieveRetentionData](https://adapty.io/docs/zh/api-export-analytics/operations/retrieveRetentionData.md) |
有关参数和过滤器的完整列表,请参阅 [API 参考文档](https://adapty.io/docs/zh/api-export-analytics.md)。
## 开始之前 \{#before-you-start\}
你需要准备以下三样东西:
- **已有数据的 Adapty 账户**:该 API 返回的数据图表与看板中显示的一致,因此你的应用必须已经在收集分析数据。
- **Secret API 密钥**:在 [App settings → General](https://app.adapty.io/settings/general) 的 **Secret key** 字段中找到。密钥与应用绑定,每个应用需使用独立的密钥。建议将其存储在环境变量中(例如 `ADAPTY_SECRET_KEY`),这样 AI 代理可以直接读取,无需手动粘贴到对话中。
- **能够调用 API 的 AI 工具**:例如 Claude Code、Cursor,或配置了 fetch 工具的 Claude Desktop。claude.ai 或 ChatGPT 等纯对话工具无法直接调用 API。
## 为你的 Agent 提供 API 规范 \{#give-your-agent-the-api-spec\}
[OpenAPI 规范](https://adapty.io/docs/zh/api-specs/export-analytics-api.yaml)描述了每个端点、认证请求头、请求体以及响应示例。Agent 获取规范后,无需你编写任何代码即可构建正确的请求。
通过 URL 向 Agent 提供规范:
- **粘贴 URL**:如果你的 AI 助手支持抓取 URL,将 `https://adapty.io/docs/zh/api-specs/export-analytics-api.yaml` 提供给它,让它读取该规范文件。
- **使用抓取工具**:如果你的 AI 助手有可以获取 URL 的工具(例如 MCP fetch 服务器),将其指向同一 URL 即可。
该规范将 base URL 设置为 `https://api-admin.adapty.io`,因此只要你的密钥已配置到环境中,AI 助手即可获取所有所需信息。
## 查询你的数据 \{#ask-about-your-data\}
加载好规范文件并将密钥设置为环境变量后,就可以用自然语言描述你想查询的数据图表了。
示例提示词:
```
What was my MRR at the end of each month this year, and how does it compare to last year?
Show my trial-to-paid conversion rate for the last 90 days, broken down by product.
Which countries drive the most revenue from my yearly subscription? Top 10.
How is week-1 retention trending for subscribers who started in the last 6 months?
What's the refund rate on my annual plan since launch, by month?
Compare LTV for paid-campaign users vs. organic over the last year, and export it as CSV.
```
代理会将您的请求映射到对应操作,从环境变量中读取密钥,并返回数据。默认响应格式为 JSON。如果需要方便导入电子表格的文件,可以要求 CSV 格式——代理会在请求体中将 `format` 设置为 `csv`。
:::warning
请将密钥保存在环境变量中,不要粘贴到聊天记录里,也不要提交到规则文件中。密钥与应用绑定,如有泄露,请在 **Settings → General** 中轮换密钥。详见[轮换 API 密钥](https://adapty.io/docs/zh/export-analytics-api-authorization.md)。
:::
## 一次配置,反复使用 \{#set-up-once-for-repeated-use\}
为避免每次会话都重复配置,请将 spec 和 key 保存到 agent 可复用的位置:
- **保存规格链接**:将规格 URL 添加到你的智能体规则或记忆文件中(例如 `CLAUDE.md` 或 Cursor 规则文件),这样每次会话都会自动加载。
- **将密钥存入环境变量**:将 `ADAPTY_SECRET_KEY` 保存到你的 Shell 配置文件或工具的密钥存储中,以后就不需要再手动粘贴了。
- **保存常用提示词或创建自定义技能**:将常用问题保存为提示词,或封装成自定义技能或斜杠命令,让智能体随时按需生成报告。
## 限制 \{#limits\}
请注意以下约束:
- **速率限制**:API 每个 API key 每秒允许 2 次请求,超出限制将返回 `429 Too Many Requests` 错误。请告知你的 agent 在收到 `429` 时等待并重试。
- **应用专属密钥**:每个密钥仅对应一个应用。如需拉取多个应用的数据,请为每个应用提供对应的密钥。
- **输出格式**:默认响应格式为 JSON。如需导出 CSV,请在请求体中将 `format` 设置为 `csv`。
完整的身份验证和请求规则,请参阅[授权与请求格式](https://adapty.io/docs/zh/export-analytics-api-authorization.md)。
---
# File: handle-webhooks-with-ai
---
---
title: "使用 Webhook 处理 Adapty 订阅事件"
description: "通过 Webhook 在服务器端接收并处理 Adapty 订阅事件——涵盖端点配置、身份验证、数据载荷及测试的完整指南。"
---
Webhooks 让您的服务器能够实时接收 Adapty 订阅事件——包括购买、续订、取消、账单问题和退款——从而授予访问权限、同步后端或触发工作流。本指南将带您在同一页面上完成从端点配置到验证、测试的完整集成流程,并介绍如何让 AI 编程助手为您的技术栈编写处理程序。
:::tip
正在使用 AI 编程助手?点击标题下方的 **Copy for LLM**,将整个页面粘贴到您的助手中——它包含所需的配置说明、数据载荷和处理逻辑。
:::
## Adapty Webhook 的工作原理 \{#how-adapty-webhooks-work\}
- **单向实时推送**:当事件发生时,Adapty 会向你的服务器发送 HTTP `POST` 请求,无需轮询。
- **两种请求类型**:一次性验证请求(在你保存集成时发送)和持续的订阅事件通知。
- **每个环境独立 URL**:你需要分别为生产环境和沙盒环境配置独立的接收端点。
- **需要应答每个请求**:请尽快以 `2xx` 状态码响应,否则 Adapty 会在失败时重试。
## 构建你的 Endpoint \{#build-your-endpoint\}
创建一个能处理以下两种请求类型的公开 HTTPS Endpoint:
- **验证请求**:在你保存集成时发送一次,请求体为空 JSON(`{}`)。请返回 `2xx` 状态码及 JSON 响应体。
- **订阅事件**:持续发来的 `POST` 请求,事件内容在请求体中。请在 10 秒内返回 `200`,然后以异步方式处理耗时操作。
选择一个密钥字符串并将其存储为环境变量(例如 `ADAPTY_WEBHOOK_SECRET`)。在每次请求时,验证 `Authorization` 请求头是否与其匹配,若不匹配则拒绝该请求——稍后你将在看板中输入相同的密钥。
```javascript title="webhook.js"
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.ADAPTY_WEBHOOK_SECRET;
app.post("/adapty/webhook", (req, res) => {
// 1. Verify the shared secret Adapty echoes back.
if (req.get("Authorization") !== WEBHOOK_SECRET) {
return res.sendStatus(401);
}
// 2. Acknowledge fast, then process asynchronously.
res.status(200).json({});
// 3. The verification request has an empty body — nothing to handle.
const event = req.body;
if (!event.event_type) return;
switch (event.event_type) {
case "subscription_started":
case "subscription_renewed":
case "trial_converted":
// Grant or extend access.
break;
case "subscription_expired":
case "subscription_refunded":
// Revoke access.
break;
default:
break;
}
});
app.listen(3000);
```
在配置集成之前,请先将端点部署到公开的 HTTPS URL——Adapty 会在你保存的瞬间发送验证请求。
### 关键事件及其数据载荷 \{#key-events-and-the-payload\}
每个事件共享相同的外层结构。字段内容因事件类型、应用商店及所启用的选项而有所不同。以下是一个精简版的 `subscription_started` 事件示例:
```json title="Example event"
{
"profile_id": "00000000-0000-0000-0000-000000000000",
"customer_user_id": "UserIdInYourSystem",
"event_type": "subscription_started",
"event_datetime": "2024-11-15T10:45:36.181000+0000",
"event_properties": {
"store": "play_store",
"currency": "USD",
"price_usd": 4.99,
"vendor_product_id": "onemonth_no_trial",
"transaction_id": "0000000000000000",
"original_transaction_id": "0000000000000000",
"subscription_expires_at": "2024-12-15T10:45:36.181000+0000",
"profile_event_id": "00000000-0000-0000-0000-000000000000"
},
"event_api_version": 1
}
```
最常处理的事件:
| 事件类型 | 触发时机 |
| --- | --- |
| `subscription_started` | 用户开始付费订阅 |
| `subscription_renewed` | 订阅成功续期并完成扣款 |
| `subscription_renewal_cancelled` | 用户关闭自动续订(访问权限持续至到期) |
| `subscription_expired` | 订阅未续期到期后访问权限终止 |
| `trial_started` | 用户开始免费试用 |
| `trial_converted` | 试用转换为付费订阅 |
| `billing_issue_detected` | 续订付款失败 |
| `subscription_refunded` | 订阅购买被退款 |
完整的事件列表及每个字段的详细说明,请参阅 [Webhook 事件类型与字段](https://adapty.io/docs/zh/webhook-event-types-and-fields.md)。
:::warning
不要按 `event_datetime` 对事件排序——该字段表示事件的业务发生时间,事件可能乱序到达,也可能具有相同的时间戳。请按自己的接收时间排序,并使用 `profile_event_id` 或事务 ID 进行去重。
:::
## 在 Adapty 中配置 Webhook \{#configure-the-webhook-in-adapty\}
1. 在 Adapty 看板中打开 [Integrations → Webhook](https://app.adapty.io/integrations/customwebhook)。
2. 开启该集成。
3. 在 **Production endpoint URL** 中,输入你部署的端点的 HTTPS URL。
4. 在 **Authorization header value for production endpoint** 中,输入与端点校验逻辑相同的密钥。Adapty 会在每次请求时通过 `Authorization` 请求头将该值回传给你的端点。此项为可选,但强烈建议填写。
5. 如需先在沙盒环境中测试,同样填写 **Sandbox endpoint URL** 及其 **Authorization header value**。
6. 点击 **Save**。Adapty 会立即向你的端点发送验证请求,端点返回 `2xx` 响应后即完成配置。
要选择发送哪些事件、映射事件名称,或启用可选字段(试用价格、历史事件、归因、用户属性、Play Store token),请参阅[设置 Webhook 集成](https://adapty.io/docs/zh/set-up-webhook-integration.md)。
## 用 AI 编程助手来构建 \{#build-it-with-your-ai-coding-agent\}
将本指南和以下参考文档以 Markdown 格式提供给你的 AI 编程助手(在任意页面 URL 后加 `.md` 即可获取),告诉它你的技术栈,让它自动生成处理程序:
- [Webhook 事件类型与字段](https://adapty.io/docs/zh/webhook-event-types-and-fields.md)
- [配置 Webhook 集成](https://adapty.io/docs/zh/set-up-webhook-integration.md)
示例提示词:
```
Read these Adapty webhook docs, then write a webhook handler for my Express app:
verify the Authorization header against ADAPTY_WEBHOOK_SECRET, answer the
verification request, acknowledge events with 200, and grant or revoke access
based on event_type.
```
代理会编写处理程序代码,但无法部署你的端点或配置看板——请自行托管端点,并在 **Integrations → Webhook** 中设置 URL 和密钥。
## 测试您的 Webhook \{#test-your-webhook\}
在正式上线前,请先在沙盒环境中进行测试:
1. 按照上述说明配置沙盒端点和密钥。
2. 在沙盒应用中完成购买、开启试用或申请退款,以触发相应事件。
3. 打开集成页面的 **Last sent events** 部分。已成功投递的事件会显示 **Success** 状态。
如果事件显示 **Sending failed**,说明您的服务器返回了 200–399 范围之外的状态码——将鼠标悬停在状态上可查看详情。完整的测试流程,请参阅[测试 Webhook 集成](https://adapty.io/docs/zh/test-webhook.md)。
## 限制 \{#limits\}
- **10 秒内响应**:如果 Adapty 未能在规定时间内收到响应,会将本次尝试标记为失败并重新发送。
- **重试机制**:如果您返回的状态码不在 200–404 范围内,Adapty 会以指数退避策略进行重试——在 24 小时内最多重试 9 次。
- **取消延迟**:取消事件最多可能延迟 2 小时送达。
- **每个环境仅支持一个 URL**:如需将事件推送至多个服务,请将 Webhook 指向您自己的后端,再由后端进行分发。
---
# File: server-side-api-with-ai
---
---
title: "从后端检查并授予订阅访问权限"
description: "使用 Adapty 服务端 API 检查用户是否拥有有效订阅,并通过 AI 编程助手手动授予访问权限。"
---
在您的后端,使用 Adapty 服务端 API 来检查用户是否拥有有效订阅,以及手动授予访问权限。本指南涵盖两个最常用的接口调用——`getProfile` 和 `grantAccessLevel`——并介绍如何让 AI 编程助手为您的技术栈编写集成代码。
:::tip
正在使用 AI 编程助手?点击标题下方的 **Copy for LLM**,将整个页面粘贴给您的助手——其中包含所需的接口调用、字段说明和注意事项。
:::
## 开始之前 \{#before-you-start\}
- **一个密钥(Secret API key)**:在 [App settings → General](https://app.adapty.io/settings/general) 的 **Secret key** 字段中找到它。密钥与应用绑定。请将其存储在环境变量中(例如 `ADAPTY_SECRET_KEY`),并通过 `Authorization: Api-Key {key}` 传递。
- **基础 URL**:所有请求均发送至 `https://api.adapty.io`。
- **识别用户的方式**:发送 `adapty-customer-user-id`(你自己的用户 ID——仅当你在应用中标识了用户时有效)或 `adapty-profile-id`(Adapty 用户画像 ID)。两者可互换,选其一即可。
## 查看订阅 \{#check-a-subscription\}
要查看订阅状态,请使用 `GET` 方法调用 `getProfile`,并在请求头中传入用户标识符——无需请求体。
```javascript title="check-access.js"
const res = await fetch("https://api.adapty.io/api/v2/server-side-api/profile/", {
headers: {
"Authorization": `Api-Key ${process.env.ADAPTY_SECRET_KEY}`,
"adapty-customer-user-id": userId,
},
});
const { data } = await res.json();
function hasActiveAccess(profile, accessLevelId = "premium") {
const level = profile.access_levels?.find(a => a.access_level_id === accessLevelId);
if (!level) return false;
if (level.is_in_grace_period) return true;
if (!level.expires_at) return true; // lifetime / non-expiring
return new Date(level.expires_at) > new Date(); // not expired yet
}
if (hasActiveAccess(data)) {
// unlock premium features
}
```
与 SDK 的用户画像不同,服务端响应**没有 `is_active` 字段**。请自行根据 `access_levels[].expires_at` 判断状态:`null` 表示永久授权,未来日期表示有效,过去日期表示已过期。`is_in_grace_period` 应视为仍处于有效状态。完整的用户画像及访问等级字段说明,请参阅 [getProfile](https://adapty.io/docs/zh/api-adapty/operations/getProfile.md)。
## 手动授予访问权限 \{#grant-access-manually\}
如需在不经过购买流程的情况下解锁付费功能——例如促销码、投资者或测试版访问权限、客服案例——可使用 `POST` 方法调用 `grantAccessLevel`。
```javascript title="grant-access.js"
await fetch("https://api.adapty.io/api/v2/server-side-api/purchase/profile/grant/access-level/", {
method: "POST",
headers: {
"Authorization": `Api-Key ${process.env.ADAPTY_SECRET_KEY}`,
"adapty-customer-user-id": userId,
"Content-Type": "application/json",
},
body: JSON.stringify({ access_level_id: "premium" }), // add "expires_at" for temporary access
});
```
请注意以下两点:
- **访问等级必须已存在**于看板中(**Access levels**)—— `access_level_id` 是该等级的标识符,而非新名称。
- **手动授予的访问等级不会出现在分析报告中**。相关数据仅会推送至你的 webhook 集成和 Event Feed,因此收入和转化率数据图表不会反映这些操作。
请求与响应的详细信息,请参阅 [grantAccessLevel](https://adapty.io/docs/zh/api-adapty/operations/grantAccessLevel.md)。
## 使用 AI 编程助手来构建 \{#build-it-with-your-ai-coding-agent\}
将本指南和 API 规范(在任意页面 URL 后加 `.md` 即可获取 Markdown 格式)提供给你的 AI 编程助手,告诉它你使用的技术栈,让它帮你编写调用代码:
- [OpenAPI 规范](https://adapty.io/docs/zh/api-specs/adapty-api.yaml)
- [getProfile](https://adapty.io/docs/zh/api-adapty/operations/getProfile.md)
- [grantAccessLevel](https://adapty.io/docs/zh/api-adapty/operations/grantAccessLevel.md)
示例提示词:
```
Using the Adapty server-side API spec, write backend functions to check whether a
user has an active "premium" access level (GET /profile/, derive status from
expires_at — there's no is_active field) and to grant it (grantAccessLevel).
Authenticate with ADAPTY_SECRET_KEY and identify users by adapty-customer-user-id.
```
The agent writes the code, but it can't run your backend or set your keys — you provide the secret key and the user identifiers.
## 限制 \{#limits\}
- **频率限制**:每个应用每分钟最多 40,000 次请求。
- **应用专属密钥**:每个密钥仅对应一个应用,请为每个应用使用对应的密钥。
- **必填标识符**:每个请求都需要提供 `adapty-customer-user-id` 或 `adapty-profile-id`。
---
# File: test-purchases-in-sandbox
---
---
title: "沙盒测试"
description: "在沙盒环境中测试购买流程,确保交易顺畅。"
---
在 Adapty 看板和移动应用中完成所有配置后,就可以开始进行应用内购买测试了。
**注意:** 任何测试工具都不会向用户收取实际费用。App Store 不会针对测试环境中的购买或退款发送邮件通知。
:::note
**沙盒交易不会显示在任何分析数据图表中。** 它们仍会出现在各个用户画像页面和事件流中。
:::
:::info
在进行应用内购买测试之前,请确认以下事项:
- 已完成[快速入门](quickstart)指南中关于商店集成、添加产品及 Adapty SDK 集成的步骤。
- 你的产品在 App Store Connect 中已标记为 [**Ready to submit**](InvalidProductIdentifiers#step-2-check-products)。
:::
## 沙盒测试 \{#sandbox-testing\}
2. 填写测试用户信息。请务必设置 **Country or Region**,因为这会影响该地区的产品可用性和购买货币。
:::tip
- 如果你使用 Gmail 或 iCloud,可以通过[加号子地址](https://www.wikihow.com/Use-Plus-Addressing-in-Gmail)复用现有邮箱地址。
- 你也可以使用一个根本不存在的随机邮箱地址,但请确保在测试设备上登录时拒绝双因素认证(2FA)。
:::
3. 点击 **Create**。
### 第二步:启用开发者模式 \{#step-2-enable-the-developer-mode\}
:::note
如果您的测试设备**已启用**开发者模式,或者您**没有 Mac 设备**,请跳过此步骤。
:::
您需要准备一台安装了 Xcode 的 Mac 以及测试设备的数据线:
1. 在 Mac 上打开 Xcode。如果您打算通过 TestFlight 测试应用内购买,只需确保已安装 Xcode 即可,不需要在其中打开任何项目。
2. 用数据线将测试设备连接到 Mac。
3. 在测试设备上进入 **Settings > Privacy & Security > Developer Mode**,然后开启**开发者模式**。
### 第三步:从 TestFlight 下载应用 \{#step-3-download-the-app-from-testflight\}
:::info
此步骤仅适用于通过 TestFlight 进行测试的情况。如果你在 Xcode 中直接构建应用,请跳过此步骤。
:::
有关如何将应用提交至 TestFlight 的详细信息,请参阅 [Apple 文档](https://developer.apple.com/documentation/StoreKit/testing-in-app-purchases-with-sandbox#Prepare-for-sandbox-testing)。
在下载 TestFlight 应用之前,请确保在测试设备上已使用你的正式 Apple 账号登录,然后从 TestFlight 下载要测试的应用。
:::danger
下载完成后请勿打开应用,直接进行后续步骤。
如果不小心打开了,请从测试设备上删除该应用并重新下载。否则,您的购买记录可能不干净,测试应用内购时会出现错误。
:::
### 第四步:切换到沙盒测试账号 \{#step-4-switch-to-sandbox-test-account\}
4. 向下滚动到 **Sandbox Apple Account** 部分,然后点击 **Sign In**。
5. 使用您的沙盒 Apple 账户凭据登录。
### 第 5 步:清除购买记录 \{#step-5-clear-purchase-history\}
如果你刚创建了一个新的沙盒测试账户并已切换到该账户,可以跳过此步骤,因为它仅适用于使用同一沙盒测试账户重复测试的情况。
1. 在测试设备上,前往 **Settings > Developer > Sandbox Apple Account**。
2. 从弹出菜单中选择 **Manage**。
3. 进入 **Account Settings**,然后点击 **Clear Purchase History**。
:::danger
每次使用同一个沙盒测试账号重复测试时,都需要执行此步骤。此时,你还需要[退出沙盒测试账号](#step-4-switch-to-sandbox-test-account),然后重新登录,以清除测试设备上的购买历史缓存。
:::
### 第六步:在 Xcode 中构建并运行 \{#step-6-build-in-xcode-and-run\}
:::info
此步骤仅适用于使用 Xcode 构建进行测试的情况。如果你使用的是 TestFlight,请跳过此步骤。
:::
1. 将测试设备连接到 Mac。
2. 打开 Xcode。
3. 点击工具栏中的 **Run**,或选择 **Product > Run**,将应用构建并运行到已连接的设备上。
构建成功后,Xcode 会在你的设备上启动应用,并在调试区域开启调试会话。
现在,你的应用已准备就绪,可以在设备上进行测试了。
### 第 7 步:进行测试购买 \{#step-7-make-test-purchase\}
打开应用,通过付费墙完成测试购买。
完成后,请前往[验证测试购买](validate-test-purchases)文章查看结果。
### 第八步:继续测试 \{#keep-testing\}
现在,您的测试环境已全部配置完成。如需再次测试,请[清除沙盒账号的购买记录](https://developer.apple.com/help/app-store-connect/test-in-app-purchases/manage-sandbox-apple-account-settings/)。
## 测试问题 \{#testing-issues\}
以下是测试应用时可能遇到的常见问题。
### TestFlight 问题 \{#testflight-issues\}
**如果你在 TestFlight 中未使用沙盒测试账号**,将无法清除购买记录,从而导致各种问题和错误的测试结果。
如果你不小心忘记[切换到沙盒测试账号](#step-4-switch-to-sandbox-test-account)就打开了应用,哪怕只打开过一次,TestFlight 也会将你的购买记录关联到正式 Apple 账号,进而引发意想不到的问题。
请按以下步骤解决:
1. 从测试设备上删除该应用。
2. 按照[沙盒测试](#sandbox-testing)的步骤进行操作。
:::note
不仅要重新安装应用,还需要切换到沙盒测试账户、清除购买记录,并使用沙盒测试账户启动应用。
:::
### 共享访问等级问题 \{#shared-access-levels-issues\}
如果你用同一个沙盒测试账号反复测试,可能会遇到测试用户的[共享访问等级](sharing-paid-access-between-user-accounts)出现异常行为的情况。
要检查用户是否继承了访问等级,请在 Adapty 看板中前往 [Profiles & Segments](https://app.adapty.io/profiles/users),然后打开该用户的用户画像。
如果用户拥有继承的访问等级,请按以下步骤操作以获得准确的测试结果:
1. 删除父用户画像。
2. 从测试设备上卸载应用。
3. [从 TestFlight 下载应用](#step-3-download-the-app-from-testflight)。
4. [切换到沙盒测试账号](#step-4-switch-to-sandbox-test-account)。
5. [清除购买记录](#step-5-clear-purchase-history)。
6. [打开应用并完成测试购买](#step-6-make-test-purchase)。
:::note
清除购买记录会重置商店端的购买历史。删除父级用户画像只会移除 Adapty 端的记录。若要了解为何重复使用的账户仍保留访问权限,以及哪些重置操作真正有效,请参阅[重置测试者的订阅](#resetting-a-testers-subscription)。
:::
### 在 TestFlight 中更新应用 \{#updating-app-in-testflight\}
如果 TestFlight 上的应用已更新:
1. 从测试设备上删除该应用。
2. [从 TestFlight 下载应用](#step-3-download-the-app-from-testflight)。
3. [切换到沙盒测试账号](#step-4-switch-to-sandbox-test-account)。
4. [清除购买记录](#step-5-clear-purchase-history)。
5. [打开应用并进行测试购买](#step-6-make-test-purchase)。
## 重置测试者的订阅 \{#resetting-a-testers-subscription\}
在沙盒环境中,购买记录归属于 **Apple 沙盒账号**,而非 Adapty 用户画像。对用户画像执行的操作(如删除或修改其访问等级)不会从 Store 账号中移除购买记录。下次重新安装或同步时,SDK 会重新关联相同的交易,测试者将再次获得访问权限。
下表列出了每种重置操作所影响的内容,以及测试者在此之后看到的结果。
| 操作 | Adapty 用户画像 | Apple 沙盒账号 | 测试者后续访问情况 |
| :--- | :--- | :--- | :--- |
| 在 Adapty 看板中删除用户画像 | 已删除 | 不受影响 | **恢复** — 重装后,新用户画像会重新关联相同的交易链 |
| 通过 [Delete profile API](api-adapty/operations/deleteProfile) 删除用户画像 | 已删除 | 不受影响 | **恢复** — 与在看板中删除效果相同 |
| 通过 **Add access level** 添加一个过去的到期日期 | 下次同步时被覆盖 | 不受影响 | **恢复** — 下次续订时,有效订阅会重新应用一个未来的到期日期 |
| 调用 [Revoke access level API](api-adapty/operations/revokeAccessLevel) | 立即到期,触发 `access_level_updated`(`is_active=false`) | 不受影响 | **恢复** — 下次续订或重装时恢复,不能作为可靠的沙盒重置手段 |
| 在沙盒账号中取消订阅 | 无直接变更 | 订阅已取消 | 续订停止,当前订阅期到期后失去访问权限,测试者可重新购买该产品 |
| 使用全新的 Apple 沙盒账号登录 | 新用户画像 | 全新的空账号 | **干净** — 推荐用于重复测试 |
### 将测试人员重置为干净状态 \{#reset-a-tester-to-a-clean-state\}
如需反复测试购买流程,建议每次使用全新的 Apple 沙盒账号,而不是重置用户画像。按照[第 1 步](#step-1-create-sandbox-test-account-in-app-store-connect)创建账号,再按照[第 4 步](#step-4-switch-to-sandbox-test-account)在设备上切换到该账号。如果要复用已有的沙盒账号,请先[清除其购买记录](#step-5-clear-purchase-history)——删除 Adapty 用户画像并不会清除购买记录。
### 撤销现有测试用户的访问权限 \{#remove-access-from-an-existing-tester\}
如需撤销测试用户的访问权限,请不要回溯修改过期时间,也不要调用 Revoke access level API。在沙盒环境中,订阅每隔几分钟就会自动续订,每次续订都会在同一交易链上恢复一个未来的过期时间,因此访问权限会自动恢复。Revoke access level API 确实会触发 `access_level_updated`(`is_active=false`)事件,但下一次续订会将其覆盖。
要真正停止访问权限,需要在商店侧取消订阅。在测试设备上,进入 **Settings > Developer > Sandbox Apple Account**,选择 **Manage**,然后取消订阅。续订将停止,访问权限将在当前订阅周期到期后终止。
### 为什么删除用户画像后访问权限又回来了 \{#why-deleting-the-profile-brings-access-back\}
当测试人员重新安装应用时,Adapty 会接收沙盒账户的购买记录,并将新安装与已有购买关联起来。购买记录绑定的是商店账户,而不是你删除的用户画像。
- **匿名用户画像**:在未设置 `customer_user_id` 的情况下重新安装,无论你的[付费访问共享](sharing-paid-access-between-user-accounts)设置如何,都会继承该商店账户的访问等级。
- **已识别用户画像**:访问等级是否会转移到新的 `customer_user_id`,取决于你的付费访问共享设置。
关于 Adapty 如何将这些用户画像关联成链,请参阅[用户画像的工作原理](how-profiles-work#parent-and-inheritor-profiles)。
## 测试订阅 \{#test-subscriptions\}
在使用沙盒测试账号测试应用时,你可以为每位测试人员单独设置沙盒中的订阅续订频率。详情请参阅 [Apple 官方文档](https://developer.apple.com/help/app-store-connect/test-in-app-purchases/manage-sandbox-apple-account-settings)中关于编辑订阅续订频率的说明。
默认情况下,订阅最多续订 12 次后停止,具体时间表如下:
| 订阅时长 | 1 周 | 1 个月 | 2 个月 | 3 个月 | 6 个月 | 1 年 |
| :----------------------------- | :--------- | :--------- | :--------- | :--------- | :--------- | :--------- |
| 订阅续订速度 | 3 分钟 | 5 分钟 | 10 分钟 | 15 分钟 | 30 分钟 | 1 小时 |
| 计费重试时长 | 10 分钟 | 10 分钟 | 10 分钟 | 10 分钟 | 10 分钟 | 10 分钟 |
| 计费宽限期时长 | 3 分钟 | 5 分钟 | 5 分钟 | 5 分钟 | 5 分钟 | 5 分钟 |
:::note
请注意,测试交易最多需要 10 分钟才能出现在[事件流](validate-test-purchases)中。
:::
使用沙盒来验证你的应用和后端能否正确处理续订、账单重试和宽限期——而不是用来预测生产环境的续订时间。上述加速且有上限的时间表与生产环境并不一致。如需在服务器上重放交易以进行后端测试,请使用 [Set transaction API](api-adapty/operations/setTransaction)。
## 测试优惠 \{#test-offers\}
测试优惠要求删除所有用户收据,以确保资格判断正常生效。
最可靠的测试方式是使用全新的[沙盒测试账号](#step-1-create-sandbox-test-account-in-app-store-connect)。使用同一个沙盒测试账号反复测试可能会导致意外行为。
:::danger
如果要使用同一个沙盒测试账号重复测试,请务必先[清除购买历史记录](#step-5-clear-purchase-history),以避免资格判断方面的问题。
:::
---
# File: local-sk-files
---
---
title: "在 Xcode 中进行 StoreKit 测试"
description: "在沙盒环境中测试购买流程,确保交易顺畅。"
---
在 Xcode 中进行 StoreKit 测试,无需设置沙盒账户即可在本地测试应用内购买。
进行此类测试,您需要:
1. [在 Adapty 中创建产品](quickstart-products)并为其分配 **App Store product ID**。
2. 在 Xcode 中,创建一个本地 [StoreKit 配置文件](https://developer.apple.com/documentation/xcode/setting-up-storekit-testing-in-xcode)并向其中添加产品。产品 ID 必须与 Adapty 中的 **App Store product ID** 相同。
3. 将 StoreKit 配置文件添加到您的构建方案并构建应用。在模拟器或设备上启动它。
## 我应该使用 Xcode 中的 StoreKit 测试吗?\{#should-i-use-storekit-testing-in-xcode\}
如果您是应用开发者,希望随时测试构建版本或使用 Xcode 功能测试不同的购买场景,这种测试方式最为方便。
但请注意,这种测试是本地进行的,因此不会在 Adapty 看板上显示任何变更。在将应用发布到生产环境之前,我们建议您在[沙盒环境](test-purchases-in-sandbox)中测试[用户画像相关功能](ios-quickstart-identify)。
**应该**使用 StoreKit 测试的场景:
- 测试购买逻辑
- 使用 Xcode 工具重现不同的购买场景(例如取消付款或退款)
- 使用模拟器进行测试
**不应该**使用 StoreKit 测试的场景:
- 测试用户画像相关逻辑
- 查看应用中的操作是否显示在 Adapty 看板中
- 与非开发团队共享应用进行测试
## 第一步:创建 StoreKit 配置文件\{#step-1-create-a-storekit-configuration-file\}
在 Xcode 中创建 StoreKit 配置文件:
1. 点击 **File > New > File from template**,然后选择 **StoreKit Configuration File** 并点击 **Next**。
2. 为文件命名。然后,根据您是否已在 App Store Connect 中创建了产品进行选择:
- 勾选 **Sync this file with an app in App Store Connect**:创建一个包含所有 App Store Connect 产品的配置文件,以便在本地测试。
- 不勾选 **Sync this file with an app in App Store Connect**:创建一个空配置文件,需手动添加产品。
点击 **Next**。
3. 不要将应用添加为目标,直接继续。如果您使用的是从 App Store Connect 同步的产品,请跳至[第二步](#step-2-add-the-configuration-file-to-the-build-scheme)。
4. 如果您的产品未从 App Store Connect 同步,点击左下角的 **+** 并选择产品类型。
5. 输入订阅组名称并点击 **Next**。
6. 输入参考名称。在 **Product ID** 字段中,输入您在 Adapty 中产品的 **App Store product ID**。
7. 在配置文件中配置定价、优惠及其他产品设置,或继续添加更多产品。
## 第二步:将配置文件添加到构建方案\{#step-2-add-the-configuration-file-to-the-build-scheme\}
要使用此配置文件构建应用,您需要将其添加到构建方案中。最佳实践是将测试方案与生产方案分开,因此我们建议为测试创建一个新方案:
1. 在顶部点击应用名称并选择 **New scheme**。
2. 输入方案名称并点击 **OK**。
3. 再次点击应用名称并选择 **Edit scheme**。在 **StoreKit configuration** 中,选择您的本地配置文件,这样构建时将使用该文件。
## 第三步:构建并测试\{#step-3-build--test\}
现在,您可以构建应用并测试应用内购买,无需连接到 App Store 后端。您可以在本地购买产品并获取访问等级。这些更改不会反映在 Adapty 看板中,但您仍可以在本地测试解锁付费功能。
[了解更多](https://developer.apple.com/documentation/xcode/testing-in-app-purchases-with-storekit-transaction-manager-in-code)关于在 Xcode 中进行 StoreKit 测试的其他可用功能。
---
# File: testing-on-android
---
---
title: "在 Google Play Store 中测试应用内购买"
description: "使用 Adapty 在 Android 上测试订阅购买。"
---
在将应用发布给用户之前,测试 Android 应用中的应用内购买(IAP)是至关重要的一步。沙盒测试是一种安全高效的方式,让你无需向用户实际收费即可测试 IAP。本指南将带你了解如何在 Google Play Store 上对 Android 应用进行沙盒测试。
:::note
**沙盒交易不会显示在任何分析数据图表中。** 它们仍会出现在各个用户画像页面和事件流中。
:::
## 测试环境 \{#testing-environment\}
为确保 Android 应用的最佳性能,建议您在真实设备上进行测试,而非使用模拟器。虽然我们已成功在模拟器上进行了测试,但 Google 建议使用真实设备。
如果您决定使用模拟器,请确保其已安装 Google Play,这有助于确保应用正常运行。
## 1. 为应用测试设置测试账号 \{#1-set-up-test-account-for-app-testing\}
为了在后续开发阶段方便测试,你需要为应用内购买测试设置一个测试用户。该用户将是你在 Android 测试设备上首次登录的账号。
请注意,Android 设备的主账号只能通过恢复出厂设置来更换,而这会清除所有数据。因此,务必提前正确配置好测试用户账号,以免日后不得不执行恢复出厂设置。
:::important
设置测试账号的方式取决于你使用的设备类型:
- 如果你有专用测试设备,请创建一个**独立测试账号(新 Gmail 账号)**。
- 如果没有专用测试设备,可以使用自己的**个人账号**,并为其临时开启**License testing**。
- 如果完全没有 Android 设备,可以**创建独立测试账号并在模拟器上使用**。但不推荐这种方式,因为它无法覆盖所有真机可能出现的问题。
:::
## 2. 启用许可证测试 \{#2-enable-license-testing\}
完成测试用户账号设置后,还需要为你的应用配置许可证测试。具体步骤如下:
1. 在 Google Play Console 侧边栏中,进入 **Settings**,然后在 **Monetization** 部分选择 **License testing**。
2. 选择一个已有的测试许可账号列表,或新建一个。
3. 将用于测试的账号添加到列表中并保存更改。如果团队成员也需要测试应用,可以将他们的邮箱一并添加到列表,这样整个团队都能获得访问权限。
## 3. 创建封闭测试轨道并添加测试账号 \{#3-create-closed-track-and-add-test-account-to-it\}
要开始测试,你需要将已签名的应用版本发布到封闭测试轨道:
1. 打开你的应用,在菜单中选择 **Test and release > Testing > Closed testing**,然后点击 **Create track**。
2. 输入封闭测试轨道名称,然后点击 **Create track**。
3. 向该轨道添加测试人员列表。
4. 在 **How testers join your test** 部分,复制链接并将其发送到已登录测试账号的设备。在测试设备上打开该链接,即可将该用户设置为测试人员。
:::warning
请注意以下几点,以确保测试顺利进行:
- 打开 opt-in URL 会将你的 Play 账号标记为测试账号。如果跳过此步骤,产品将无法加载。
- 开发者通常会为测试版本使用不同的应用 ID。这会导致问题,因为 Google Play Services 依赖应用 ID 来查找应用内购买项目。
- 在某些情况下,如果测试设备未设置 PIN 码,测试用户可能只能购买消耗型商品,而无法购买订阅。这种情况可能会出现一条含糊的"Something went wrong"错误提示。请确保测试设备已设置 PIN 码,并且已登录 Google Play Store。
:::
## 4. 上传已签名的 APK 到封闭测试轨道 \{#4-upload-a-signed-apk-to-the-closed-track\}
生成已签名的 APK,或使用 Android App Bundle,将已签名的 APK 上传到你刚创建的封闭测试轨道。你甚至不需要发布版本,只需上传 APK 即可。更多信息请参阅[此支持文章](https://support.google.com/googleplay/android-developer/answer/9859348?visit_id=638929100639477968-3849460621&rd=1)。
:::important
如果你的应用是新应用,可能需要先在你所在的国家或地区开放下载。请前往 **Testing > Closed testing**,点击你的测试轨道,然后进入 **Countries/regions** 添加所需的国家和地区。
:::
## 5. 测试应用内购买 \{#5-test-in-app-purchases\}
上传 APK 后,请等待几分钟以使版本完成处理。然后,在测试设备上使用您添加到测试人员列表的电子邮件账号登录。之后,您可以像在正式应用中一样测试应用内购买。
## 延伸阅读 \{#read-more\}
请阅读以下资源,了解有关在 Android 应用中测试应用内购买的更多信息:
- [沙盒中的续订周期](https://developer.android.com/google/play/billing/test#subs)
- [测试一次性购买](https://developer.android.com/google/play/billing/test#one-time)
---
# File: validate-test-purchases
---
---
title: "验证测试购买"
description: "在 Adapty 中验证测试购买,确保交易顺畅无误。"
---
在将移动应用发布到生产环境之前,全面测试应用内购买至关重要。请参阅我们的[在 Apple App Store 中测试应用内购买](test-purchases-in-sandbox)和[在 Google Play Store 中测试应用内购买](testing-on-android)主题,了解详细的测试指南。开始测试后,您需要验证测试购买是否成功。
每次在移动设备上完成测试购买后,请在 Adapty 看板的 [**Event Feed**](https://app.adapty.io/event-feed) 中查看对应的交易记录。如果购买未出现在 **Event Feed** 中,则说明 Adapty 未能追踪到该购买。
## 测试购买成功 \{#test-purchase-is-successful\}
如果测试购买成功,其交易事件将显示在 **Event Feed** 中:
如果交易按预期正常运行,请继续查看[发布检查清单](release-checklist),然后发布应用。
## 测试购买未成功 \{#test-purchase-is-not-successful\}
如果 10 分钟内未观察到任何交易事件,或在移动应用中遇到错误,请参阅[故障排除](troubleshooting-test-purchases)以及各平台的错误处理文章:[iOS](ios-sdk-error-handling)、[Android](android-sdk-error-handling)、[React Native](react-native-handle-errors)、[Flutter](error-handling-on-flutter-react-native-unity)、[Unity](unity-handle-errors) 和 [Kotlin Multiplatform](kmp-handle-errors),以寻找可能的解决方案。
---
# File: troubleshooting-test-purchases
---
---
title: "测试购买故障排查"
description: "在 Adapty 中排查测试购买问题并解决常见的应用内交易问题。"
---
如果您遇到交易问题,请首先确保您已完成[发布检查清单](release-checklist)中列出的所有步骤。如果您已完成所有步骤但仍遇到问题,请按照以下指南进行解决:
## 移动应用中返回错误 \{#an-error-is-returned-in-the-mobile-app\}
请参阅适用于您平台的错误列表:[iOS](ios-sdk-error-handling)、[Android](android-sdk-error-handling)、[React Native](react-native-troubleshoot-purchases)、[Flutter](error-handling-on-flutter-react-native-unity) 和 [Unity](unity-troubleshoot-purchases),并按照我们的建议解决问题。
## 事件流中没有交易记录,但移动应用中也未返回错误 \{#transaction-is-absent-from-the-event-feed-although-no-error-is-returned-in-the-mobile-app\}
要解决此问题,请检查以下几点:
1. **iOS 专属**:确保您使用的是真实设备而非模拟器。
2. 确保您的应用的 `Bundle ID`/`Package name` 与 [**App settings**](https://app.adapty.io/settings/general) 中的一致。
3. 确保您的应用中的 `PUBLIC_SDK_KEY` 与 Adapty 看板中的 **Public SDK key** 一致:[**App settings** -> **General** 标签页 -> **API keys** 子章节](https://app.adapty.io/settings/general)。
4. 确保您使用的是沙盒账户,而非[本地 StoreKit 配置文件](local-sk-files)。如果您之前使用过本地 StoreKit 配置文件进行测试,请确保当前构建版本中未使用该文件。
## 我的测试用户画像中没有事件 \{#no-event-is-present-in-my-testing-profile\}
这是正常行为。当以下情况发生时,Adapty 会自动创建新的用户画像记录:
- 用户首次运行您的应用时
- 用户退出您的应用时
**原因说明:** 所有交易和事件都与生成第一笔交易的用户画像绑定。这样可以将完整的交易历史记录(试用、购买、续订)关联到同一个用户画像。
**您将看到的情况:** 新的用户画像记录(称为"非原始用户画像")可能会出现但没有事件,但会保留访问等级。您可能会看到 `access_level_updated` 事件。这是预期行为。
**测试建议:** 为避免出现多个用户画像,每次重新安装应用时请创建新的测试账户(沙盒 Apple ID)。
更多详情请参阅[用户画像创建](how-profiles-work#profile-creation)。
以下是一个非原始用户画像的示例。请注意 **User history** 中没有事件,但存在访问等级。
## 价格与 App Store Connect 中设置的实际价格不符 \{#prices-do-not-reflect-the-actual-prices-set-in-app-store-connect\}
在沙盒环境和使用沙盒环境进行应用内购买的 TestFlight 中,重要的是验证购买流程是否正常运行,而不是关注价格的准确性。值得注意的是,Apple 的 API 偶尔会提供不准确的数据,尤其是当设备或账户配置了不同地区时。由于价格直接来自商店,Adapty 后端不会以任何方式影响购买价格,因此在通过 Adapty 测试购买期间,您可以忽略价格上的任何不准确之处。
因此,请优先测试购买流程本身,而非价格的准确性,以确保其按预期运行。
## 事件流中的交易时间不正确 \{#the-transaction-time-in-the-event-feed-is-incorrect\}
**Event Feed** 使用的是 **App Settings** 中设置的时区。要使事件时区与您的本地时间一致,请在 [**App settings** -> **General** 标签页](https://app.adapty.io/settings/general) 中调整 **Reporting timezone**。
## 付费墙和产品加载时间过长 \{#paywalls-and-products-take-a-long-time-to-load\}
如果您的测试账户有较长的交易历史记录,可能会出现此问题。我们强烈建议每次都创建新的测试账户,具体步骤请参阅[在 App Store Connect 中创建沙盒测试账户(沙盒 Apple ID)](test-purchases-in-sandbox#step-1-create-sandbox-test-account-in-app-store-connect)章节。
如果您无法创建新账户,可以按照以下步骤在 iOS 设备上清除当前账户的交易历史记录:
1. 打开**设置**,点击 **App Store**。
2. 点击您的 **Sandbox Apple ID**。
3. 在弹出窗口中,选择 **Manage**。
4. 在 **Account Settings** 页面,点击 **Clear Purchase History**。
更多详情,请查阅 [Apple 开发者文档](https://developer.apple.com/documentation/storekit/testing-in-app-purchases-with-sandbox)。
---
# File: test-devices
---
---
title: "测试设备"
description: "了解如何在 Adapty 中管理测试设备,以便高效地进行应用测试。"
---
出于测试目的,您可以将设备指定为测试设备,这将禁用缓存并确保您的更改立即生效。
:::note
测试设备支持从以下特定 SDK 版本开始:
- iOS: 2.11.1
- Android: 2.11.3
- React Native: 2.11.1
Flutter 和 Unity 的支持将在稍后添加。
:::
## 将您的设备标记为测试设备 \{#mark-your-device-as-test\}
1. 在 Adapty 看板中打开 [**App settings**](https://app.adapty.io/settings/general)。
2. 在 **General** 选项卡中向下滚动至 **Test devices** 部分。
3. 点击 **Add test device** 按钮。
4. 在 **Add test device** 窗口中,输入:
| 字段 | 说明 |
|:-----------------------------------------| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Test device name** | 测试设备的名称,供您参考。 |
| **ID used to identify this test device** | 选择用于标识测试设备的标识符类型。请参阅下方[应使用哪种标识符](test-devices#which-identifier-you-should-use)部分中的建议,选择最佳选项。 |
| **ID value** | 输入标识符的值。 |
5. 请记得点击 **Add test device** 按钮以保存更改。
## 应使用哪种标识符 \{#which-identifier-you-should-use\}
要标识设备,您可以使用多种标识符。我们推荐以下方式:
- **Customer User ID**:适用于 iOS 和 Android 设备,前提是您已在 Adapty 中由您设置的唯一标识符,用于在您的系统中识别用户。可以是用户的电子邮件、您的内部 ID 或任何其他字符串。要使用此选项,您必须在 Adapty 中
这是标识测试设备的最佳选择,尤其是当同一账户使用多个设备时。该账户下的所有设备都将被视为测试设备。
| | Adapty profile ID |Adapty 中[用户画像](profiles-crm)的唯一标识符。
如果无法使用 Customer User ID、iOS 的 IDFA 或 Android 的 Advertising ID,则可使用此选项。请注意,Adapty Profile ID 在重新安装应用或重新登录后可能会发生变化。
| #### 如何获取 Customer User ID 和 Adapty profile ID \{#how-to-obtain-customer-user-id-and-adapty-profile-id\} 两种标识符均可在 Adapty 看板的**用户画像**详情中获取: 1. 在 [**Adapty Profiles** -> **Event feed** 选项卡](https://app.adapty.io/event-feed)中找到用户的用户画像。 :::note 要找到确切的用户画像,请进行一次不常见类型的交易。这样,一旦该交易出现在 [**Event Feed**](https://app.adapty.io/event-feed) 中,您就能轻松识别它。 ::: 2. 在用户画像详情中复制 **Customer user ID** 和 **Adapty ID** 字段的值:
### Apple 标识符 \{#apple-identifiers\}
| 标识符 | 用途 |
|----------|-----|
| IDFA | 广告标识符(IDFA)是 Apple 分配给用户设备的唯一设备标识符。
它非常适合 iOS 设备,因为它不会自行改变,但您可以手动重置。
**注意**:自 iOS 14.5 推出以来,广告商必须请求用户同意才能访问 IDFA。请确保您的应用已请求同意,并且您已在测试设备上授予同意。
| | IDFV | 供应商标识符(IDFV)是 Apple 为同一发布商/供应商在单一设备上的所有应用分配的唯一字母数字标识符。如果您重新安装或更新应用,它可能会发生变化。 | #### 如何获取 IDFA \{#how-to-obtain-the-idfa\} Apple 默认不提供 IDFA。请从 Adapty 看板的用户画像归因中获取: 1. 在 [**Adapty Profiles** -> **Event feed** 选项卡](https://app.adapty.io/event-feed)中找到用户的用户画像。 :::note 要找到确切的用户画像,请进行一次不常见类型的交易。这样,一旦该交易出现在 [**Event Feed**](https://app.adapty.io/event-feed) 中,您就能轻松识别它。 ::: 2. 打开用户画像详情,在 **Attributes** 部分复制 **IDFA** 字段的值:
您也可以[在 App Store 上找到能够显示您的 IDFA 的应用](https://www.apple.com/us/search/idfa?src=globalnav)。
#### 如何获取供应商标识符(IDFV) \{#how-to-obtain-the-identifier-for-vendors-idfv\}
要获取 IDFV,请让您的开发人员在您的应用中使用以下方法请求并将收到的标识符显示在日志或调试面板中。
```swift showLineNumbers title="Swift"
UIDevice.current.identifierForVendor
```
### Google 标识符 \{#google-identifiers\}
| 标识符 | 用途 |
|----------|-----|
| Advertising ID | 广告 ID 是 Google 分配给用户设备的唯一设备标识符。
它非常适合 Android 设备,因为它不会自行改变,但您可以手动重置。
**注意**:要使用它,如果您使用的是 Android 12 或更高版本,请在 **Ads** 设置中关闭 **Opt out of Ads Personalization**。
| | Android ID | Android ID 是每个应用签名密钥、用户和设备组合的唯一标识符。在 Android 8.0 及更高版本上可用。 | #### 如何获取 Advertising ID \{#how-to-obtain-advertising-id\} 要查找您设备的广告 ID: 1. 在 Android 设备上打开 **Settings** 应用。 2. 点击 **Google**。 3. 在 **Services** 下选择 **Ads**。您的广告 ID 将显示在屏幕底部。 #### 如何获取 Android ID \{#how-to-obtain-android-id\} 要获取 Android ID,请让您的开发人员在您的应用中使用以下方法请求 [ANDROID_ID](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID),并将收到的标识符显示在日志或调试面板中。 ```kotlin showLineNumbers title="Kotlin/Java" android.provider.Settings.Secure.getString(contentResolver, android.provider.Settings.Secure.ANDROID_ID); ``` --- # File: release-checklist --- --- title: "发布检查清单" description: "遵循 Adapty 的发布检查清单,确保应用更新过程顺畅无误。" --- 我们非常高兴您决定使用 Adapty!希望集成过程一切顺利。本指南将引导您完成确保应用准备好在商店发布所需的各个步骤,让您确信变现流程运行正常。 ## 起飞前必备事项 \{#pre-flight-essentials\} 开始验证前您需要准备: - 一台配置了沙盒账号的真实设备 - 访问 Adapty 看板的权限 - 访问 App Store Connect / Google Play Console 的权限 :::note 虽然沙盒购买可以在模拟器上运行,但要完整测试所有流程(包括支付对话框和生物识别提示),仍需要真实设备。 ::: ## 通用验证 \{#universal-validations\} - [ ] **商店连接**:确保已将 Adapty 连接至 App Store 和/或 Google Play: - [ ] [App Store](initial_ios) - [ ] [Google Play](initial-android) - [ ] **订阅事件推送**:确认服务器通知已配置: - [ ] [App Store 服务器通知](enable-app-store-server-notifications) - [ ] [实时开发者通知(RTDN)](enable-real-time-developer-notifications-rtdn) - [ ] **用户画像识别**:验证用户识别逻辑,确保购买记录关联到正确的用户画像: - [ ] [检查应用代码中的识别逻辑是否符合你的使用场景](ios-quickstart-identify) - [ ] [了解用于在用户画像之间共享付费访问权限的父级/继承逻辑](sharing-paid-access-between-user-accounts) - [ ] **优惠活动**:如果应用中包含 App Store 促销活动,请确保已将内购密钥[添加到主字段和 **App Store promotional offers** 部分](app-store-connection-configuration#step-4-for-trials-and-special-offers--set-up-promotional-offers)。 - [ ] **数据收集**:确保符合隐私合规要求: - [ ] 如需遵守 GDPR、CCPA 等隐私法规,或应用面向儿童用户,请控制是否[启用 IDFA 和 IP 的收集与共享](sdk-installation-ios#data-policies)。 - [ ] 如果应用使用了 AppTrackingTransparency,请确保已[将授权状态发送给 Adapty](ios-deal-with-att)。 - [ ] **隐私标签**:[了解更多](apple-app-privacy) Adapty 收集的数据,以及审核时需要设置哪些标志。 ## 购买验证 \{#purchase-validations\} :::tip 有疑问或遇到问题?欢迎访问我们的[支持论坛](https://adapty.featurebase.app/),在那里你可以找到常见问题的解答,也可以提出自己的问题。我们的团队和社区随时为你提供帮助! ::: 在正式上线之前,请确保应用内购买功能正常运行,且付费墙已准备好通过应用商店审核。 验证应用内购买的方式取决于你的具体实现方案: - 你展示的是通过 Adapty 付费墙编辑工具创建的付费墙 - 你实现了自定义付费墙,并在其中使用 `makePurchase` 方法处理购买 - 你以观察者模式使用 Adapty(无论是配合付费墙编辑工具还是自定义付费墙)
2. 从顶部菜单栏选择 **Product** > **Archive**。
3. 等待归档过程完成。**Organizer** 窗口会自动打开。选择您的归档文件,然后点击 **Distribute App**。
4. 选择 **App Store Connect** 作为分发方式。按照提示完成上传。
:::note
如果缺少必要资源(例如应用图标或启动界面),上传可能会失败。请查看 Xcode 错误日志了解详细信息。
:::
### 步骤 2:在 App Store Connect 中检查构建版本 \{#step-2-check-the-build-in-app-store-connect\}
1. 前往 [App Store Connect](https://appstoreconnect.apple.com) 并打开您的应用。
2. 滚动到 **Build** 部分。确认您刚刚上传的构建版本已显示在此处。
:::note
上传后,构建版本可能需要几分钟才能出现在 App Store Connect 中。
:::
## 提交应用和产品以供审核 \{#submit-your-app-and-products-for-review\}
构建版本出现在 **Build** 部分后,请附加您的应用内订阅并将应用提交给苹果审核。
### 步骤 1:将产品附加到提交内容 \{#step-1-attach-products-to-the-submission\}
在附加订阅之前,每个订阅在 App Store Connect 中必须具有 **Ready to Submit** 状态。如果订阅仍处于草稿状态或缺少元数据,它将不会出现在列表中。
1. 在同一页面上,滚动到 **In-App Purchases and Subscriptions** 部分。
2. 点击 **Select in-app purchases or subscriptions**。
3. 选择您想要包含在此次提交中的所有产品,然后点击 **Done**。
### 步骤 2:提交审核 \{#step-2-submit-for-review\}
1. 填写页面上所有必填字段(描述、截图、关键词等)。
2. 在 **App Store Version Release** 部分,选择在应用获批后是自动发布、手动发布还是按计划发布。
3. 点击 **Add for Review**,然后点击 **Submit to App Review**。
苹果通常在 1–2 天内完成审核,但审核时间可能有所不同。
## 在生产环境中验证您的应用 \{#verify-your-app-in-production\}
苹果批准您的应用后:
1. 进行一笔真实购买(或等待您的第一位用户购买)。
2. 在 Adapty 看板中打开 [**Event Feed**](https://app.adapty.io/event-feed),确认生产环境中的交易事件已出现。
3. 检查订阅事件(续订、取消)是否正常流转——这取决于是否配置了 [App Store 服务器通知](enable-app-store-server-notifications)。
如果生产环境事件未出现,请验证您的 [App Store 连接配置](app-store-connection-configuration)。
## 后续步骤 \{#next-steps\}
您的应用已上线。开始增加您的订阅收入:
- **[A/B 测试](ab-tests)**:尝试不同的付费墙,找出转化率最高的方案。
- **[数据分析](charts)**:跟踪 MRR、流失率和转化率等订阅数据图表。
- **集成**:将订阅事件发送到[数据分析](analytics-integration)和[归因](attribution-integration)平台。
---
# File: general
---
---
title: "应用设置"
description: "探索 Adapty 中的常规设置与配置,实现顺畅使用。"
---
您可以前往 App Settings 页面的 **General** 标签页,管理应用的行为、外观和收益分成。在这里,您可以自定义应用名称和图标、管理 Adapty SDK 和 API 密钥、设置小型企业计划状态,以及为应用的分析和数据图表选择时区。
## 1. 应用详情 \{#1-app-details\}
为您的应用选择一个独特的名称和图标,以便在 Adapty 界面中识别。请注意,此处设置的应用名称和图标不会影响该应用在 App Store 或 Google Play 中显示的名称和图标。此外,请务必选择一个准确反映应用用途和内容的**应用分类**,这有助于用户发现您的应用,并确保其出现在应用商店的相应分类中。
## 2\. 小企业计划成员与降低服务费 \{#member-of-small-business-program-and-reduced-service-fee\}
如果您的组织已加入 Apple 的[小企业计划](app-store-small-business-program)或 Google 的[降低服务费计划](google-reduced-service-fee),您的应用将享受较低的应用商店佣金比例。
如果您的应用加入了佣金减免计划,请在"Reduced Store Fee"部分注明相关状态,以确保 Adapty 正确计算数据。
减免费率设置仅对未来的交易生效。请在生效**前**更新状态,Adapty 将自动调整佣金比例。
:::warning
* 如果您延续了减免费率计划的参与资格,请**新增一个资格有效期**。
* 如果您失去了计划资格,请**修改当前有效期的到期日期**。
:::
以下文章对此主题进行了深入探讨:
* [App Store 小型企业计划](app-store-small-business-program)
* [Google 降低服务费](google-reduced-service-fee)
## 3\. 报告时区 \{#reporting-timezone\}
选择与您所在地区或应用数据分析最相关地区对应的时区。我们建议使用与您的 App Store Connect 或 Google Play Console 账户相同的时区,以确保数据一致性。请注意,此时区设置不会影响 Adapty 系统中的第三方集成,这些集成使用 UTC 时区。
您可以在 **App Settings** 页面 **General** 标签页的 **Reported timezone** 部分访问时区设置。您也可以勾选相应的复选框,为 Adapty 账户中的所有应用设置统一的时区。
## 4\. 分析中的安装定义 \{#4-installs-definition-for-analytics\}
选择在分析中将什么定义为新安装事件:
| 基准 | 说明 |
|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 新增 device_ids | (推荐)用户在设备上从应用商店每安装一次应用,均计为一次新增安装,包括首次安装和重新安装。
安装次数按设备 ID 统计,与用户认证状态无关。创建用户画像(在 SDK 激活或退出登录时)、登录或升级应用均不会产生额外的安装事件。
例如,同一应用安装在 5 台不同设备上,分析数据中将显示 5 次安装。
| | 新增 customer_user_ids |此选项适用于在 Adapty 中
对于已登录用户,只有与某个 customer user ID 关联的首次安装才计为一次安装,在其他设备上的安装不计为新增安装。
匿名用户(未登录的用户)不计入分析数据。
重新安装应用或再次登录不会产生额外的安装记录。
应用商店和归因平台(如 App Store Connect、Google Play Console 和 AppsFlyer)均采用基于设备的方式统计安装量。若您在 Adapty 中按 customer user ID 统计安装量,结果可能与这些外部服务存在差异。
⚠️ 如果您未在 Adapty 中识别用户,启用此选项后将不会统计任何安装量。
| | Adapty 中的新用户画像(旧版) | (旧版)每次应用安装、重新安装,以及退出登录时创建的匿名用户画像,均计为新增安装。 | 请注意,此选项仅影响 [**Analytics**](https://app.adapty.io/analytics) 页面,不影响 [**Overview**](https://app.adapty.io/overview) 页面——后者可以单独配置视图。 ## 5. App Store 价格上涨逻辑 \{#5-app-store-price-increase-logic\} 为了保持数据准确、避免 Adapty 数据分析与 App Store Connect 结果出现偏差,在 App Store Connect 中调整价格上涨相关配置时,请务必选择合适的选项。 你可以选择 Adapty 处理订阅价格上涨时所采用的逻辑:
- **为现有用户保留订阅价格:** 选择此选项后,即使您在 App Store Connect 中修改了价格,现有订阅者仍将按原价计费。
- **在 App Store Connect 中修改订阅价格后,现有订阅者同步更新:** 选择此选项后,在 App Store Connect 中所做的任何价格调整都将同步应用于现有订阅者,即现有订阅者将按 App Store Connect 中的最新价格计费。
:::warning
请注意,所选选项不仅会影响 Adapty 中的数据分析,还会影响集成功能和整体交易处理行为。
:::
请确保选择与您处理现有订阅者订阅价格方式相符的选项。这有助于确保 Adapty 数据分析与 App Store Connect 结果之间的数据准确性和同步性。
## 6. 在用户账户之间共享付费访问权限 \{#6-sharing-paid-access-between-user-accounts\}
:::link
主要文章:[在用户账户之间共享付费访问权限](sharing-paid-access-between-user-accounts)
:::
**Sharing paid access between user accounts** 设置决定了当多个[用户画像](identifying-users)尝试访问同一购买时 Adapty 的处理方式。您可以为[沙盒环境](test-purchases-in-sandbox)单独指定访问共享设置。
**已启用(默认)**
已识别用户(即设置了 [Customer User ID](identifying-users#set-customer-user-id-on-configuration) 的用户)在设备登录相同 Apple/Google ID 的情况下,可以共享 Adapty 提供的同一[访问等级](access-level)。这在用户重新安装应用并使用不同邮箱登录时非常有用——他们仍然可以访问之前的购买内容。使用此选项时,多个已识别用户可以共享同一访问等级。
尽管访问等级是共享的,但所有过去和未来的交易都会作为事件记录在原始 Customer User ID 下,以保持数据一致性并完整保留交易历史——包括试用期、订阅购买、续订等,均关联到同一用户画像。
**将访问权限转移给新用户**
已识别用户可以继续访问 Adapty 提供的[访问等级](access-level),即使他们使用不同的 [Customer User ID](identifying-users#set-customer-user-id-on-configuration) 登录或重新安装应用,只要设备登录的是相同的 Apple/Google ID 即可。
与上一选项不同,Adapty 会在已识别用户之间转移购买记录。这确保购买内容始终可用,但同一时间只有一个用户能拥有访问权限。例如,如果 UserA 购买了订阅,而 UserB 在同一设备上登录并恢复了交易,则 UserB 将获得该订阅的访问权限,UserA 的访问权限将被撤销。
如果其中一个用户(无论新用户还是旧用户)未被识别,Adapty 中这些用户画像之间的访问等级仍会共享。
尽管访问等级会被转移,但所有过去和未来的交易都会作为事件记录在原始 Customer User ID 下,以保持数据一致性并完整保留交易历史——包括试用期、订阅购买、续订等,均关联到同一用户画像。
切换到**将访问权限转移给新用户**后,用户画像之间的访问等级不会立即转移。每个特定访问等级的转移流程仅在 Adapty 收到来自商店的事件时触发,例如订阅续订、恢复购买或验证交易时。
**已禁用**
第一个获得访问等级的已识别用户画像将永久保留该访问等级。如果你的业务逻辑要求购买记录必须绑定到单个 Customer User ID,这是最佳选项。
请注意,访问等级在匿名用户之间仍会共享。
你可以通过[删除所有者的用户画像](https://adapty.io/docs/zh/api-adapty/operations/deleteProfile)来"解绑"购买记录。删除后,访问等级将归属于第一个声明它的用户画像,无论是匿名用户还是已识别用户。
禁用共享仅影响新用户。已在用户之间共享的订阅在禁用此选项后仍会继续共享。
:::warning
Apple 和 Google 要求在用户之间共享或转移应用内购买,因为这些购买是依赖 Apple/Google ID 进行关联的。如果不启用共享,用户在重新安装应用后可能无法恢复购买。
禁用共享可能导致用户登录后无法重新获得访问权限。
我们建议仅在用户**必须先登录**才能进行购买的情况下禁用共享。否则,已识别用户可能在购买订阅后登录另一个账号,从而永久失去访问权限。
:::
### 应该选择哪个设置?\{#which-setting-should-i-choose\}
| 我的应用…… | 推荐选项 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 没有登录系统,仅使用 Adapty 的匿名用户画像 ID。 | 使用默认选项,因为对于所有三个选项,匿名用户画像 ID 之间的访问等级始终是共享的。 |
| 有可选登录系统,允许用户在创建账号之前进行购买。 | 选择**将访问权限转移给新用户**,确保未登录账号就完成购买的用户之后仍能恢复交易。 |
| 要求用户在购买前创建账号,但允许购买记录关联到多个 Customer User ID。 | 选择**将访问权限转移给新用户**,确保同一时间只有一个 Customer User ID 拥有访问权限,同时允许用户使用不同 Customer User ID 登录而不丢失已付费的访问权限。 |
| 要求用户在购买前创建账号,并严格规定购买记录只能绑定到单个 Customer User ID。 | 选择**已禁用**,确保交易记录永远不会在账号之间转移。 |
## 7. SDK 和 API 密钥 \{#7-sdk-and-api-keys\}
使用 Public SDK key 将 Adapty SDK 集成到您的应用中,使用 Secret Key 访问 Adapty 的 Server API。您可以根据需要生成新密钥或撤销现有密钥。要为 Developer CLI 创建令牌,请前往 **Settings → Developer API**。请参阅[身份验证](developer-cli-authentication)。
## 8. 测试设备 \{#8-test-devices\}
指定用于测试的设备,确保它们能够即时获取付费墙或版位更改的更新,绕过任何缓存延迟。更多信息,请参阅[测试设备](test-devices)。
## 9. 跨版位实验变体固定时长 \{#cross-placement-variation-stickiness\}
定义测试结束后,用户继续看到测试中实验变体的时长。这会影响数据分析的准确性和用户体验——如果向用户展示与之前不同的优惠,可能会影响他们的购买决策。
最大固定时长(也是默认值)为 90 天。
:::warning
请注意以下几点:
- 修改此设置会影响所有之前已被分配到某个实验变体的用户。这些用户在下次触达版位时将立即获得新的付费墙,这可能会干扰正在进行的 A/B 测试结果。
- 如果某个用户的粘性期已结束,他们可能会看到新的付费墙或 A/B 测试。但即便如此,他们也永远无法再参与任何其他跨版位测试。
:::
## 10. 删除应用 \{#delete-the-app\}
如果某个应用不再需要,可以将其从 Adapty 中删除。
:::warning
请注意,此操作不可撤销,删除后将无法恢复该应用及其数据。
:::
---
# File: ios-settings
---
---
title: "Apple App Store 凭据"
description: "在 Adapty 中配置 iOS 设置,以实现无缝的订阅管理。"
---
要配置 App Store 凭据并确保 Adapty iOS SDK 的最佳功能,请导航至 Adapty 看板 App Settings 页面中的 [iOS SDK](https://app.adapty.io/settings/ios-sdk) 选项卡,然后配置以下参数:
| 字段 | 描述 |
|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Bundle ID** | 您的[应用 Bundle ID](app-store-connection-configuration#step-1-provide-bundle-id-and-apple-app-id)。 |
| **In-app purchase API (StoreKit 2)** | 用于启用应用内购买交易历史记录请求的安全身份验证和验证的[密钥](app-store-connection-configuration#step-2-provide-issuer-id-and-key-id)。 |
| **App Store Server Notifications** | 用于启用 App Store 向 Adapty 发送[服务器到服务器通知](enable-app-store-server-notifications)的 URL,以便监控和响应用户订阅状态变更。 |
| **App Store Promotional Offers** | 用于在 Adapty 中为特定产品创建[促销活动](generate-in-app-purchase-key)的订阅密钥。 |
| **Apple app ID** | 您在 App Store 中的应用 ID。查找方式:在 App Store Connect 中打开您的应用页面,从左侧菜单进入 **App Information** 页面,复制 **Apple ID**。 |
| **App Store Connect shared secret (LEGACY)** | **适用于 Adapty SDK v2.9.0 之前版本的旧版密钥**
用于收据验证和防止应用内欺诈的[密钥](app-store-connection-configuration#step-5-enter-app-store-shared-secret)。
| --- # File: google-play-store-connection-configuration --- --- title: "配置 Google Play 商店集成" description: "在 Adapty 中配置 Google Play 商店连接,以顺畅处理应用内购买。" --- 本节介绍通过 Google Play 销售的移动应用与 Adapty 的集成流程。您需要将应用在 Play 商店中的配置数据填写到 Adapty 看板中。此步骤对于在 Adapty 中验证购买及接收来自 Play 商店的订阅更新至关重要。 您可以在初始用户引导期间完成此流程,也可以稍后在 Adapty 看板的 **App Settings** 中进行修改。 :::danger 配置更改仅应在您发布集成了 Adapty 付费墙的移动应用之前进行。发布后进行更改将导致集成中断,付费墙将无法在您的移动应用中显示。 ::: ## 步骤 1. 提供包名 \{#step-1-provide-package-name\} 包名是您的应用在 Google Play 商店中的唯一标识符。这是 Adapty 基本功能(如订阅处理)所必需的。 1. 打开 [Google Play 开发者控制台](https://play.google.com/console/u/0/developers)。 2. 选择您需要获取 ID 的应用,**Dashboard** 窗口将会打开。
3. 在应用名称下方找到产品 ID 并复制。
4. 从 Adapty 顶部菜单打开 [**App settings**](https://app.adapty.io/settings/android-sdk)。
5. 在 **App settings** 窗口的 **Android SDK** 标签页中,粘贴已复制的 **Package name**。
## 步骤 2. 上传账号密钥文件 \{#step-2-upload-the-account-key-file\}
1. 将您在[创建服务账号密钥文件](create-service-account)步骤中创建的 JSON 格式服务账号私钥文件上传到 **Service account key file** 区域。
请不要忘记点击 **Save** 按钮以确认更改。
**下一步**
- [在 Google Play 控制台中启用实时开发者通知(RTDN)](enable-real-time-developer-notifications-rtdn)
---
# File: enable-real-time-developer-notifications-rtdn
---
---
title: "在 Google Play Console 中启用实时开发者通知 (RTDN)"
description: "通过在 Google Play Console 中为 Adapty 启用实时开发者通知 (RTDN),及时了解关键事件并保持数据准确性。了解如何设置 RTDN 以接收来自 Play Store 的退款及其他重要事件的即时更新"
---
设置实时开发者通知 (RTDN) 对于确保数据准确性至关重要,它能让您即时接收来自 Play Store 的更新,包括退款及其他事件的信息。
## 启用通知 \{#enable-notifications\}
1. 确保已启用 **Google Cloud Pub/Sub**。打开[此链接](https://console.cloud.google.com/flows/enableapi?apiid=pubsub)并选择您的应用项目。如果尚未启用 **Google Cloud Pub/Sub**,请在此处启用。
2. 从 Adapty 顶部菜单进入 [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk),复制 **Google Play RTDN topic name** 标题旁 **Enable Pub/Sub API** 字段的内容。
:::note 如果 **Enable Pub/Sub API** 字段的内容格式不正确(正确格式以 `projects/...` 开头),请参阅[修复 Enable Pub/Sub API 字段格式错误](enable-real-time-developer-notifications-rtdn#fixing-incorrect-format-in-enable-pubsub-api-field)部分获取帮助。 ::: 3. 打开 [Google Play Console](https://play.google.com/console/),选择您的应用,然后前往 **Monetize with Play** -> **Monetization setup**。在 **Google Play Billing** 部分,勾选 **Enable real-time notifications** 复选框。 4. 将您在 Adapty **App Settings** 中复制的 **Enable Pub/Sub API** 字段内容粘贴到 **Topic name** 字段中。 5. 在 Google Play Console 中点击 **Save changes**。
## 测试通知 \{#test-notifications\}
要验证您是否已成功订阅实时开发者通知:
1. 在 Google Play Console 设置中保存更改。
2. 在 Google Play Console 的 **Topic name** 下方,点击 **Send test notification**。
3. 在 Adapty 中进入 [**App settings > Android SDK**](https://app.adapty.io/settings/android-sdk)。如果测试通知已发送,您将在主题名称上方看到其状态。
## 修复 Enable Pub/Sub API 字段格式错误 \{#fixing-incorrect-format-in-enable-pubsub-api-field\}
如果 **Enable Pub/Sub API** 字段的内容格式不正确(正确格式以 `projects/...` 开头),请按以下步骤排查并解决问题:
### 1. 验证 API 启用状态与权限 \{#1-verify-api-enablement-and-permissions\}
请仔细确认所有必需的 API 已启用,且权限已正确授予服务账号。即使您已完成这些步骤,也请再次逐一核查,确保没有遗漏任何子步骤。请重复以下各节中的步骤:
1. [在 Google Play Console 中启用开发者 API](enabling-of-devepoler-api)
2. [在 Google Cloud Console 中创建服务账号](create-service-account)
3. [在 Google Play Console 中授予服务账号权限](grant-permissions-to-service-account)
4. [在 Google Play Console 中生成服务账号密钥文件](create-service-account-key-file)
5. [配置 Google Play Store 集成](google-play-store-connection-configuration)
### 2. 调整域策略 \{#2-adjust-domain-policies\}
更改 **Domain restricted contacts** 和 **Domain restricted sharing** 策略:
1. 打开 [Google Cloud Console](https://console.cloud.google.com/),选择您用于管理应用的服务账号所在的项目。
2. 在 **Quick Access** 部分,选择 **IAM & Admin**。
3. 在左侧面板中,选择 **Organization Policies**。
4. 找到 **Domain restricted contacts** 策略。
5. 点击 **Actions** 列中的省略号按钮,选择 **Edit policy**。
6. 在策略编辑窗口中:
1. 在 **Policy source** 下,选择 **Override parent's policy** 单选按钮。
2. 在 **Policy enforcement** 下,选择 **Replace** 单选按钮。
3. 在 **Rules** 下,点击 **ADD A RULE** 按钮。
4. 在 **New rule** -> **Policy values** 下,选择 **Allow All**。
5. 点击 **SET POLICY**。
7. 对 **Domain restricted sharing** 策略重复步骤 4-6。
最后,重新生成 **Google Play RTDN topic name** 标题旁 **Enable Pub/Sub API** 字段的内容。该字段现在将显示正确的格式。
成功启用实时开发者通知 (RTDN) 后,请务必将已更新策略的 **Policy source** 切换回 **Inherit parent's policy**。
## 原始事件转发 \{#raw-events-forwarding\}
有时,您可能仍希望接收来自 Google 的原始 S2S 事件。如需在使用 Adapty 的同时继续接收这些事件,只需将您的端点添加到 **URL for forwarding raw Google events** 字段,我们将原样转发来自 Google 的原始事件。
---
**下一步**
为以下平台配置 Adapty SDK:
- [Android](sdk-installation-android)
- [React Native](sdk-installation-reactnative)
- [Flutter](sdk-installation-flutter)
- [Kotlin Multiplatform](sdk-installation-kotlin-multiplatform)
- [Unity](sdk-installation-unity)
---
# File: apple-search-ads
---
---
title: "Apple Ads"
description: "将 Apple Ads 与 Adapty 集成,优化订阅转化率。"
---
:::important
**App settings** 中的 Apple Ads 集成仅用于基础分析以及 SplitMetrics Acquire 和 Asapty 集成。
[Adapty Ads Manager](adapty-ads-manager) 使用单独的连接方式。请在 [Adapty Ads Manager](adapty-ads-manager-get-started) 中连接您的 Apple Ads 账户。
:::
Adapty 可以帮助您获取 Apple Ads 的归因数据,并通过广告系列和关键词细分来分析您的数据图表。Adapty 通过其 SDK 和 AdServices 框架自动收集 Apple Ads 的归因数据。
完成 Apple Ads 集成设置后,Adapty 将开始接收来自 Apple Ads 的归因数据。您可以在用户画像页面轻松访问和查看这些数据。
## 设置集成 \{#set-up-integration\}
### 将 Adapty 连接到 AdServices 框架 \{#connect-adapty-to-the-adservices-framework\}
通过 [AdServices](https://developer.apple.com/documentation/adservices) 使用 Apple Ads 需要在 Adapty 看板中进行一些配置,同时也需要在应用端启用该功能。按照以下步骤,通过 Adapty 使用 AdServices 框架完成 Apple Ads 的设置:
#### 步骤 1:获取公钥 \{#step-1-obtain-public-key\}
在 Adapty 看板中,前往 [Settings -> Apple Ads。](https://app.adapty.io/settings/apple-search-ads)
找到预先生成的公钥(Adapty 会为您提供一对密钥)并复制。
:::note
如果您使用其他服务或自有方案进行 Apple Ads 归因,可以上传您自己的私钥。
:::
#### 第二步:在 Apple Ads 上配置用户管理 \{#step-2-configure-user-management-on-apple-ads\}
在您的 [Apple Ads 账户](https://ads.apple.com/app-store)中,前往 **Settings > User Management** 页面。为使 Adapty 能够获取归因数据,您需要邀请另一个 Apple ID 账户并授予其 API Account Manager 访问权限。您可以使用任何有权限的账户,或专门创建一个新账户。重要的是,您必须能够使用该 Apple ID 登录 Apple Ads。
#### 步骤 3:生成 API 凭据 \{#step-3-generate-api-credentials\}
接下来,在 Apple Ads 中登录新添加的账户,进入 Apple Ads 界面中的 Settings -> API,将之前复制的公钥粘贴到指定字段中,然后生成新的 API 凭据。
#### 步骤 4:在 Adapty 中配置 Apple Ads 凭据 \{#step-4-configure-adapty-with-apple-ads-credentials\}
从 Apple Ads 设置中复制 Client ID、Team ID 和 Key ID 字段。在 Adapty 看板中,将这些凭据粘贴到对应字段中。
### 将您的应用连接到 AdServices 网络 \{#connect-your-app-to-the-adservices-network\}
完成 [AdServices 框架设置](#connect-the-adservices-framework)后,Adapty 会自动开始收集 Apple Search Ad 归因数据。您无需添加任何 SDK 代码。
对于 iOS 应用,此归因数据将**始终**优先于其他来源的数据。如果不需要此行为,请按照以下说明*禁用* ASA 归因。
## 禁用集成 \{#disable-integration\}
要关闭 Apple Search Ads 归因,请打开 [**App Settings** -> **Apple Search Ads** 标签页](https://app.adapty.io/settings/apple-search-ads),然后关闭 **Receive Apple Search Ads attribution** 开关。
:::warning
请注意,禁用此选项将完全停止接收 ASA 分析数据。因此,ASA 将不再用于数据分析,也不会发送至任何集成。此外,SplitMetrics Acquire 和 Asapty 也将停止运行,因为它们依赖 ASA 归因才能正常工作。
此更改之前已接收的归因数据不受影响。
:::
## 上传您自己的密钥 \{#uploading-your-own-keys\}
:::note
可选
这些步骤不是 Apple Ads 归因所必需的,仅用于与 Asapty 等其他服务或您自己的解决方案配合使用。
:::
如果您使用其他服务或自己的 ASA 归因解决方案,可以使用您自己的公私密钥对。
### 第 1 步 \{#step-1\}
在终端中生成私钥
```text showLineNumbers title="Text"
openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
```
在 Adapty Settings -> Apple Ads 中上传(点击 Upload private key 按钮)
### 第 2 步 \{#step-2\}
在终端中生成公钥
```text showLineNumbers title="Text"
openssl ec -in private-key.pem -pubout -out public-key.pem
```
您可以在具有 API Account Manager 角色的账户的 Apple Ads 设置中使用此公钥。这样您就可以将生成的 Client ID、Team ID 和 Key ID 值用于 Adapty 和其他服务。
---
# File: account
---
---
title: "账户详情与计费"
description: "管理您的 Adapty 账户,优化设置以更好地追踪订阅数据。"
---
**Account** 页面让您可以管理用户画像、团队成员和计费信息。
该页面包含三个标签页:
- [通用](#general-settings)
- [订阅与计费](#billing-info)
- [成员](#members)
要访问账户设置,请点击右上角的 **Account**,或前往 [app.adapty.io/account](https://app.adapty.io/account)。
## 常规设置 \{#general-settings\}
**General** 标签页包含用户画像、账户设置、显示偏好以及报告配置。
- **Profile**:输入您的名字、姓氏和公司名称。公司名称最多可包含 256 个字符。
- **Account settings**:查看您的注册邮箱地址并修改密码。
- **Date & Time formats**:选择 Adapty 中日期和时间的显示方式:
- **American format**:January 31, 2022,以及 12 小时制(AM/PM)
- **European format**:31 January, 2022,以及 24 小时制(16:00)
- **Email reports**:为一个或所有应用设置每日、每周或每月报告。可以同时接收所有应用的汇总报告,也可以为每个选定的应用单独获取详细报告。
## 订阅与账单 \{#billing-info\}
**Subscription & Billing** 选项卡让您管理支付信息和功能访问权限:
- 添加或更新支付详情
- 查看账单信息
- 购买额外的付费功能
了解更多关于[功能与定价](https://adapty.io/pricing)的信息。
## 成员 \{#members\}
您可以在账户设置中管理团队成员。要添加团队成员,请通过电子邮件邀请他们并为其分配角色。
阅读更多关于管理团队成员及其访问权限的内容,请点击[此处](members-settings)。
---
# File: members-settings
---
---
title: "成员"
description: "在 Adapty 看板中管理成员设置和权限。"
---
:::note
本页面介绍 Adapty 看板成员相关内容
如果您想为应用的用户设置不同的访问等级,请查看[访问等级](access-level)。
:::
Adapty 看板成员系统允许您为每位成员授予不同级别的 Adapty 访问权限,并指定其可访问的应用。
## 角色 \{#roles\}
以下角色可在 Adapty 看板中分配给成员:
| 角色 | 访问账单 | 添加新成员 | 修改任何内容 | 访问所有模块 |
|-------------|----------|-----------|--------------|--------------|
| Owner | ✅ | ✅ | ✅ | ✅ |
| Admin | ❌ | ✅ | ✅ | ✅ |
| Developer | ❌ | ❌ | ✅ | ❌ |
| Viewer | ❌ | ❌ | ❌ | ✅ |
| Support | ❌ | ❌ | ❌ | ❌ |
| ASA manager | ❌ | ❌ | ❌ | ❌ |
- **Owner(所有者):** Owner 是 Adapty 账户的原始创建者,拥有最高级别的访问权限和控制权。Owner 可以完全访问 Adapty 账单,管理付款信息和订阅计划。此外,只有 Owner 和 Admin 才能为新成员指定应用访问权限。每个 Adapty 账户只能有一位 Owner。
- **Admin(管理员):** 拥有 Admin 角色的成员可以完全访问所选应用。他们可以执行各种管理任务,包括创建和修改付费墙、开展 A/B 测试、分析数据以及管理这些应用内的成员。
- **Developer(开发者):** 拥有 Developer 角色的成员可以完全访问所有实体,但数据分析和账户成员管理除外。他们无法访问任何账单设置。此角色适合负责配置付费墙、A/B 测试及其他实体并将 Adapty 集成到应用中、但不应查看财务数据的人员。
- **Viewer(查看者):** 拥有 Viewer 角色的成员对所选应用拥有只读访问权限。他们可以查看信息,但无法创建或修改付费墙、A/B 测试及其他功能,也无法邀请新用户、创建新应用或更改应用设置。
- **Support(客服):** 拥有 Support 角色的成员只能访问所选应用中的用户画像。但他们无法添加新成员或访问 Adapty 的其他任何板块。此角色特别适合需要协助用户处理订阅相关咨询或问题排查的客服团队或个人。
- **ASA manager(ASA 管理员):** 拥有 ASA manager 角色的成员只能访问 [Adapty Ads Manager](adapty-ads-manager) 看板。
## 添加成员 \{#add-a-member\}
在 Adapty 中,你最多可以邀请 256 名团队成员,添加新成员免费。
:::note
你只能邀请尚未在 Adapty 注册的邮箱地址。如果你的同事已有独立账户,请使用其他邮箱地址邀请,或联系 Adapty 支持团队删除其现有账户。
:::
添加团队成员的步骤:
1. 点击右上角的 **Account**,打开 **Members** 标签页。
2. 点击 **Invite member**。
3. 输入成员的电子邮件地址。
4. 从列表中选择一个[角色](#roles)。
5. 选择要授权访问的应用。
6. (可选)启用 **Always allow access to new apps**,以便自动为未来新增的应用授予访问权限。
7. 点击 **Save**。
## 转让账户所有权 \{#transfer-account-ownership\}
如需转让整个**账户所有权**,请通过 [support@adapty.io](mailto:support@adapty.io) 联系我们的支持团队。
如需转让**应用所有权**,请阅读[专项指南](transfer-apps)了解详情。
---
# File: set-up-app-store-connect
---
---
title: "设置 App Store Connect"
description: "面向首次开发者的指南,介绍如何注册 Apple 开发者计划并设置 App Store Connect 以支持应用内购买。"
---
如果您正在**构建第一个 iOS 应用**,必须先设置 Apple 开发者账户和 App Store Connect,然后再集成 Adapty。
:::note
如果您已拥有 Apple 开发者账户并已在 App Store Connect 中注册了应用,可以跳过本指南,直接前往[与 App Store 的初始集成](initial_ios)。
:::
## 第一步:注册 Apple 开发者计划 \{#step-1-enroll-in-apple-developer-program\}
要在 App Store 上分发应用并销售应用内购买,您必须加入 [Apple 开发者计划](https://developer.apple.com/programs/)。
### 选择注册类型 \{#choose-enrollment-type\}
Apple 提供两种注册类型:
| | 个人 | 组织 |
|-----------------------------|--------------------|------------------------------|
| **适用对象** | 独立开发者 | 公司、团队、非营利组织 |
| **是否需要 D-U-N-S 编号** | 否 | 是 |
| **应用发布名义** | 您的个人姓名 | 您的组织名称 |
| **团队管理** | 不支持 | 支持 |
:::tip
如果您以组织身份注册,需要一个 **D-U-N-S 编号** —— 由邓白氏公司颁发的唯一九位数企业标识符。您可以[查询您的组织是否已有编号](https://developer.apple.com/enroll/duns-lookup/),或申请新编号——申请链接位于查询页面底部。获取 D-U-N-S 编号最多需要 5 个工作日。
:::
### 注册 \{#enroll\}
1. 前往 [Apple 开发者计划注册页面](https://developer.apple.com/programs/enroll/)。
2. 使用您的 Apple ID 登录。如果没有 Apple ID,请先创建一个。
3. 按照适合您注册类型(个人或组织)的步骤操作。
4. 支付年费。
Apple 处理完您的注册申请后,您将获得 [App Store Connect](https://appstoreconnect.apple.com) 的访问权限。注册通常需要最多 48 小时。对于组织注册,如果需要 D-U-N-S 验证,可能需要更长时间。
## 第二步:在 App Store Connect 中设置您的应用 \{#step-2-set-up-your-app-in-app-store-connect\}
在销售应用内购买之前,需要在 App Store Connect 中完成初始设置,包括签署协议、添加付款信息以及注册应用。
### 签署付费应用协议 \{#sign-the-paid-applications-agreement\}
Apple 要求您在 App Store 上销售之前签署付费应用协议。无论是付费应用还是免费应用中的应用内购买,均需签署此协议。
1. 前往 [App Store Connect](https://appstoreconnect.apple.com/business) 中的 **Business** 页面。
2. 找到 **Paid Apps** 协议,点击 **Review and Agree**。
3. 填写所需信息:
- **Banking information**:添加银行账户,Apple 将向该账户汇入您的收益。
- **Tax information**:填写您希望销售的国家/地区的税务表格。
- **Contact information**:提供您的联系方式。
:::important
您必须完成全部三个部分(银行信息、税务信息、联系信息),协议才能生效。协议未生效之前,您无法销售应用内购买。
:::
### 创建 Bundle ID \{#create-a-bundle-id\}
Bundle ID 在 Apple 生态系统中唯一标识您的应用。您需要它来在 App Store Connect 中注册应用,以及配置 Adapty 集成。
1. 打开 [Apple 开发者门户](https://developer.apple.com/account)。
2. 前往 **Certificates, Identifiers & Profiles** → **Identifiers**。
3. 点击 **+** 注册新标识符。
4. 选择 **App IDs**,点击 **Continue**。
5. 选择 **App** 作为类型,点击 **Continue**。
6. 填写以下字段:
- **Description**:帮助您识别此 Bundle ID 的名称(例如 "My Subscription App")。
- **Bundle ID**:选择 **Explicit**,并以反向域名格式输入唯一标识符(例如 `com.yourcompany.yourapp`)。
7. 在 **Capabilities** 部分,向下滚动并勾选 **In-App Purchase**。
8. 点击 **Continue**,然后点击 **Register**。
### 在 App Store Connect 中注册您的应用 \{#register-your-app-in-app-store-connect\}
1. 前往 [App Store Connect](https://appstoreconnect.apple.com/apps) 中的 **Apps** 页面。
2. 点击 **+** → **New App**。
3. 填写所需字段:
- **Platforms**:选择 **iOS**。
- **Name**:您的应用名称,将在 App Store 上显示。
- **Primary language**:应用元数据的默认语言。
- **Bundle ID**:选择您在上一步中创建的 Bundle ID。
- **SKU**:您应用的唯一标识符(用户不可见)。例如 `my_subscription_app_2025`。
4. 点击 **Create**。
您的应用现已在 App Store Connect 中注册,可以进行 Adapty 集成了。
## 后续步骤 \{#whats-next\}
- [与 App Store 的初始集成](initial_ios):将您的 App Store 应用连接到 Adapty
- [SDK 集成](quickstart-sdk):将 Adapty SDK 集成到您的应用代码中
- [沙盒测试](test-purchases-in-sandbox):在发布前测试您的应用内购买
- [将您的 iOS 应用提交至 App Store](submit-app-to-app-store):上传构建版本并提交 Apple 审核
- [App Store 小型企业计划](app-store-small-business-program):将您的 App Store 佣金从 30% 降至 15%
---
# File: app-store-products
---
---
title: "App Store 中的产品"
description: "使用 Adapty 的订阅工具高效管理 App Store 产品。"
---
本页面提供了在 App Store Connect 中创建产品的指导。尽管这些信息可能与 Adapty 的功能没有直接关系,但如果您在 App Store Connect 账号中创建产品时遇到困难,这将是一份有价值的参考资料。
要创建一个将与 Adapty 关联的产品:
1. 打开 **App Store Connect**。在左侧菜单中前往 [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) 部分。
2. 如果您尚未创建订阅组,请点击 **Subscription Groups** 标题下的 **Create** 按钮开始创建流程。App Store Connect 中的[订阅组](https://developer.apple.com/help/app-store-connect/manage-subscriptions/offer-auto-renewable-subscriptions)用于对您的产品进行分类和管理,让用户可以在不同产品之间无缝切换。请注意,无法在订阅组之外创建订阅。
3. 在弹出的 **Create Subscription Group** 窗口中,在 **Reference Name** 字段中输入新的订阅组名称。参考名称是一个用户自定义的标签或标识符,帮助您区分和管理应用中不同的订阅组。
参考名称对用户不可见,主要供您内部使用和组织管理。它使您能够在 App Store Connect 界面中轻松识别和引用特定的订阅组。如果您有多个订阅产品,或希望按照对应用结构有意义的方式进行分类,这将特别有用。
4. 点击 **Create** 按钮确认创建订阅组。
5. 订阅组已创建并打开。现在您可以在该组中创建订阅。点击 **Subscriptions** 标题下的 **Create** 按钮。如果您要向现有组添加新订阅,请点击 **Subscriptions** 标题旁的 **Plus** 按钮。
6. 在弹出的 **Create Subscription** 窗口中,在 **Reference Name** 字段中输入名称,在 **Product ID** 字段中输入订阅的唯一代码。
Reference Name 是您的应用内订阅在 App Store Connect 中的专属标识符,对 App Store 上的用户不可见。我们建议使用清晰、易于理解的描述,以准确表达您要创建的具体订阅。请注意,该名称不得超过 64 个字符。
Product ID 是一个唯一的字母数字标识符,在开发阶段访问您的产品以及将其与 Adapty(一项用于管理应用内订阅的服务)同步时必不可少。Product ID 中只允许使用字母数字字符、句点和下划线。
7. 点击 **Create** 按钮确认创建订阅。
8. 订阅已创建并打开。现在在 **Subscription Duration** 列表中选择订阅时长。即使订阅名称中已经包含了时长信息,也请记得填写 **Subscription Duration** 字段。
9. 现在是设置订阅价格的时候了。点击 Subscription Prices 标题下的 **Add Subscription Price** 按钮。您可能需要向下滚动才能找到该按钮。
10. 在弹出的 **Subscription Price** 窗口中,在 **Country or Region** 列表中选择基准国家,在 **Price** 列表中选择基准货币。之后,Apple 将根据该基准价格和最新汇率自动计算所有 175 个国家或地区的价格。
11. 点击 **Next** 按钮。在弹出的 **Price by Country or Region** 窗口中,您可以看到所有国家自动重新计算后的价格。如有需要,您可以对其进行修改。
12. 更新各地区价格后,点击窗口底部的 **Next** 按钮继续。
13. 在弹出的 **Confirm Subscription Price?** 窗口中,仔细核对最终价格。如需修正价格,可点击 **Back** 按钮返回 **Price by Country or Region** 窗口进行更新。确认价格无误后,点击 **Confirm** 按钮。
14. 关闭 **Confirm Subscription Price?** 窗口后,请记得点击订阅窗口中的 **Save** 按钮。否则,订阅将不会被创建,所有已输入的数据都将丢失。
请注意,目前提供的步骤侧重于配置自动续期订阅。但是,如果您打算设置其他类型的应用内购买,可以点击侧边栏中的 **In-App Purchases** 标签,而不是"Subscriptions"。这将引导您进入可以管理和创建各种类型应用内购买的部分。
### 将产品添加到 Adapty \{#add-products-to-adapty\}
在 App Store Connect 中完成应用内购买、订阅和优惠的添加后,下一步是[将这些产品添加到 Adapty](create-product)。
---
# File: apple-app-privacy
---
---
title: "Apple App Privacy"
description: "了解 Apple 应用隐私政策及其对您的订阅应用的影响。"
---
Apple 要求所有新应用及应用更新在 App Store Connect 的 **App Privacy** 部分以及应用清单文件中进行隐私披露。Adapty 是您应用的第三方依赖项,因此您需要披露如何在用户数据方面使用 Adapty。
## Apple 应用隐私清单 \{#apple-app-privacy-manifest\}
[隐私清单文件](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests)(命名为 `PrivacyInfo.xcprivacy`)描述了您的应用使用了哪些私有数据以及使用原因。作为每位应用所有者,您必须为自己的应用创建清单文件。此外,如果您集成了额外的 SDK,请确保那些出现在[需要隐私清单和签名的 SDK](https://developer.apple.com/support/third-party-SDK-requirements/) 列表中的 SDK 的清单文件已被包含。构建应用时,Xcode 会将所有这些清单文件合并为一个。
尽管 Adapty 不在[需要隐私清单和签名的 SDK](https://developer.apple.com/support/third-party-SDK-requirements/) 列表中,但 Adapty SDK 2.10.2 及更高版本已为方便起见包含了该文件。请确保更新 SDK 以获取清单。
虽然 Adapty 不要求在清单文件(也称为应用隐私报告)中包含任何数据,但如果您使用 Adapty 的 `customerUserId` 进行追踪,则需要在清单文件中按如下方式指定:
1. 在隐私信息文件的 `NSPrivacyCollectedDataTypes` 数组中添加一个字典。
2. 向该字典添加 `NSPrivacyCollectedDataType`、`NSPrivacyCollectedDataTypeLinked` 和 `NSPrivacyCollectedDataTypeTracking` 键。
3. 在 `NSPrivacyCollectedDataTypes` 字典的 `NSPrivacyCollectedDataType` 键中添加字符串 `NSPrivacyCollectedDataTypeUserID`(即[清单文件中需报告的数据类别和类型列表](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests#Describe-the-data-your-app-or-third-party-SDK-collects)中 `UserID` 数据类型的标识符)。
4. 在 `NSPrivacyCollectedDataTypes` 字典的 `NSPrivacyCollectedDataTypeTracking` 和 `NSPrivacyCollectedDataTypeLinked` 键中添加 `true`。
5. 在 `NSPrivacyCollectedDataTypes` 字典的 `NSPrivacyCollectedDataTypePurposes` 键中使用字符串 `NSPrivacyCollectedDataTypePurposeProductPersonalization` 作为值。
如果您将付费墙定向到具有自定义属性的目标受众,请仔细考虑您使用的自定义属性是否与[清单文件中需报告的数据类别和类型](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests)匹配。如果匹配,请对每种数据类型重复上述步骤。
在报告所有收集的数据类型和类别后,请按照 [Apple 文档](https://developer.apple.com/documentation/bundleresources/describing-data-use-in-privacy-manifests#Create-your-apps-privacy-report)中的说明创建应用的隐私报告。
## App Store Connect 中的 Apple 应用隐私披露 \{#apple-app-privacy-disclosure-in-app-store-connect\}
1. 在 [App Store Connect](https://appstoreconnect.apple.com/) 中,打开您的应用并进入 **App Privacy**。点击 **Get Started**。
2. 选择 **Yes, we collect data from this app**,然后点击 **Next**。
### 数据类型 \{#data-types\}
下表列出了 Apple 要求披露的数据类型,并指出了 Adapty 所需的数据类型。**此处仅涵盖 Adapty。** 如果您的应用通过其他 SDK 或自己的代码收集了额外数据,也请选择相应的数据类型。
✅ = Adapty 必填
👀 = 可能必填(详见下方说明)
❌ = Adapty 不需要——如果您的应用通过其他方式收集此数据,请选择
| 数据类型 | 是否必填 | 说明 |
|--------------------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| Identifiers | ✅ | 如果您使用 customerUserId 标识用户,请选择"User ID"。
Adapty 会收集 IDFA,因此您必须选择"Device ID"。
| | Purchases | ✅ | Adapty 会从用户处收集购买历史记录。 | | Contact Info,包括姓名、电话号码或电子邮件地址 | 👀 | 如果您通过 **`updateProfile`** 方法传递姓名、电话号码或电子邮件地址等个人数据,则为必填。 | | Usage Data | 👀 | 如果您使用 Amplitude、Mixpanel、AppMetrica 或 Firebase 等分析 SDK,可能需要填写。 | | Location | ❌ | Adapty 不收集精确位置数据。如果您的应用收集,请选择。 | | Health & Fitness | ❌ | Adapty 不收集健康或健身数据。如果您的应用收集,请选择。 | | Sensitive Info | ❌ | Adapty 不收集敏感信息。如果您的应用收集,请选择。 | | User Content | ❌ | Adapty 不收集用户内容。如果您的应用收集,请选择。 | | Diagnostics | ❌ | Adapty 不收集诊断数据。如果您的应用收集,请选择。 | | Browsing History | ❌ | Adapty 不收集浏览历史记录。如果您的应用收集,请选择。 | | Search History | ❌ | Adapty 不收集搜索历史记录。如果您的应用收集,请选择。 | | Contacts | ❌ | Adapty 不收集联系人列表。如果您的应用收集,请选择。 | | Financial Info | ❌ | Adapty 不收集财务信息。如果您的应用收集,请选择。 | ### 必填数据类型 \{#required-data-types\} #### 购买记录 \{#purchases\} 使用 Adapty 时,您必须披露您的应用收集 **Purchase History**。
#### 标识符 \{#identifiers\}
使用 Adapty 时,您必须披露以下标识符:
- **Device ID** — Adapty 收集 IDFA。
- **User ID** — 如果您使用 **`customerUserId`** 标识用户,则为必填。
### 数据用途 \{#data-usage\}
保存 **Data types** 后,您需要说明数据的用途:
1. 点击 **Purchases** 模块中的 **Set up purchase history**。
2. 当 Apple 询问购买历史记录数据的用途时,请为 Adapty 选择以下选项:
- **Analytics** — Adapty 使用购买历史记录进行收入分析、同期群分析和数据图表统计。
- **Product Personalization** — Adapty 使用购买数据进行目标受众市场细分和付费墙定向。
- **App Functionality** — Adapty 验证购买、管理访问等级并追踪订阅状态。
如果您的应用以其他方式使用购买数据(例如,通过 Adapty 集成将购买事件发送到广告平台),请选择额外的用途。
3. 点击 **Next**。
4. 对于 **Device ID** 和 **User ID**(如适用):
1. 点击 **User/Device ID** 模块中的 **Set up user/device ID**。
2. 当 Apple 询问标识符数据的用途时,请为 Adapty 选择以下选项:
- **App Functionality** — Adapty 使用标识符管理用户画像、关联购买记录并追踪访问等级。
如果您通过 Adapty 集成(例如 AppsFlyer 或 Adjust)向第三方平台发送归因数据,还请选择 **Third-Party Advertising**。如果您的应用以其他方式使用标识符,请选择额外的用途。
5. 点击 **Next**。
---
# File: apple-family-sharing
---
---
title: "Apple 家庭共享"
description: "在 Adapty 中启用 Apple 家庭共享以支持共享订阅。"
---
Apple 的家庭共享功能允许在家庭成员之间分发应用内购买,为视频流媒体服务和儿童应用等面向群体的应用用户提供了一种便捷的方式,无需共享 Apple ID 即可分摊订阅费用。通过允许最多五名家庭成员使用同一订阅,[家庭共享](https://developer.apple.com/documentation/storekit/supporting-family-sharing-in-your-app)可以有效提升应用的用户互动度和留存率。
本指南将介绍如何为订阅开启家庭共享,并说明 Adapty 如何管理家庭内共享的购买行为。
要为特定产品启用家庭共享,请前往 [App Store Connect](https://appstoreconnect.apple.com/)。家庭共享对新旧应用内购买项目均默认关闭,因此需要为每个应用内购买项目单独启用。您可以进入**应用页面**,导航到对应的应用内购买页面,然后在"家庭共享"部分选择**开启**选项来完成操作。
请注意,一旦为某个产品启用家庭共享,**将无法再次关闭**,因为这会影响已与家庭成员共享订阅的用户体验。此外,请注意只有非消耗型商品和订阅才可以被共享。
在弹出的对话框中,点击**确认**按钮完成设置。完成后,家庭共享部分将显示消息:"此订阅可由家庭群组中的所有人共享。"这表明订阅已成功启用家庭共享,最多可与五名家庭成员共享。
Adapty 让您无需任何额外操作即可轻松支持家庭共享。您只需[配置您的产品](app-store-products)(来自 App Store),一旦您在 App Store Connect 中**启用****家庭共享**,它将自动在 **Adapty** 中生效,并以事件形式通过 webhook 接收。
:::note
请注意,沙盒环境不支持家庭共享。
:::
需要注意的是,当用户购买订阅并与家庭成员共享时,家庭成员最多需要等待**一小时**才能使用该订阅。Apple 设计此延迟是为了让用户有时间改变主意并取消共享。但是,如果订阅续订,家庭成员可立即使用,无需等待。
当用户购买支持家庭共享的应用内产品时,该交易将照常出现在其收据中,但会新增一个名为 `in_app_ownership_type` 的字段,其值为 `PURCHASED`。此外,系统将为所有家庭成员创建新的交易,这些交易与原始购买相比具有不同的 `web_order_line_item_id` 和 `original_transaction_id`,以及值为 `FAMILY_SHARED` 的 `in_app_ownership_type` 字段。
为确保收入计算准确,Adapty 分析仅统计 `in_app_ownership_type` 为 `PURCHASED` 的交易。`FAMILY_SHARED` 交易不计入收入和转化数据图表。
**家庭共享交易触发的事件。**
`FAMILY_SHARED` 交易仅触发 **Access level updated** 事件,家庭成员不会触发各产品的订阅事件。
| 事件 | `FAMILY_SHARED` | `PURCHASED` |
| --- | --- | --- |
| **访问等级已更新** | 是 | 是 |
| **订阅已开始** | 否 | 是 |
| **试用已开始** | 否 | 是 |
| **订阅已续期** | 否 | 是 |
| **订阅已到期** | 否 | 是 |
| **订阅已退款** | 否 | 是 |
| **检测到账单问题** | 否 | 是 |
如果您的下游分析系统以 **订阅已开始** 作为关键事件,家庭成员将不会出现在其中。请使用 **访问等级已更新** 来检测活跃的家庭共享成员。
要在 Adapty 中识别其他家庭成员,您可以在事件详情中找到相关信息。首先,找到原始的家庭购买交易,然后查看该交易的事件详情,重点关注具有相同产品、购买日期和到期日期的记录。通过分析事件详情,您可以识别与原始购买相关联的其他家庭成员交易。
---
# File: app-store-small-business-program
---
---
title: "App Store 小型企业计划"
description: "了解 Apple 的小型企业计划、其对您收入的影响以及 Adapty 分析的相关内容"
---
:::link
如需了解 Play Store 的对应计划,请参阅 [Google 降低服务费计划](google-reduced-service-fee)。
:::
每年从 App Store 获得不超过 100 万美元收益的机构,均可申请加入苹果的[小型企业计划](https://developer.apple.com/app-store/small-business-program/)。加入后,标准 30% 的商店佣金将降至 **15%**。
计划成员必须**更改 Adapty 设置**,以确保收益计算和集成事件处理的准确性。
---
title: "小型企业计划"
description: "了解如何在 Adapty 中配置小型企业计划,以及如何申请加入该计划以降低商店佣金。"
metadataTitle: "小型企业计划 | Adapty 文档"
---
本文介绍以下内容:
* [如果您的应用已加入小型企业计划,如何配置 Adapty](#configure-adapty)
* [如果您想降低商店佣金,如何申请加入该计划](#apply-for-the-program)
## 配置 Adapty \{#configure-adapty\}
Adapty 可以将折扣佣金率应用于您的[数据分析](analytics)和[集成事件](analytics-integration)。要启用此功能,请按应用逐个设置小型企业计划状态。
:::warning
**在获得批准后,请立即**在 Adapty 中配置您的 SBP 状态。事后修改无法重写已推送的 webhook 事件([详情](#retroactive-setting-changes))。
:::
1. 打开 [**App Settings** → **General**](https://app.adapty.io/account)
2. 找到 **Small Business Program** 部分。
3. 点击 **Add period**。
4. 选择加入计划的开始日期。
5. 选择结束日期,或勾选 **At the current moment** 以无限期延续此状态。如果您将来[失去资格](#losing-eligibility),可以修改结束日期。
6. 点击 **Apply**。
如果您的组织仍符合该计划的资格要求,其会员资格将自动延续到下一个日历年。但会员状态**仅适用于您指定的日期范围**。
* 点击 **Add period** 添加新的会员期。
* 如需将此状态设置为无限期,请勾选 **At the current moment**。
如需验证配置是否正确,请打开[收入数据图表](revenue)并选择 **Proceeds after store commission**,确认显示的收益已反映出降低后的佣金比例。
## 申请加入计划 \{#apply-for-the-program\}
### 资格要求 \{#eligibility-requirements\}
Apple 根据您的**年度收益**来确定小型企业计划资格——即上一个日历年扣除商店佣金和税款**后**的销售额。
要获得资格,您的组织及其
2. 点击 **Create subscription** 按钮。
3. 在打开的 **Create subscription** 窗口中,在 **Product ID** 字段输入订阅 ID,在 **Name** 字段输入订阅名称。
Product ID 必须唯一,且必须以数字或小写字母开头,还可以包含下划线(\_)和句点(.)。它用于在开发过程中访问您的产品,并与 Adapty 进行同步。一旦在 Google Play 控制台中将 Product ID 分配给某个产品,即使该产品被删除,该 ID 也无法再用于任何其他应用。
在命名产品 ID 时,建议遵循标准化格式。我们推荐使用更简洁的方式,将产品命名为 `
3. 订阅详情打开后,点击 **Base plans and offers** 标题下的 **Add base plan** 按钮。您可能需要向下滚动才能找到它。
4. 在打开的 **Add base plan** 窗口中,在 **Plan ID** 字段输入基础方案的唯一标识符。它必须以数字或小写字母开头,可以包含数字(0-9)、小写字母(a-z)和连字符(-),并填写所有必填字段。
5. 按地区指定价格。
6. 点击 **Save** 按钮完成设置。
7. 点击 **Activate** 按钮使基础方案生效。
请注意,在 Adapty 中,订阅产品只能有一个具有固定时长和续费类型的基础方案。
### 备用产品 \{#fallback-products\}
:::warning
支持非向后兼容基础方案
旧版本的 Adapty SDK 不支持 Google Billing Library v5+ 的特性,特别是每个订阅产品的多个基础方案和优惠。只有在 Google Play 控制台中标记为 **[向后兼容](https://support.google.com/googleplay/android-developer/answer/12124625?hl=en#backwards_compatible)** 的基础方案才能在这些 SDK 版本中使用。请注意,每个订阅只能有一个基础方案被标记为向后兼容。
:::
为了充分利用 Adapty 中增强的 Google 订阅配置和功能,我们提供了设置向后兼容备用产品的能力。该备用产品专门用于使用旧版本 Adapty SDK 的应用。在创建 Google Play 产品时,您现在可以选择是否在 Play 控制台中将该产品标记为向后兼容。Adapty 会利用此信息来判断该产品是否可以被旧版本 SDK(2.5 及以下版本)购买。
假设您有一个名为 `subscription.premium` 的订阅,它提供两个基础方案:每周(向后兼容)和每月。如果您将 `subscription.premium:weekly` 产品添加到 Adapty,则无需指定向后兼容产品。但是,对于 `subscription.premium:monthly` 产品,您需要指定一个向后兼容产品。如果不这样做,可能会导致在 Google 第四代计费库中意外购买 `subscription.premium:weekly` 产品。为了解决这种情况,您应该创建一个单独的产品,其基础方案也是每月且标记为向后兼容。这样可以确保选择 `subscription.premium:monthly` 选项的用户按照预期的频率正确扣费。
## 将产品添加到 Adapty \{#add-products-to-adapty\}
在 App Store Connect 中完成添加应用内购买、订阅和优惠之后,下一步是[将这些产品添加到 Adapty](create-product)。
---
# File: google-play-data-safety
---
---
title: "Google Play 数据安全"
description: "确保在 Adapty 中符合 Google Play 数据安全政策。"
---
Google Play 上提供的数据安全部分为应用开发者提供了一种简便方法,用于向用户说明应用收集或共享的数据,并突出显示应用的关键隐私和安全措施。这些信息能够帮助用户在选择下载和使用哪些应用时做出更明智的决定。
以下是关于 Adapty 收集的数据的简短指南,帮助您向 Google Play 提供所需信息。
## 数据收集与安全 \{#data-collection-and-security\}
**您的应用是否收集或共享任何所需的用户数据类型?**
选择"是",因为 Adapty 会收集用户的购买历史记录。
**您的应用收集的所有用户数据在传输过程中是否都经过加密?**
选择"是",因为 Adapty 会对传输中的数据进行加密。
**您是否提供了让用户请求删除其数据的方式?**
如果选择"是",请确保您的用户有办法联系您的支持团队以请求删除数据。您可以直接从 Adapty 看板或通过 REST API 删除用户。
## 数据类型 \{#data-types\}
以下是 Google 要求报告的数据类型列表,我们已说明 Adapty 是否收集了各类特定数据。
| 数据类型 | 详情 |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 位置 | Adapty 不收集 |
| 健康与健身 | Adapty 不收集 |
| 照片与视频 | Adapty 不收集 |
| 文件与文档 | Adapty 不收集 |
| 日历 | Adapty 不收集 |
| 联系人 | Adapty 不收集 |
| 用户内容 | Adapty 不收集 |
| 浏览历史记录 | Adapty 不收集 |
| 搜索历史记录 | Adapty 不收集 |
| 应用信息与性能 | Adapty 不收集 |
| 网页浏览 | Adapty 不收集 |
| 联系信息 | Adapty 不收集 |
| 财务信息 | Adapty 收集用户的购买历史记录 |
| 个人信息与标识符 | 如果您明确将相关信息传递给 Adapty SDK,Adapty 会收集用户 ID 及其他可识别联系信息,包括姓名、电子邮件地址、电话号码等。 |
| 设备及其他标识符 | Adapty 收集设备 ID 数据。 |
## 数据使用与处理 \{#data-usage-and-handling\}
### 用户 ID \{#user-ids\}
**1. 此数据是被收集、共享,还是两者皆有?**
此数据由 Adapty 收集。如果您正在使用 Adapty 与非服务提供商第三方之间设置的集成,您可能还需要在此处披露"共享"。
**2. 此数据是否以临时方式处理?**
选择"否"。
**3. 此数据对您的应用是必需的,还是用户可以选择是否收集?**
此数据收集是必需的,无法关闭。
**4. 为什么收集此用户数据?/ 为什么共享此用户数据?**
勾选"应用功能"和"分析"复选框。
### 财务信息 \{#financial-info\}
如果您正在使用 Adapty,您必须在 Google Play Console 的数据类型部分中披露您的应用会收集"购买历史记录"信息。
### 设备或其他 ID \{#device-or-other-ids\}
## 后续步骤 \{#next-steps\}
完成数据安全选项后,Google 将显示您应用隐私部分的预览。如果您已选择前文提到的"财务信息"和"设备或其他 ID",您的隐私信息应与以下示例类似:
如果您已准备好提交应用进行审核,请参阅我们的[发布检查清单](release-checklist)文档,以获取有关准备提交应用的更多指导。
---
# File: google-reduced-service-fee
---
---
title: "Google 降低服务费"
description: "了解 Google 降低服务费计划、其对收入的影响以及 Adapty 的分析处理方式"
---
:::link
如需了解 App Store 的对应计划,请参阅 [App Store 小型企业计划](app-store-small-business-program)。
:::
Google Play 的[降低服务费计划](https://support.google.com/googleplay/android-developer/answer/112622?hl=en)将每年前 100 万美元收益的佣金从 30% 降低至 **15%**。同一日历年内超过 100 万美元的收益仍按标准 30% 费率收取。
:::note
自 2022 年 1 月 1 日起,Google 对所有自动续订订阅统一收取 15% 的费率,与该计划无关。降低服务费计划主要适用于非订阅类应用内购买和付费应用。
:::
项目成员必须**修改 Adapty 设置**,以确保收入计算和集成事件处理的准确性。
本文介绍:
* [如果您的应用已加入减免服务费项目,如何配置 Adapty](#configure-adapty)
* [如果您希望降低商店佣金,如何加入该项目](#enroll-in-the-program)
## 配置 Adapty \{#configure-adapty\}
Adapty 可以将降低的佣金率应用到[数据图表](analytics)和[集成事件](analytics-integration)中。要启用此功能,请按应用逐一设置你的降低服务费状态。
:::warning
**一旦注册**,请立即在 Adapty 中配置你的降低服务费状态。事后修改不会重写已发送的 webhook 事件([详情](#retroactive-setting-changes))。
:::
1. 打开 [**App Settings** → **General**](https://app.adapty.io/account)。
2. 找到 **Reduced Service Fee** 部分。
3. 点击 **Add period**。
4. 选择会员资格的开始日期。
5. 选择结束日期,或勾选 **At the current moment** 以无限期延续此状态。如果你的[年收入超过 100 万美元](#exceeding-the-threshold),可以修改结束日期。
6. 点击 **Apply**。
会员状态**仅适用于您指定的日期范围**,且每个自然年重置一次。
* 点击 **Add period** 可添加新的会员期。
* 若要无限期延续该状态,请勾选 **At the current moment**。
要验证配置是否正确,请打开[收入数据图表](revenue)并选择 **Proceeds after store commission**,确认显示的收益已反映折扣后的佣金比例。
## 加入计划 \{#enroll-in-the-program\}
### 资格要求 \{#eligibility-requirements\}
Google 根据您在 | 选项 | 描述 | | ------- | ------------------------------------------------------------ | | Opt-out | (默认)如果 Adapty 不知道用户的同意状态,则假定用户**已授权**,退款保护功能**将向** Apple 共享退款相关数据。 | | Opt-in | 如果 Adapty 不知道用户的同意状态,则假定用户**未授权**,退款保护功能**不会**向 Apple 共享任何数据。这是 Apple 推荐的方式。 | ## 在 SDK 中更新用户同意状态 \{#update-user-consent-in-the-sdk\} 如需告知 Adapty 某个用户是否已提供同意,请使用 `updateCollectingRefundDataConsent` 方法。该值会按用户画像持久化存储在服务端,因此只需在同意状态发生变化时调用此方法。
:::note 如需追踪订阅事件,请在 Adapty 中使用 [Webhook](webhook) 集成,或直接与您现有的服务进行集成。 ::: ## 案例一:同步网页端与移动端的订阅用户 \{#case-1-sync-subscribers-between-web-and-mobile\} 如果你使用 Stripe、ChargeBee 或其他网页支付服务商,可以轻松同步订阅用户。操作步骤如下: 1.
2. 为您的用户引导填写一个描述性名称,然后点击 **Proceed to build onboarding**。
3. 您将被跳转到用户引导编辑工具。
它包含一个默认演示模板,你可以通过研究该模板了解用户引导如何收集数据,以及如何使用变量和测验对其进行个性化定制。你可以随意删除不需要的屏幕,并在此[设计你自己的用户引导体验](design-onboarding)。
4. 准备就绪后,点击右上角的 **Preview** 按钮。亲自完成用户引导流程,确保一切正常运行。
5. 如果一切正常,点击右上角的 **Publish**。请等待发布完成后再返回 Adapty,否则您的进度将会丢失。
:::danger
如果不点击 **Publish**,SDK 将无法获取你创建的用户引导。
:::
发布用户引导后,点击 **Back to Adapty**。你的用户引导已创建完成,接下来可以将其添加到版位中开始使用。
## 第二步:为用户引导创建版位 \{#step-2-create-a-placement-for-your-onboarding\}
1. 从主菜单进入 **Placements**,切换到 **Onboardings** 标签,点击 **Create placement**。
2. 输入版位名称和 ID,然后点击 **Run onboarding**,选择要向所有用户展示的用户引导。
3. 如果你为特定用户群体准备了单独的用户引导,请[添加更多目标受众](audience),并为其选择不同的用户引导。
## 第三步:将用户引导集成到您的应用中 \{#step-3-integrate-the-onboarding-into-your-app\}
:::important
用户引导功能适用于使用 Adapty SDK v3.8.0+(iOS、Android、React Native、Flutter)、v3.14.0+(Unity)或 v3.15.0+(Kotlin Multiplatform、Capacitor)的应用。
:::
要在您的应用中展示用户引导,请使用 Adapty SDK 进行集成:
- [iOS](ios-onboardings)
- [Android](android-onboardings)
- [React Native](react-native-onboardings)
- [Flutter](flutter-onboardings)
- [Unity](unity-onboardings)
- [Kotlin Multiplatform](kmp-onboardings)
- [Capacitor](capacitor-onboardings)
为了了解哪个用户引导效果更好,你也可以运行 [A/B 测试](ab-tests)。
---
# File: design-onboarding
---
---
title: "设计用户引导"
description: "创建有意义的用户引导。"
---
这款无代码移动应用用户引导编辑工具功能强大、高度可定制,可帮助您为用户提供最佳的用户引导体验。即使您不是开发者或设计师,也能获得出色的效果。
## 用户引导页面 \{#onboarding-screens\}
用户引导流程由多个页面组成,你可以自由添加和设计这些页面。
用户点击按钮即可在页面之间跳转。
:::tip
如果某些用户需要稍有不同的流程(例如,在健身应用中,你可能希望根据用户性别展示不同的"目标"图片),不必单独创建多个用户引导。
你可以将部分页面默认设为隐藏,仅在特定场景下显示。
:::
## 用户引导元素 \{#onboarding-elements\}
用户引导元素按显示顺序列在左侧。点击右上角的 **Add** 按钮可添加新元素。
可添加的元素分为以下几组:
- **容器**:容器可让您灵活布局。例如,如果想添加两列文本,需要先添加 **Columns**,然后在左侧面板将两个文本块拖入 **Columns** 中。如果要添加轮播图,则需要在 **Media** 元素内添加图片。
- **排版**:添加预格式化文本块,并按需调整其样式。
- **媒体与展示**:除图片和视频外,您还可以添加动态数据图表,直观展示应用价值,吸引用户购买。
支持的**视频格式**为 MP4 和 WebM。**媒体文件大小上限**为 15 MB。
如果您想添加不支持的动画元素(例如 Lottie),可以将其转换为视频(例如使用[此工具](https://www.lottielab.com/lottie/lottie-to-video)),然后以视频形式嵌入。
- **Quiz**:创建包含文字和图片选项的简短问卷,让用户引导体验更加个性化,同时深入了解您的用户。
- **Inputs**:收集用户数据。
- **Buttons**:按钮让用户可以在页面之间导航、关闭用户引导或跳转到付费墙。您还可以添加光泽或动态按钮,吸引用户注意力,将安装转化为购买。
- **Loaders**:动画加载器在流程进行中保持用户的参与度。
- **User engagement**:添加用户评价、用户邮件列表和倒计时。
:::note
作为 **Media & Display** 分组的一部分,如果现有的自定义选项不够用,你也可以添加自定义 HTML 代码。
不过,自定义 HTML 元素既不会预加载也不会被缓存,因此建议仅将 **Raw HTML** 用于小型、轻量的元素。
:::
### 元素 ID 与动作 ID \{#element-id-and-action-id\}
如果你想让按钮执行自定义操作,请为其指定一个**动作 ID**,然后在源代码中使用它。动作 ID 让你可以用同一种方式处理具有相同动作 ID 的不同按钮。
如果您需要处理用户在某个字段中的输入内容(例如保存其年龄或邮箱),请为该字段分配一个**元素 ID**,然后在源代码中使用它将问题与答案关联起来。元素 ID 在同一个用户引导中只能使用一次。
## 自定义选项 \{#customization-options\}
编辑工具中提供以下自定义选项:
- **Styles** 标签页:调整元素的外观样式。
- **Element** 标签页:设置元素的属性,例如可见性、按钮点击动作,以及其他与外观无关的属性。
- **Screen** 标签页:配置屏幕的全局设置,例如页眉或页面计数器的显示方式。
## 复制屏幕和元素 \{#copy-screens-and-elements\}
如果您已创建了一个用户引导并希望复用其中的部分内容,或者想进行细微修改并运行 A/B 测试,可以将一个或多个屏幕从一个用户引导复制到另一个。
要复制屏幕,请打开用户引导编辑工具,然后执行以下任一操作:
- 右键单击单个屏幕并选择 **Copy**
- 选中所需屏幕并按 `Ctrl+C`(Windows)或 `⌘+C`(Mac)
您还可以复制单个元素或文本块,可以在同一用户引导内复制,也可以在不同用户引导之间复制。
## 从网页转应用漏斗复制屏幕 \{#copy-screens-from-web-to-app-funnels\}
如果你在 [FunnelFox](https://funnelfox.com/) 中创建了网页转应用漏斗,并希望在用户引导中使用漏斗里的屏幕,可以直接在漏斗编辑器中复制屏幕,然后粘贴到用户引导编辑器中:
1. 在 FunnelFox 漏斗编辑器中,右键单击某个屏幕并选择 **Copy**,或选中该屏幕后按 `Ctrl+C`/`⌘+C`。
2. 打开用户引导编辑器。
3. 右键单击要插入复制屏幕的位置,选择 **Paste**,或选中该屏幕后按 `Ctrl+V`/`⌘+V`。复制的屏幕将插入到所选屏幕的下方。
---
# File: adapty-paywall-builder
---
---
title: "Adapty 付费墙编辑工具(旧版)"
description: "使用可视化无代码编辑工具创建付费墙和用户引导流程。"
---
:::warning
付费墙编辑工具仍可正常使用,但 Adapty 已停止为其添加新功能或发布更新。对于新项目,建议使用 [Adapty Flow Builder](adapty-flow-builder) —— 一款可视化无代码编辑器,支持单屏付费墙和多屏用户引导流程,并可在设备上原生渲染:
- **任意流程类型**:可构建单屏付费墙、包含付费墙的多步骤用户引导,以及介于两者之间的任何形式。
- **原生渲染**:流程通过 Adapty SDK 渲染,无需 Web 视图。
- **无需重新发布即可更新**:随时修改文案、设计或逻辑,更新无需发布新版本即可触达用户。
:::
Adapty **付费墙编辑工具**是一款可视化无代码工具,专为设计自定义付费墙而生。你可以从模板出发,自定义布局,并添加轮播图、卡片、产品列表、页脚等元素。该工具还支持自定义字体、产品标签和本地化。
付费墙编辑工具需要 Adapty SDK v3.0 或更高版本。设计好付费墙后,[将其添加到版位](add-audience-paywall-ab-test)并在应用中展示:
- [iOS](ios-quickstart-paywalls)
- [Android](android-quickstart-paywalls)
- [React Native](react-native-quickstart-paywalls)
- [Flutter](flutter-quickstart-paywalls)
- [Unity](unity-quickstart-paywalls)
- [Capacitor](capacitor-quickstart-paywalls)
- [Kotlin Multiplatform](kmp-quickstart-paywalls)
---
# File: flutterflow
---
---
title: "Adapty FlutterFlow 插件"
description: "将 FlutterFlow 与 Adapty 集成,实现更强大的订阅管理功能。"
---
Adapty 是一个多功能平台,专为帮助移动应用实现增长而设计。无论您是刚刚起步还是已拥有数千名用户,Adapty 都能让您节省数月的应用内购买集成时间,并通过付费墙管理将订阅收入翻倍。
FlutterFlow 的 Adapty 插件让您无需编写任何代码即可使用 Adapty 的全部功能。您可以在 FlutterFlow 中设计付费墙页面,为其启用购买功能,然后远程控制页面上展示的产品,包括针对特定用户群体进行定向推送或开展 A/B 测试。应用发布后,您可以在我们的看板中立即查看客户购买行为的详细分析数据。
想要更新付费墙上的可用产品?非常简单!只需在 Adapty 看板中点击几下即可完成修改,您的客户将立即看到新产品——无需发布新的应用版本!
Adapty 还为您提供以下功能:
- **订阅与应用内购买**:Adapty 为您处理服务端收据验证,并在所有平台(包括 Web)之间同步您的客户数据。
- **付费墙 A/B 测试**:测试不同的价格、时长、试用期和视觉元素,以优化您的订阅和一次性购买方案。
- **强大的数据分析**:访问详细的数据图表,更好地了解并提升应用的变现效果。
- **集成能力**:Adapty 可与 Amplitude、AppsFlyer、Adjust、Branch、Mixpanel、Facebook Ads、AppMetrica、自定义 Webhook 等第三方分析工具无缝连接。
---
# File: ff-getting-started
---
---
title: "快速入门"
description: "通过 Adapty 功能标志开始个性化订阅流程。"
---
通过 Adapty,您可以在移动应用用户旅程的不同节点(例如用户引导、设置等)创建并运行付费墙和 A/B 测试。这些节点称为[版位](placements)。应用中的一个版位可以同时管理多个付费墙或 [A/B 测试](ab-tests),每个付费墙或测试面向特定的用户群体,我们称之为[目标受众](audience)。此外,您还可以对付费墙进行实验,在不发布新版本的情况下随时替换付费墙。唯一需要硬编码到移动应用中的是版位 ID。
Adapty 库会根据 Adapty 看板中的最新产品持续更新您的付费墙。它会[获取产品数据](ff-action-flow)并[在付费墙上展示](ff-add-variables-to-paywalls),[处理购买](ff-make-purchase),以及[检查用户的访问等级](ff-check-subscription-status)以确定是否向其开放付费内容。
要开始使用,只需按照以下步骤将 [Adapty 库添加](ff-getting-started#add-the-adapty-library-as-a-dependency)到您的 FlutterFlow 项目中,并[初始化它](ff-getting-started#initiate-adapty-plugin)。
:::warning
开始之前,请注意以下限制:
- 适用于 FlutterFlow 的 Adapty 库不支持 Web 应用。请避免使用它编译 Web 应用。
- 适用于 FlutterFlow 的 Adapty 库不支持通过 Adapty 付费墙编辑工具创建的付费墙。您需要在 FlutterFlow 中自行设计付费墙,然后再通过 Adapty 启用购买功能。
:::
## 将 Adapty 库添加为依赖项 \{#add-the-adapty-library-as-a-dependency\}
1. 在 [FlutterFlow Dashboard](https://app.flutterflow.io/dashboard) 中,打开您的项目,然后从左侧菜单点击 **Settings and Integrations**。在左侧的 **Project setup** 部分,选择 **Project dependencies**。
2. 在 **FlutterFlow Libraries** 部分,点击 **Add Library** 并输入 `adapty-xtuel0`。点击 **Add**。
3. 现在,您需要将 SDK 密钥与库关联。点击库旁边的 **View details**。
4. 从 Adapty 看板的 [**App Settings** -> **General** 标签页](https://app.adapty.io/settings/general)复制 **Public SDK key**。
5. 将密钥粘贴到 FlutterFlow 中的 **AdaptyApiKey** 字段。
Adapty FF 库现在将作为依赖项添加到您的项目中。在 **Adapty** FF 库窗口中,您将找到已导入项目的所有 Adapty 资源。
## 在应用启动时调用新的激活操作 \{#call-the-new-activation-action-at-application-launch\}
1. 从左侧菜单进入 **Custom Code** 部分,打开 `main.dart`。
2. 点击 **+** 并选择 `activate (Adapty)`。
3. 点击 **Save**。
## 初始化 Adapty 插件 \{#initiate-adapty-plugin\}
为了让 Adapty 看板识别您的应用,您需要在 FlutterFlow 中提供一个特殊密钥。
1. 在您的 FlutterFlow 项目中,从左侧菜单进入 **Settings and Integrations > Permissions**。
2. 在打开的 **Permissions** 窗口中,点击 **Add Permission** 按钮。
3. 在 **iOS Permission Key** 和 **Android Permission Key** 字段中,均粘贴 `AdaptyPublicSdkKey`。
4. 对于 **Permission Message**,从 Adapty 看板的 [**App Settings** -> **General** 标签页](https://app.adapty.io/settings/general)复制 **Public SDK key**。每个应用都有其专属的 SDK 密钥,如果您有多个应用,请确保获取正确的密钥。
完成以上步骤后,您将能够在 FlutterFlow 应用中调用付费墙,并通过它启用购买功能。
## 下一步? \{#whats-next\}
1. [创建操作流](ff-action-flow),用于在 FlutterFlow 中处理 Adapty 付费墙产品及其数据。
2. [将获取到的数据映射到付费墙](ff-add-variables-to-paywalls),即您在 FlutterFlow 中设计的付费墙。
3. [设置购买按钮](ff-make-purchase),使其在点击时通过 Adapty 处理交易。
4. 最后,[添加订阅状态检查](ff-check-subscription-status),以确定是否向用户展示付费内容。
---
# File: ff-action-flow
---
---
title: "步骤 1. 创建展示付费墙数据的流程"
description: "在 Adapty 中设置功能标志操作流程,以个性化用户订阅旅程。"
---
:::important
使用 FlutterFlow 插件时,您无法使用在 Adapty 付费墙编辑工具中创建的付费墙。您必须在 FlutterFlow 中自行实现付费墙页面,并将其连接到 Adapty。
:::
将 Adapty 库作为依赖项添加到您的 FlutterFlow 项目后,接下来需要构建一个流程,用于**从 Adapty 获取付费墙和产品数据,并将其展示在您在 FlutterFlow 中设计的付费墙上**。
首先,我们需要从 Adapty 接收付费墙数据。我们将从请求 Adapty 付费墙开始,然后获取其关联产品,最后检查数据是否成功接收。如果成功,我们将在付费墙页面上显示产品标题和价格;否则,将显示错误消息。
在继续之前,请确保您已完成以下操作:
1. 在 Adapty 看板中[创建至少一个付费墙并向其添加至少一个产品](create-paywall)。
2. 在 Adapty 看板中[创建至少一个版位](create-placement),并[将您的付费墙添加到该版位](add-audience-paywall-ab-test)。
让我们开始吧!
## 步骤 1.1. 请求 Adapty 付费墙 \{#step-11-request-adapty-paywall\}
如前所述,要在您的 FlutterFlow 付费墙中显示数据,我们首先需要从 Adapty 获取数据。第一步是获取 Adapty 付费墙本身。操作如下:
1. 打开您的付费墙屏幕,在右侧面板切换到 **Actions** 部分,然后打开 **Action Flow Editor**。
2. 在 **Select Action Trigger** 窗口中,选择 **On Page Load**。
3. 点击 **Add Action**,然后搜索 `getPaywall` 自定义操作并选择它。
4. 在 **Set Actions Arguments** 部分,输入您在 Adapty 看板中[创建的版位](create-placement)的真实 ID,该版位包含付费墙。在本示例中为 `monthly`。请务必使用您真实的版位 ID!
5. 如果您已在 Adapty 看板中对付费墙进行了[本地化](localizations-and-locale-codes),还可以设置 **locale** 参数。
6. 在 **Action Output Variable Name** 中,创建一个新变量并将其命名为 `getPaywallResult`。我们将在下一步中使用它来引用 Adapty 付费墙并请求其产品。
## 步骤 1.2. 请求 Adapty 付费墙产品 \{#step-12-request-adapty-paywall-products\}
很好!我们已经获取了 Adapty 付费墙。现在,让我们获取与该付费墙关联的产品:
1. 点击已创建操作下方的 **+** 并选择 **Add Action**。此操作将接收 Adapty 付费墙产品。为此,搜索并选择 `getPaywallProducts`。
2. 在 **Set Actions Arguments** 部分,选择之前创建的 `getPaywallResult` 变量。
3. 按如下方式填写其他字段:
- **Available Options**:Data Structured Field
- **Select Field**:value
- **Available Options**:无需进一步更改
4. 点击 **Confirm**。
5. 在 **Action Output Variable Name** 中,创建一个新变量并将其命名为 `getPaywallProductsResult`。我们将使用它将您在 FlutterFlow 中设计的付费墙与 Adapty 付费墙数据进行映射。
## 步骤 1.3. 添加检查付费墙是否成功加载 \{#step-13-add-check-if-the-paywall-uploaded-successfully\}
在继续之前,让我们验证 Adapty 付费墙是否已成功接收。如果是,我们可以用产品数据更新付费墙;如果不是,我们将处理错误。以下是添加检查的方法:
1. 点击 **+** 并点击 **Add Conditional**。
2. 在 **Action Output** 部分,选择之前创建的操作输出变量(在本示例中为 `getPaywallResult`)。
3. 要验证 Adapty 付费墙是否已接收,请检查是否存在包含值的字段。按如下方式填写字段:
- **Available Options**:Has Field
- **Field (AdaptyGetPaywallResult)**:value
4. 点击 **Confirm** 以完成条件设置。
## 步骤 1.4. 记录付费墙查看事件 \{#step-14-log-the-paywall-review\}
为确保 Adapty 分析能够追踪付费墙查看事件,我们需要记录此事件。如果没有此步骤,该查看将不会被计入分析数据。操作如下:
1. 点击 **TRUE** 标签下方的 **+** 并点击 **Add Action**。
2. 在 **Select Action** 字段中,搜索并选择 **logShowPaywall**。
3. 在 **Set Action Arguments** 区域点击 **Value**,然后选择我们创建的 `getPaywallResult` 变量。该变量包含付费墙数据。
4. 按如下方式填写字段:
- **Available Options**:Data Structured Field
- **Select Field**:value
5. 点击 **Confirm**。
## 步骤 1.5. 如果未收到付费墙则显示错误 \{#step-15-show-error-if-paywall-not-received\}
如果未收到 Adapty 付费墙,您需要[处理错误](error-handling-on-flutter-react-native-unity#system-storekit-codes)。在本示例中,我们将简单地显示一条警告消息。
1. 向 **FALSE** 标签添加一个 **Informational Dialog** 操作。
2. 在 **Title** 字段中,添加您希望作为对话框标题显示的文本。在本示例中为 **Error**。
3. 点击 **Message** 框中的 **Value**。
4. 按如下方式填写字段:
- **Set Variable**:我们创建的 `getPaywallProductResult` 变量
- **Available Options**:Data Structure Field
- **Select Field**:error
- **Available Options**:Data Structure Field
- **Select Field**:errorMessage
5. 点击 **Confirm**。
6. 向 **FALSE** 流程添加一个 **Terminate action**。
7. 点击右上角的 **Close**。
恭喜!您已成功接收产品数据。现在,让我们[将其映射到您在 FlutterFlow 中设计的付费墙](ff-add-variables-to-paywalls)。
---
# File: ff-add-variables-to-paywalls
---
---
title: "步骤 2. 向付费墙页面添加数据"
description: "将 Feature Flag 变量添加到 Adapty 的付费墙中。"
---
在[获取所有必要的产品数据](ff-action-flow)之后,是时候将其映射到您在 FlutterFlow 中设计的精美付费墙上了。在本示例中,我们将映射产品标题及其价格。
## 步骤 2.1. 向付费墙页面添加产品标题 \{#step-21-add-product-title-to-paywall-page\}
1. 双击付费墙页面上的产品文本。在 **Set from Variable** 窗口中,搜索 `getPaywallProductResult` 变量并选择它。
2. 按如下方式填写字段:
- **Available Options**:Data Structured Field
- **Select Field**:value
- **Available Options**:Item at Index
- **List Index Options**:First
- **Available Options**:Data Structured Field
- **Select Field**:localizedTitle
- **Default Variable Value**:null
- **UI Builder Display Value**:任意内容,本示例中为 `product.title`
3. 点击 **Confirm** 保存更改。
## 步骤 2.2. 向付费墙页面添加价格文本 \{#step-22-add-price-text-to-paywall-page\}
按照步骤 2.1 中的操作,对价格文本重复以下步骤:
1. 双击付费墙页面上的价格文本。在 **Set from Variable** 窗口中,搜索 `getPaywallProductResult` 变量并选择它。
2. 按如下方式填写字段:
- **Available Options**:Data Structured Field
- **Select Field**:value
- **Available Options**:Item at Index
- **List Index Options**:First
- **Available Options**:Data Structured Field
- **Select Field**:price
- **Default Variable Value**:null
- **UI Builder Display Value**:任意内容,本示例中为 `product.price`
3. 点击 **Confirm** 按钮保存更改。
### 向付费墙页面添加本地货币价格 \{#add-price-in-local-currency-to-paywall-page\}
1. 双击付费墙页面上的价格。在 **Set from Variable** 窗口中,搜索 `getPaywallProductResult` 变量并选择它。
2. 按如下方式填写字段:
- **Available Options**:Data Structured Field
- **Select Field**:value
- **Available Options**:Item at Index
- **List Index Options**:First
- **Available Options**:Data Structured Field
- **Select Field**:price
- **Available Options**:Data Structured Field
- **Select Field**:amount
- **Available Options**:Decimal
- **Decimal Type**:Automatic
- **Default Variable Value**:null
- **UI Builder Display Value**:任意内容,本示例中为 `price.amount`
3. 点击 **Confirm** 保存更改。
大功告成!现在,当您启动应用时,它将直接在付费墙页面上展示来自 Adapty 付费墙的产品数据!
接下来,是时候[让用户购买该产品](ff-make-purchase)了。
---
# File: ff-make-purchase
---
---
title: "步骤 3. 启用购买"
description: "了解如何使用 Adapty 的功能标志系统进行购买。"
---
恭喜!您已成功[设置付费墙以显示来自 Adapty 的产品数据](ff-add-variables-to-paywalls),包括产品标题和价格。
现在,让我们继续最后一步——让用户通过付费墙进行购买。
## 步骤 3.1. 启用用户进行购买 \{#step-31-enable-users-to-make-purchases\}
1. 双击付费墙页面上的购买按钮。在右侧面板中,打开 **Actions** 部分(如果尚未打开)。
2. 打开 **Action Flow Editor**。
3. 在 **Select Action Trigger** 窗口中,选择 **On Tap**。
4. 在 **No Actions Created** 窗口中,点击 **Add Action**。搜索 `makePurchase` 动作并选择它。
5. 在 **Set Actions Arguments** 部分,选择之前创建的 `getPaywallProductsResult` 变量。
6. 按如下方式填写字段:
- **Available Options**: Data Structure Field
- **Select Field**: value
- **Available Options**: Item at Index
- **List Index Options**: First
7. 点击 `subscriptionUpdateParameters`,搜索 `AdaptySubscriptionUpdateParameters` 并选择它。点击 **Confirm**。
:::info
默认情况下,您可以将所有对象字段留空。如果需要在 Android 应用中将一个订阅替换为另一个订阅,则需要填写这些字段。详情请阅读[此处](https://android.adapty.io/adapty/com.adapty.models/-adapty-subscription-update-parameters/)。
:::
8. 点击 **Confirm**。
9. 在 **Action Output Variable Name** 中,创建一个新变量并将其命名为 `makePurchaseResult`——稍后将用于确认购买是否成功。
## 步骤 3.2. 检查购买是否成功 \{#step-32-check-if-the-purchase-was-successful\}
现在,让我们设置一个检查,以确认购买是否已完成。
1. 点击 **+** 并点击 **Add Conditional**。
2. 在 **Set Condition for Action** 中,选择 `makePurchaseResult` 变量。
3. 在 **Set Variable** 窗口中,按如下方式填写字段:
- **Available Options**: Has Field
- **Select Field**: profile
4. 点击 **Confirm**。
## 步骤 3.3. 打开付费内容 \{#step-33-open-paid-content\}
如果购买成功,您可以解锁付费内容。以下是设置方法:
1. 点击 **TRUE** 标签下的 **+**,然后点击 **Add Action**。
2. 在 **Define Action** 字段中,从 **Navigate To** 列表中搜索并选择您要打开的页面。在此示例中,该页面为 **Questions**。
## 步骤 3.4 购买失败时显示错误消息 \{#step-34-show-error-message-if-purchase-failed\}
如果购买失败,让我们向用户显示一个提示。
1. 向 **FALSE** 标签添加一个 **Informational Dialog** 动作。
2. 在 **Title** 字段中,输入对话框标题的文字,例如 **Purchase Failed**。
3. 在 **Message** 框中点击 **Value**。在 **Set from Variable** 窗口中,搜索 `makePurchaseResult` 并选择它。按如下方式填写字段:
- **Available Options**: Data Structure Field
- **Select Field**: error
- **Available Options**: Data Structure Field
- **Select Field**: errorMessage
4. 点击 **Confirm**。
5. 向 **FALSE** 流程添加一个 **Terminate** 动作。
6. 最后,点击右上角的 **Close**。
恭喜!您的用户现在可以购买您的产品了。作为额外步骤,让我们[在其他地方设置用户对付费内容的访问检查](ff-check-subscription-status),以决定是向他们显示付费内容还是付费墙。
---
# File: ff-check-subscription-status
---
---
title: "步骤 4. 检查付费内容访问权限"
description: "了解如何使用 Adapty 的功能标志检查订阅状态,以实现更好的用户细分。"
---
在判断用户是否有权访问特定付费内容时,您需要验证其访问等级。这意味着需要检查用户是否至少拥有一个访问等级,以及该等级是否符合要求。
您可以通过检查用户画像来完成此操作,用户画像中包含所有可用的访问等级。
现在,让我们允许用户购买您的产品:
1. 双击应显示付费内容的按钮,并在右侧面板中打开 **Actions** 部分(如果尚未打开)。
2. 打开 **Action Flow Editor**。
3. 在 **Select Action Trigger** 窗口中,选择 **On Tap**。
4. 在 **No Actions Created** 窗口中,点击 **Add Conditional Action** 按钮。
5. 点击 **UNSET** 以设置操作参数,然后选择 `currentProfile` 变量。这是 Adapty 中保存当前用户画像数据的变量。
6. 按如下方式填写字段:
- **Available Options**:Data Structure Field
- **Select Field**:accessLevels
- **Available Options**:Filter List Items
- **Filter Conditions**:
1. 选择 **Conditions -> Single Condition**,然后点击 **UNSET**。
2. 在 **First value** 字段中,将 **Source** 选择为 **Item in list**,并按如下方式填写字段:
- **Available Options**:Data Structure Field
- **Select Field**:accessLevelIdentifier
3. 将过滤运算符设置为 **Equal to**。
4. 点击 **Second value** 旁边的 **UNSET**,在 **Value** 字段中输入您的访问等级 ID;在本示例中,我们使用 `premium`。
5. 点击 **Confirm**,然后继续填写下方的其他字段。
- **Available Options**:Item at Index
- **List Index Options**:First
- **Available Options**:Data Structure Field
- **Select Field**:accessLevel
- **Available Options**:Data Structure Field
- **Select Field**:isActive
7. 点击 **Confirm**。
现在,添加后续操作——根据用户是否拥有正确的订阅来决定下一步。可以将其引导至高级订阅用户可访问的页面,或打开付费墙页面让其购买访问权限。
---
# File: ff-resources
---
---
title: "Adapty FlutterFlow 插件操作与数据类型"
description: "访问 Adapty 的功能标志资源,以简化基于订阅的功能。"
---
## 自定义操作 \{#custom-actions\}
以下是通过 Adapty 插件传递给 FlutterFlow 的 Adapty 方法。它们可以在 FlutterFlow 中用作自定义操作。
| 自定义操作 | 描述 | 操作参数 | Adapty 数据类型 - 操作输出变量 |
|---|----|--------|----|
| activate | 初始化 Adapty SDK | 无 ||
| getPaywall
| 获取付费墙。该操作不返回付费墙产品,请使用 `getPaywallProducts` 操作获取实际产品 |getPaywallProducts
| 返回实际付费墙产品列表 | [AdaptyPaywall](ff-resources#adaptypaywall) | [AdaptyGetProductsResult](ff-resources#adaptygetproductsresult) | |getProductsIntroductoryOfferEligibility
| 检查用户是否符合 iOS 订阅新用户优惠的资格 | [AdaptyPaywallProduct](product) | [AdaptyGetIntroEligibilitiesResult](ff-resources#adaptygetintroeligibilitiesresult) | |makePurchase
| 完成购买并解锁内容。如果付费墙有促销活动,Adapty 会在结账时自动应用 |getProfile
|获取当前应用用户的用户画像,以便设置访问等级及其他参数。
如果获取失败(例如因为没有网络),将返回缓存数据。Adapty 会定期更新用户画像缓存,以确保信息尽可能保持最新。
| 无 | [AdaptyGetProfileResult](ff-resources#adaptygetprofileresult) | | updateProfile | 修改当前用户画像的可选属性,如电子邮件、电话号码等。您可以使用这些属性创建用户[市场细分](segments),或直接在 CRM 中查看 | [AdaptyProfile](ff-resources#adaptyprofile) 的 ID 及需要更新的任意参数 | [AdaptyError](ff-resources#adaptyerror)(可选) | | restorePurchases | 恢复用户已完成的购买 | 无 | [AdaptyGetProfileResult](ff-resources#adaptygetprofileresult) | | logShowPaywall | 记录特定付费墙向用户展示的事件 | [AdaptyPaywall](ff-resources#adaptypaywall) | [AdaptyError](ff-resources#adaptyerror)(可选) | | identify | 使用您系统的 `customerUserId` 识别用户 | customerUserId | [AdaptyError](ff-resources#adaptyerror)(可选) | | logout | 将当前用户退出登录 | 无 | [AdaptyError](ff-resources#adaptyerror)(可选) | | presentCodeRedemptionSheet | 显示允许用户兑换码的界面(仅限 iOS) | 无 | 无 | ## 数据类型 \{#data-types\} Adapty 数据类型(数据值的集合)通过 Adapty 插件传递至 FlutterFlow。 ### AdaptyAccessLevel 关于用户[访问等级](access-level)的信息。 | 字段名称 | 类型 | 描述 | |--------------------------|----------|-------------| | activatedAt | DateTime | 此访问等级的激活时间 | | activeIntroductoryOfferType | String | 当前生效的新用户优惠类型。若已设置,表示在此订阅周期内应用了优惠 | | activePromotionalOfferId | String | 当前生效的促销活动 ID(从 iOS 购买)| | activePromotionalOfferType | String | 当前生效的促销活动类型(从 iOS 购买)。若已设置,表示在此订阅周期内应用了优惠 | | billingIssueDetectedAt | DateTime | 检测到账单问题的时间。订阅可能仍处于有效状态。若付款处理成功,则设为 null | | cancellationReason | String | 订阅被取消的原因 | | expiresAt | DateTime | 访问等级的到期时间(可能已过期,或对于永久授权未设置此字段)| | id | String | 访问等级的标识符 | | isActive | Boolean | 若此访问等级处于激活状态则为 true。通常可通过此属性判断用户是否有权访问高级功能 | | isInGracePeriod | Boolean | 若此自动续期订阅处于[宽限期](https://developer.apple.com/help/app-store-connect/manage-subscriptions/enable-billing-grace-period-for-auto-renewable-subscriptions)则为 true | | isLifetime | Boolean | 若此访问等级为永久授权(无到期日期)则为 true | | isRefund | Boolean | 若此购买已退款则为 true | | offerId | String | 当前生效的促销活动 ID(从 Android 购买)| | renewedAt | DateTime | 访问等级上次续期的时间 | | startsAt | DateTime | 此访问等级的开始时间(可能为将来的时间)| | store | String | 购买发生的商店 | | unsubscribedAt | DateTime | 订阅关闭自动续期的时间。订阅可能仍处于有效状态。若未设置,表示用户已重新激活订阅 | | vendorProductId | String | 解锁此访问等级的商店产品 ID | | willRenew | Boolean | 若此自动续期订阅已设置为续期则为 true | ### AdaptyAccessLevelIdentifiers 此结构体用于替换 `Map
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` 文件中,确保根标签 `
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)。
可选
默认值:`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可选
默认值:`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 ) { } ```
## 付费墙视图数量过大 \{#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),其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。
:::
可选
默认值:`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` 属性。这是一个最多包含两个折扣阶段的列表:免费试用阶段和新用户优惠价格阶段。每个阶段对象包含以下有用属性:可选
默认值:`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\}一个 [`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)。" ---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` 属性获取。 |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),即可确认其订阅状态。[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。`可选
默认值:`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)
}
```
:::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
}
```
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