# REACT-NATIVE - 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.398Z Total files: 45 --- # File: sdk-installation-react-native-expo --- --- title: "Install & configure Adapty React Native SDK in an Expo project" description: "Step-by-step guide on installing Adapty React Native SDK in an Expo project for subscription-based apps." --- :::important 本指南介绍如何在 **Expo 项目**中安装和配置 Adapty React Native SDK。 如果你使用的是**纯 React Native(不含 Expo)**,请参阅 [React Native 安装指南](sdk-installation-react-native-pure)。 ::: Adapty SDK 包含两个核心模块,可无缝集成到你的 React Native 应用中: - **Core Adapty**:此模块是 Adapty 在您的应用中正常运行的必要组件。 - **AdaptyUI**:如果您使用 [Adapty 付费墙编辑工具](adapty-paywall-builder)——一款无需编写代码即可轻松创建跨平台付费墙的工具,则需要此模块。AdaptyUI 会随核心模块一并自动激活。 如果您需要一份关于如何在 React Native 应用中实现 IAP 的完整教程,请参阅[这篇文章](https://adapty.io/blog/react-native-in-app-purchases-tutorial/)。 :::tip 想看看 Adapty SDK 如何集成到 Expo 应用中的真实示例?请参考我们的示例应用: - [Expo dev build 示例](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo):包含真实购买和付费墙编辑工具的完整功能 - [Expo Go & Web 示例](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/ExpoGoWebMock):使用模拟模式进行测试 ::: 如需完整的实现流程演示,也可以观看以下视频:
### 登录/注册时 \{#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)。