IAP 沙盒购买失败:7 步诊断顺序

沙盒 IAP 有七个环节必须对齐:设备上的 Sandbox Apple Account、地区、App Store Connect 协议、产品状态、bundle ID、capability、StoreKit 配置文件。诊断顺序,已对照 iOS 18+ 与 Xcode 16(截至 2026 年 6 月)。

第一次接 IAP,在真机上跑 App(模拟器跑不了真实沙盒购买,只能用本地 StoreKit 配置文件),点 Buy,结果是这三种之一:购买弹窗出现后立即报 “Your purchase could not be completed”;弹窗根本没出来,报 “Cannot connect to iTunes Store”;或者 Product.products(for:) 返回空数组,paywall 没东西可显示。代码没动过;昨天还好好的集成今天就崩了。

最快修复,按顺序:(1) iOS 18+ 上,在 Settings → Developer → Sandbox Apple Account 登入一个 Sandbox Apple Account(必须先开 Developer Mode)。(2) 确认 App Store Connect 的 Paid Apps 协议、税务、银行全部为 Active。(3) 确认每个 IAP 产品至少是 Ready to Submit。大约 80% 的沙盒失败就是这三项之一。本文剩下的部分会把完整的七步顺序走一遍,让你不用再瞎猜。

沙盒 IAP 的接线比 API 表面看的多得多:设备登入的 Sandbox Apple Account、它的地区和密码状态、App Store Connect 协议、IAP 产品状态、bundle ID、App ID 的 capability,以及任何 StoreKit 配置文件,每一项都要对齐。任何一项漂移,报错信息都含糊得指不到原因。

TestFlight、沙盒、模拟器:先搞清你在哪个环境

这三种行为不一样,认错环境能浪费你几个小时:

Build 来源环境用的 Apple 账号能清记录吗
Xcode → 真机沙盒你登入的 Sandbox Apple Account能,在 App Store Connect
Xcode → 模拟器只有本地 .storekit 文件无(由文件驱动)不适用(在 Xcode 右键 reset)
TestFlight沙盒后端,但用真 Apple ID测试者的正常 Apple ID,不登沙盒账号不能

最大的坑:TestFlight 购买免费、走沙盒后端,但用的是测试者的 Apple ID,不是 Sandbox Apple Account。所以下面”登入沙盒账号”那些步骤适用于 Xcode 真机跑,不适用于 TestFlight。(来源:Apple, Testing subscriptions and IAP in TestFlight。)

常见原因

按命中率排序。

1. 设备上没登 Sandbox Apple Account

这是最常见的单个原因,而且 Apple 改了它的位置。测 IAP 不需要登出你的真 Apple 账号(那只在测 Apple Pay 时才需要)。你只要把一个 Sandbox Apple Account 登进专门的沙盒槽位:

  • iOS 18 及以后Settings → Developer → Sandbox Apple Account。要先开 Developer Mode(Settings → Privacy & Security → Developer Mode,然后重启)。
  • iOS 12 到 17Settings → App Store,滚到 Sandbox Account 那一节。这些版本上,该节只在你用开发签名的 build 尝试过一次购买之后才出现。

两个槽位都没有 Sandbox Apple Account 的话,iOS 就回退用你的真 ID,购买就失败,因为真 ID 买不了沙盒产品。

如何判断:按你的 iOS 版本去上面的路径看。沙盒槽位空着或显示你的真 ID,就是这个原因。(来源:Apple, Create a Sandbox Apple Account。)

2. App Store Connect 协议、银行、税务没填完

Paid Apps 协议没签、银行 / 税务表无效、状态不是 Active 之前,任何 IAP 产品都跑不通,沙盒也一样。Apple 会在 Product.products(for:) 的结果里静默地把这个产品丢掉。

如何判断:App Store Connect → Agreements, Tax, and Banking。看 Action Items 里有没有红,或状态不是 Active。缺 W-9 / W-8BEN、银行过期、协议修订没签都会挡沙盒。

3. IAP 产品是 “Missing Metadata” 或未提交状态

你建了产品起了名,但没加本地化、审核截图、定价。产品永远到不了 StoreKit 能返回的状态。Product.products(for:) 对这个 ID 返回空数组。

如何判断:App Store Connect → 你的 App → In-App Purchases。状态必须是 Ready to SubmitApprovedMissing Metadata 就查询不到。

4. StoreKit 配置文件和生产对不上

你为本地测试加了 .storekit 文件。Xcode 在用它,而不是查 App Store Connect,文件里的 ID 和生产不一致。你代码以为某产品存在,真沙盒里没有。

如何判断Product → Scheme → Edit Scheme → Run → Options → StoreKit Configuration。选了文件,StoreKit 就从那里读。要么设成 None,要么把文件里的 product ID 和 App Store Connect 同步。

