TestFlight 沙盒中 StoreKit 2 的 `Product.products(for:)` 对两个非消耗型内购持续返回空数组

大家好,我正在排查一个 TestFlight 沙盒环境中的 StoreKit 2 商品加载问题,希望获得建议。

一、现象

iOS App 通过 TestFlight 安装(Build 4)后,使用 Product.products(for:) 请求两个非消耗型内购项目。调用不会抛出错误,但始终返回空数组,因此购买页没有任何可购买商品。

products = try await Product.products(for: [
    "com.shidan.qieting.plus.trial.7day",
    "com.shidan.qieting.plus.lifetime"
]).sorted { $0.id < $1.id }

两个 Product ID 分别为:

Product ID类型价格App Store Connect 状态
com.shidan.qieting.plus.trial.7day非消耗型Tier 0 / US$0.00可供审核
com.shidan.qieting.plus.lifetime非消耗型US$5.99可供审核

二、日志

在 TestFlight App 冷启动后,诊断日志记录:

2026-09-03T08:12:08Z [settings] plus products loaded: trialAvailable=false, lifetimeAvailable=false

打开购买页时再次调用,结果仍然为空。没有捕获到 StoreKit 异常,也没有购买流程报错。

三、系统级沙盒验证结果

为排除 App UI 和 StoreKit 商品加载代码的影响,我们还使用同一开发团队创建的 Sandbox Apple Account,在 iOS 的“设置 > 开发者 > Sandbox Apple Account > 管理 > Test Transactions”中直接测试:

  • Bundle ID:com.shidan.qieting
  • Product ID:com.shidan.qieting.plus.lifetime

系统提示:“提供的产品 ID 或套装 ID 无效”。该工具不经过 App 内的购买页或 Product.products(for:) 结果,仍无法识别这组 Bundle ID 与 Product ID。

四、已完成核验

  1. 代码中的 Product ID 与 App Store Connect 中的 Product ID 完全一致。
  2. Release Bundle ID 与 App Store Connect App Record 一致,均为 com.shidan.qieting
  3. App ID 为显式 ID,不是 wildcard App ID。
  4. Certificates, Identifiers & Profiles 中已启用 In-App Purchase capability;Xcode Target 也已启用,Xcode Cloud 使用自动签名。
  5. TestFlight 使用 Archive 构建;共享 Scheme 的 ArchiveAction 没有 StoreKit Configuration 文件。本地 .storekit 文件只用于 Xcode Run/Test,本身不影响 Archive/TestFlight。
  6. 两个项目均已设置价格、175 个 App Store 地区可售,以及简体中文、繁体中文、英语本地化。
  7. 最后一次修改 IAP 元数据已经超过 48 小时。
  8. Paid Apps Agreement、税务信息和银行信息均为有效/正在使用中。
  9. 尚未提交 App 或 IAP 审核;理解为 TestFlight 沙盒测试不应以提交审核为前提。
  10. 欧盟《数字服务法》(DSA)交易商合规信息仍在审核中;但该要求针对欧盟 App Store 产品页的交易商信息。Apple 官方说明中,单独通过 TestFlight 分发不属于在欧盟 App Store 以交易商身份分发,因此这不应影响 TestFlight 沙盒或 Test Transactions。

五、想请教

在上述条件已确认、Product.products(for:) 不报错却返回 [] 的情况下,是否还有容易遗漏的 App Store Connect、沙盒、签名或 TestFlight 配置?

是否有人遇到过“所有 Product ID 均正确、商品完整且全球可售,但 TestFlight 沙盒始终返回空数组”的情况?如果这是 Apple 沙盒端的已知延迟或异常,有什么进一步的排查或反馈渠道?

我可以按需要补充已打码的 App Store Connect 配置截图和完整诊断日志。谢谢。

Answered by ChoiSophie in 904188022

已解决

Apple Developer Forums Post Draft (English)

Title

StoreKit 2 Product.products(for:) returns an empty array for two non-consumable IAPs in TestFlight sandbox

