IAP 沙箱购买失败:7 个原因 + 完整排查清单

TestFlight 或沙箱测试时 IAP 失败。常见原因:用了真 Apple ID 而非沙箱;App Store Connect 协议 / 银行 / 税务未激活;IAP 产品不在 "Ready to Submit" 类状态。先做:iOS 设置 → App Store → 登出真 Apple ID(购买时会提示沙箱)。

第一次接 IAP,在真机上跑 App(模拟器跑不了真实购买),点 Buy,弹窗出现后立即报 “Your purchase could not be completed”,或者弹窗根本没出来报 “Cannot connect to iTunes Store”,或者弹窗问 Apple ID 密码、拒绝你沙盒账号的凭证。代码没动过;昨天还好的集成今天就崩了。

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

常见原因

按命中率排序。

1. 真 Apple ID 还在 App Store 里登录着

iOS 12 之前要去 Settings → iTunes & App Store 登出真 ID。iOS 12+ 加了 Settings → App Store → Sandbox Account 单独入口,但两者都没设的话 iOS 用你的真 ID 去购买,真 ID 买不了沙盒产品就失败。

如何判断Settings → App Store。滚到 Sandbox Account(iOS 12+)。空着或显示你的真 ID 就是这个。

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

Paid Apps 协议没签、银行 / 税务表无效、状态不是 Active 之前,任何 IAP 产品包括沙盒都跑不通。Apple 在 requestProducts 时静默返回 “product not available”。

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

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

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

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

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

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

如何判断:Xcode → scheme 设置 → Run → Options → StoreKit Configuration。选了文件就 StoreKit 从那里读。要么清掉选择,要么把文件 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. 沙盒账号地区错或密码过期

沙盒账号有地区锁。建的账号在美区但设备地区设成日本,账号买不了。密码也会过期(沙盒密码比生产过期更快)。

如何判断:App Store Connect → Users and Access → Sandbox → Testers。看地区和最近一次密码重置日期。先在 Settings → App Store → Sandbox Account 试登录;密码挂就去 App Store Connect 重置。

7. App ID 没开 In-App Purchase capability

Apple Developer → Identifiers → 你的 App ID → Capabilities 里 In-App Purchase 必须勾上。没勾,即使产品在 App Store Connect 也跑不通。

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

动手前先确认

  • 弄清你在测的是 TestFlight(默认走沙盒)、本地 build(走沙盒)、还是生产(真钱)——行为不同。
  • 确认设备是真机不是模拟器——真实沙盒购买只在真机上跑。
  • 改之前把当前 App Store Connect IAP 产品配置截图备份。
  • 在沙盒账号上确保能稳定复现问题再开始改。

需要收集的信息

  • 购买弹窗的具体错误文案 + Console.app 按你 bundle ID 过滤的日志。
  • 沙盒账号的邮箱、地区、密码重置日期。
  • App Store Connect 协议 / 税务 / 银行的状态。
  • 各 IAP 产品的状态(至少 Ready to Submit)。
  • 你 build 的 bundle ID 和 Developer Portal 里的 App ID。
  • 是否启用了 .storekit 配置文件。

最短修复路径

Step 1:在设备上配沙盒账号

Settings → App Store → Sandbox Account → Sign In。用你在 App Store Connect → Users and Access → Sandbox → Testers 建的沙盒账号。用一个不绑任何真 Apple ID 的唯一邮箱(用 +sandbox alias 也行)。

如果提示 “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 的产品对 Products.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 5:在 build 里直接测

Xcode 在真机上跑 App,触发购买流程。iOS 提示 Apple ID 时用你的沙盒账号(或接受 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 是假成功。

怎么确认已经修好

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

如果还是没修好

  1. 在 App Store Connect → Users and Access → Sandbox → Testers → 点账号 → Clear History 清沙盒账号购买记录。
  2. iOS 设置里把 Sandbox Account 登出再登入。
  3. 删掉 scheme 里的 .storekit 配置强制走真沙盒查询。
  4. 建一个新沙盒账号,地区匹配设备地区;老账号会进入卡死状态。
  5. 用第二个沙盒账号测,排除单账号问题。

预防建议

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

相关阅读

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