本技能旨在为 React Native(Expo)应用提供一套可落地的内购实现规范与代码模板,用于销售一个非消耗型“Premium”产品。其核心设计目标是在不依赖自有服务器的前提下,在 iOS 和 Android 上“诚实地”完成购买、保存、恢复与退款处理。
用途
- 在应用中接入 expo-iap(精确锁定 5.8.0),实现 Premium 内购。
- 提供从契约定义、适配器封装、权益存储、支付墙状态设计到多层级测试的完整流程。
- 通过两个检查脚本(check-purchases.mjs、check-purchase-behaviour.mjs)对实际代码进行规则校验。
主要能力
- 购买流程:先调用 grantPremium 同步保存权益并写入 entitlements 表,返回 ok 后才调用 port.finish(tx) 结束交易;保存失败则展示 save-failed 并保持交易未完成,等待下次启动重试。
- 权益撤销:仅在收到带撤销日期的已验证交易且无更新的有效交易时,才调用 revokePremium;无证据(缺失、读取错误、离线)均保留已保存的 Premium。
- 事件监听:startPremium 先订阅购买事件,再连接商店,然后加载价格,最后通过 getAvailablePurchases({ onlyIncludeActiveItemsIOS: false }) 静默复查。
- 幂等处理:所有处理器幂等;仅在购买中时计数失败;仅在支付墙接受点击后才向商店发起 BUY。
- 恢复购买:区分 restore-failed(同步或读取失败)与 restore-empty(成功同步后无证据),保存失败则为 save-failed。
- 商店不可用:空产品列表、连接失败或离线均视为“商店不可用”,不弹错误对话框;通过 withConnectivity 包装适配器,并在连通性变化和前台恢复时重新检查。
- 价格展示:始终使用商店返回的 displayPrice(storePriceText),禁止在代码或文案中硬编码价格。
- 支付墙状态:每个状态均有视图值、契约中的 test ID 和 reducer case;文本全部来自应用本地化目录。
- 单一写入者:Premium 仅由 premium store 写入(先持久化后发布),只有购买服务和测试代码可调用;entitlements 表不进入数据备份/恢复,新设备通过 Restore 恢复。
- 测试分层:Tier 1 为 Jest 单元测试;Tier 2 为 StoreKit 测试工具(仅限一次性构建)和 Android 许可测试;Tier 3 为 TestFlight 与 Play 测试轨道的人工运行。
使用方式
- 阅读 references/store-and-adapter.md,从仓库根目录运行
npm install -E expo-iap@5.8.0,并在 app.config.ts 的 plugins 中添加裸字符串'expo-iap'。 - 复制 templates/contracts/ 并填写开发者决策(价格点、区域、家庭共享),替换
__PRODUCT_ID__和__PRICE__。 - 按模板路径复制代码:服务、premium store、支付墙 store、entitlements 数据层、配置、测试与 mock。
- 在组合根中接入 ConnectivityPort 与受门控的购买端口,按 examples/purchase-root.tsx 在 AppRoot 中渲染启动逻辑。
- 构建支付墙屏幕,使用契约中的 test ID,并为所有目录添加契约键。
- 运行 Tier 1 命令:
npx jest src/services/purchase src/stores/premium src/stores/paywall test/sqlite/sqlite-entitlement-repository.test.ts scripts/asc scripts/storekit --ci --maxWorkers=2。 - 运行
node ${CLAUDE_SKILL_DIR}/scripts/check-purchases.mjs .和node ${CLAUDE_SKILL_DIR}/scripts/check-purchase-behaviour.mjs .,修复所有 FAIL 直到输出 RESULT: PASS。 - 发布前及升级 expo-iap 后,按 testing-tiers.md 在一次性模拟器上运行 StoreKit 场景,并在 Android 许可测试器上验证。
- 商店控制台步骤由人工完成:协议、税务、银行、应用记录、价格与家庭共享决策、将首个购买附加到版本,以及 Tier 3 运行。
适用场景
- 使用 Expo/React Native 且需要实现一次性非消耗型内购的应用。
- 希望在不搭建自有服务器的情况下,仅靠设备端验证完成购买与恢复的团队。
- 需要严格测试覆盖(单元、沙盒、人工)以确保支付墙各状态可达的项目。
限制
- 默认无服务器,Android 退款后 Premium 会保留(因退款仅从列表移除购买而无撤销日期),这是诚实默认行为;如需改变需参考 server profile。
- expo-iap 必须精确为 5.8.0,且仅由指定适配器导入;禁止使用 useIAP、自动完成、verifyPurchase、iapkitApiKey 等。
- 产品 ID 只能有一个非消耗型,且不可复用;StoreKit 测试文件与 xctest 不得进入发布构建。
- 商店控制台操作必须人工完成,技能不会自动提交或打印密钥。
- 文档中未提供具体的价格、收费模式或平台兼容性细节,需开发者自行在商店控制台决策。