Post

Hello,

I am troubleshooting a StoreKit 2 product-loading issue in the TestFlight sandbox and would appreciate any advice.

Issue

When the iOS app is installed from TestFlight (Build 4), Product.products(for:) is called for two non-consumable In-App Purchases. The call doesn't throw an error, but it consistently returns an empty array, so no products appear in the purchase UI.

products = try await Product.products(for: [
    "com.shidan.qieting.plus.trial.7day",
    "com.shidan.qieting.plus.lifetime"
]).sorted { $0.id < $1.id }

The products are:

Product IDTypePriceApp Store Connect status
com.shidan.qieting.plus.trial.7dayNon-consumableTier 0 / US$0.00Ready to Submit
com.shidan.qieting.plus.lifetimeNon-consumableUS$5.99Ready to Submit

Diagnostic log

On cold launch of the TestFlight app, the diagnostic log shows:

2026-09-03T08:12:08Z [settings] plus products loaded: trialAvailable=false, lifetimeAvailable=false

The same empty result occurs again when opening the purchase page. There is no StoreKit exception and no purchase-flow error.

System-level sandbox verification

To rule out the app UI and the app's product-loading code, I created a Sandbox Apple Account in the same developer team and used iOS Settings > Developer > Sandbox Apple Account > Manage > Test Transactions.

  • Bundle ID: com.shidan.qieting
  • Product ID: com.shidan.qieting.plus.lifetime

The system reports that the provided product ID or bundle ID is invalid. This happens before the app's purchase UI or Product.products(for:) result is involved, so the sandbox does not appear to recognize this bundle/product pair.

Checks completed

  1. The Product IDs in code exactly match the Product IDs in App Store Connect.
  2. The Release bundle identifier exactly matches the App Store Connect app record: com.shidan.qieting.
  3. The App ID is explicit, not wildcard.
  4. In-App Purchase is enabled for the App ID in Certificates, Identifiers & Profiles. The Xcode target also declares the capability, and Xcode Cloud uses automatic signing.
  5. The TestFlight build is an archive. The shared scheme's ArchiveAction has no StoreKit Configuration file; the local .storekit file is only used by Xcode Run/Test actions and doesn't apply to the archived/TestFlight app.
  6. Both products have a price, availability in all 175 App Store storefronts, and Simplified Chinese, Traditional Chinese, and English localizations.
  7. The last IAP metadata change was more than 48 hours ago.
  8. The Paid Apps Agreement, tax information, and banking information are active/in use.
  9. Neither the app nor the IAPs have been submitted for review. My understanding is that submission isn't required to fetch products in the TestFlight sandbox.
  10. EU Digital Services Act (DSA) trader compliance is still under review. Apple describes this as EU App Store product-page trader information, and TestFlight-only distribution isn't App Store trader distribution; therefore, it shouldn't affect TestFlight sandbox product retrieval or Test Transactions.

Question

With all of the above confirmed, what App Store Connect, sandbox, signing, or TestFlight configuration might still cause both Product.products(for:) and the system-level Test Transactions tool to reject these product IDs?

Has anyone encountered a case where the Product IDs, bundle ID, pricing, localizations, and worldwide availability were correct, but the TestFlight sandbox still treated the bundle/product pair as invalid? Is there a known sandbox propagation or backend issue, or another diagnostic step that can identify the missing association?

I can provide redacted screenshots of the App Store Connect product configuration and the Test Transactions error if helpful. Thank you.

Posting notes

  • Do not post API private keys, full banking/tax information, private team configuration, or personal address details.
  • Keep the Product IDs, Bundle ID, code snippet, and diagnostic timestamp unchanged when posting.
Accepted Answer

已解决

@ChoiSophie I have same problem, can you share how you fixed it?

I would really appreciate!! 😃

TestFlight 沙盒中 StoreKit 2 的 &#96;Product.products(for:)&#96; 对两个非消耗型内购持续返回空数组
 
 
Q