5. IAP 注册的 bundle ID 和 build 的不一致

IAP 产品注册在 com.acme.app 下,但 build 的 bundle ID 是 com.acme.app.devcom.acme.app.staging。StoreKit 只返回当前运行 bundle ID 下注册的产品。

如何判断:打开 Info.plist,把 CFBundleIdentifier 和 App Store Connect 里 IAP 产品所在的 App ID 对一遍。任何不一致(包括大小写)都查不到。

6. Sandbox Apple Account 地区错或卡死

沙盒账号锁 storefront。你建的账号在美区 storefront,但设备地区解析到别处,价格和产品可用性就会出问题。账号也会在中断购买或长跑订阅测试后卡死。注意:截至 2026 年,你可以在 App Store Connect 改沙盒账号的地区,但创建后改不了它的邮箱、姓名、密码。

如何判断:App Store Connect → Users and Access → Sandbox。看 storefront / 地区。先在 Settings → Developer → Sandbox Apple Account(iOS 18+)试登入;登不进说明问题在账号本身,不在你 App。卡死了就清它的购买记录(见”如果还是没修好”)或建一个新的。

7. App ID 没开 In-App Purchase capability

Apple Developer → Certificates, Identifiers & Profiles → Identifiers → 你的 App ID → Capabilities 里,In-App Purchase 必须开启。没开的话,即使产品在 App Store Connect 里,build 也跑不通。IAP 需要显式 App ID(不能用通配符 *)。

如何判断:Apple Developer 门户 → 你的 App ID → Capabilities 列表。In-App Purchase 没勾就是它。

需要收集的信息

  • 购买弹窗的具体错误文案,以及 Console.app 按你 bundle ID 过滤的日志(找 AMSStatusCodeSKErrorDomain 那些条目)。
  • 沙盒账号的邮箱和它的 storefront / 地区。
  • App Store Connect 协议 / 税务 / 银行的状态。
  • 各 IAP 产品的状态(至少 Ready to Submit)。
  • 你 build 的 bundle ID 和 Developer Portal 里的 App ID。
  • 运行 scheme 里是否选了某个 .storekit 配置文件。

最短修复路径

Step 1:在设备上配 Sandbox Apple Account

iOS 18+:Settings → Developer → Sandbox Apple Account → Sign In。(设置里没有 Developer 的话,先在 Settings → Privacy & Security → Developer Mode 开 Developer Mode 并重启。)iOS 12–17 上槽位在 Settings → App Store → Sandbox Account,且只在开发签名 build 尝试过一次购买后才出现。

先在 App Store Connect → Users and Access → Sandbox 建账号。用一个还不是 Apple 账号的唯一邮箱(像 you+sandbox@icloud.com 这样的 +sandbox 子地址就行,邮箱子地址是官方支持的)。测 IAP 不需要登出你的真 Apple 账号。

如果提示 “This Apple ID has not yet been used in the iTunes Store”,在设备上点提示接受一次沙盒条款。

Step 2:核对 App Store Connect 协议

App Store Connect → Business → Agreements, Tax, and Banking:

  • Paid Apps 协议状态 Active
  • 银行信息:完整未过期。
  • 税务信息:完整(美区开发者:W-9;非美:W-8BEN,符合条件加 treaty)。
  • “Action Items” 面板里没遗留事项。

任一红条沙盒交易就会静默失败。先修这些再调代码。

Step 3:核对每个 IAP 产品

App Store Connect → 你的 App → In-App Purchases。每个产品都要:

  • Display Name、Description(按 locale)。
  • 定价 tier 设了。
  • 审核截图(任何图都行,不必是真实 UI)。
  • 状态:Ready to SubmitApproved

Missing Metadata 的产品对 Product.products(for:) 不可见。

Step 4:核对代码里的 product ID

// 硬编码集合或从你服务器拉
let productIDs: Set<String> = [
    "com.acme.pro_monthly",
    "com.acme.coins_100"
]

let products = try await Product.products(for: productIDs)
print("Returned \(products.count) products of \(productIDs.count) requested")

Returned 少于 requested 就列出缺的 ID,和 App Store Connect 逐字符核拼写——com.acme.pro_monthlycom.acme.proMonthly 是静默漏掉的差别。

Step 4b:确认没有多余的 .storekit 文件劫持查询

如果 App Store Connect 里一切都对,products 还是返回空,那 Xcode 可能在读本地 StoreKit 配置文件,而不是真沙盒。检查 Product → Scheme → Edit Scheme → Run → Options → StoreKit Configuration。真沙盒测试时这里必须设成 None。选了文件,StoreKit 就只返回那个文件里的产品,永远不查 App Store Connect。

Step 5:在 build 里直接测

Xcode 在真机上跑 App,触发购买流程。iOS 提示 Apple ID 时用你的 Sandbox Apple Account(或接受 Step 1 自动填的)。

