Karsa Wang 's Blog

记录一些问题,也许对其他人也有所助益


TestFlight 和 Xcode 打包,用的不是同一个沙盒支付身份

测一个带订阅的 App,同一台手机上,TestFlight 里点订阅弹出来的是"Subscribe to unlock",没有试用;换成 Xcode 打包装上去,同一个订阅,弹出来的是"7 天免费试用"。

第一次看到的时候,脑子里第一反应是:App 判定逻辑有 bug,或者试用资格判断写错了。

结果都不是。

真相:两个身份

TestFlight 包和 Xcode 打的包,都跑在 App Store 的沙盒里。但沙盒不是只有一个身份,同一台设备上,这两种包选的是完全不同的两个账号:

构建方式实际使用的沙盒身份
TestFlight 包App Store 里登录的那个 Apple ID 的自动沙盒别名。显示你的个人邮箱、那个账号所属地区的货币(国区 Apple ID 就是人民币),带"仅供测试,不会扣款"的提示
Xcode debug 包设置 → 开发者 → 沙盒账号 里登录的那个

关键是:设置里的沙盒账号对 TestFlight 包完全无效。你辛辛苦苦在开发者设置里登录的沙盒账号,TestFlight 包根本不读它。

为什么表现不一样

因为试用资格是按 (app, Apple ID) 这个维度算的,而两个身份的试用消耗状态、storefront 货币本来就是两份独立的数据。

  • Xcode 包用的沙盒账号 → 没用过这个订阅 → 有试用
  • TestFlight 包用的个人 Apple ID → 状态是另一份 → 没有试用

两个判定都是对的,只是针对不同的身份。看起来像 bug,其实是两套完全独立的账本。

顺带一个更坑的点:沙盒试用资格只消耗一次,永久失效。调试到一半想重置,没有重置按钮。想重来的唯一办法是换一个没用过这个订阅的沙盒账号。

怎么让 TestFlight 用上沙盒账号

手机 设置 → Apple ID → 媒体与购买项目 → 退出登录,然后重新打开 TestFlight。

退出之后没有 App Store 账号了,TestFlight 购买就会回落到开发者设置里的沙盒账号。

怎么一眼看出当前用的是谁

看 Apple 的购买弹窗本身,三个信息就够了:

  • 弹窗上显示的邮箱——是你个人邮箱还是沙盒账号
  • 显示的货币——人民币说明用的是国区 Apple ID 的沙盒别名,美元说明是别的 storefront
  • 有没有**“仅供测试,不会扣款”**的沙盒标注

比在代码里加日志快得多。

吐槽一下

都 2026 年了。

这件事难受的地方不在于难,在于它没有任何一处会告诉你。界面不提示当前在用哪个身份,日志里不打印,文档里也不写清楚(Apple 的文档只说"沙盒测试",从来不区分这两种包用不同的身份)。你只能靠自己盯着两个弹窗的差异,反推出"哦原来是两个账号",然后才有机会想到去查开发者设置里的沙盒账号这一层。

整个排查过程就是:症状(试用不一致)→ 猜测(App 逻辑 bug)→ 排除(代码没错)→ 观察(弹窗邮箱不一样)→ 反推(两个身份)→ 验证(退出媒体与购买项目,问题消失)。前后折腾了挺久,全程没有一步是有人提前告诉我的。

而且这个问题只在跨构建方式对比的时候才会暴露。如果你从头到尾只测 TestFlight,或者只测 Xcode 包,永远不会碰到。你得恰好在同一个设备上把两种包都装一遍、并且恰好注意到试用文案不一样,才会被绊住。

对交付的一点影响

顺带记一个结论,这个直接影响验收流程:

客户通过 TestFlight 测,他用的永远是他自己 Apple ID 的沙盒别名 —— 他自己的货币、没买过就是全新试用。所以提前建好的沙盒测试账号,对 TestFlight 包来说是不生效的,没什么用。

客户那边什么都不用给,他第一次在 TestFlight 里点订阅,试用会自然出现。反过来说,我们自己在 Xcode 包上把某个沙盒账号的试用消耗光了,客户那边完全不受影响,两边是两份账。

这条要是早知道,验收环节能省掉不少来回确认的沟通。