看到 “Cannot connect to iTunes Store” 时检查:

  • 设备网络可达。
  • 日期时间正确(TLS 证书校验依赖它)。
  • VPN 关(某些 VPN 路由经过沙盒被屏蔽的地区)。

Step 6:购买成功后在 App Store Connect 核验

App Store Connect → Sales and Trends → Sandbox(或 Transactions 视图)。成功的沙盒购买几分钟内出现。没出现就说明 Apple 那边交易从没完成,你 client 记了一个假成功。用 StoreKit 2 的话,再读 Transaction.currentEntitlements 确认结果,别只信购买回调。

怎么确认已经修好

  • 购买弹窗显示正确产品名和价格(沙盒按沙盒账号 storefront 的货币显示价格)。
  • 点 Buy 认证后弹窗带绿勾消失。
  • Transaction.updates 推出一条 verified transaction,含预期 product ID,并且该 ID 出现在 Transaction.currentEntitlements 里。
  • App Store Connect → Users and Access → Sandbox 几分钟内列出该笔购买。
  • 同沙盒账号再跑一次走 re-purchase 流(订阅自动续;非消耗品立即 restore)。

如果还是没修好

  1. 清沙盒账号购买记录:App Store Connect → Users and Access → Sandbox → 点账号 → Clear Purchase History。这能重置卡死的订阅或中断购买状态。
  2. 在 iOS 设置里把 Sandbox Apple Account 登出再登入(iOS 18+ 在 Settings → Developer → Sandbox Apple Account)。
  3. 把 scheme 的 StoreKit Configuration 设成 None,强制走真沙盒查询(见 Step 4b)。
  4. 建一个新沙盒账号,storefront 匹配设备地区;老账号会卡死。
  5. 用第二个沙盒账号测,排除单账号问题。
  6. 如果是服务器校验收据,把校验请求指向沙盒校验端点(旧版 verifyReceipt 流用 https://sandbox.itunes.apple.com/verifyReceipt;StoreKit 2 / 签名交易校验用 App Store Server API 的沙盒 base URL)。一个常见的假失败是把沙盒收据发给了生产校验端点(状态 21007)。

预防建议

  • 写任何 IAP 代码前先把协议、税务、银行配齐——后面一切都依赖它们。
  • 你打算支持的每个 storefront 都建一个沙盒账号,凭证存团队密码管理器。
  • 仓库里维护 STOREKIT.md 列每个 product ID 及它定义的位置(App Store Connect 加任何 .storekit 文件),每次发版 diff。
  • CI 加一步对沙盒跑 Product.products(for:),任何预期 ID 缺失就 fail。
  • 每次发版前在真机测一次购买;沙盒状态会漂移,这样静默失败能早发现。

FAQ

测 IAP 必须登出我的真 Apple ID 吗? 不用。测 in-app purchase 时,你把一个 Sandbox Apple Account 登进专门的沙盒槽位(iOS 18+ 在 Settings → Developer → Sandbox Apple Account),真账号不动。只有测 Apple Pay 沙盒才需要登出设备 Apple 账号。

为什么 Product.products(for:) 返回空数组? 通常三个原因,按可能性排序:产品还不是 Ready to Submit(卡在 Missing Metadata);Paid Apps 协议 / 税务 / 银行不是 Active;或者 product ID 打错(pro_monthlyproMonthly)。scheme 里选了 .storekit 配置文件也会盖掉 App Store Connect 的产品。

TestFlight 用沙盒账号吗? 不用。TestFlight 走沙盒后端,但用测试者的 Apple ID,且不扣费。TestFlight 不要登 Sandbox Apple Account,而且 TestFlight 的购买记录不能像沙盒账号那样清掉。

代码报成功,但 App Store Connect 里没有这笔交易——怎么回事? client 信了一个并不代表服务器侧购买完成的回调。用 StoreKit 2 的话,对着 Transaction.currentEntitlements 校验,再看 App Store Connect 的 Sandbox 交易。如果服务器校验收据,状态 21007 表示把沙盒收据发给了生产校验端点。

沙盒购买能在 iOS 模拟器里测吗? 碰真沙盒不行。模拟器只能用本地 .storekit 配置文件。任何碰 Apple 沙盒服务器的环节(Sandbox Apple Account 流程、真实交易、App Store Connect 记录)都需要开了 Developer Mode 的真机。

沙盒订阅续期有多快? 比生产快得多,长周期会压缩到几分钟。默认情况下,1 个月的订阅在沙盒里大约每 5 分钟续一次,续 12 次后自动续期停止。你可以在 App Store Connect → Users and Access → Sandbox 按账号调续期速率。

相关阅读

标签: #排查 #App Store #App 审核 #IAP 沙箱 #IAP / 内购