Apple iOS App 转到新公司主体:先回答一个问题,它上架过没有

最近在处理一件事:公司主体变了,手上的 App 要从老账号挪到新主体的开发者账号底下。
我第一反应是,这不就是 App Store Connect 里点一下 Transfer App 吗。
把 Apple 的官方文档翻完、整理成一份 SOP 之后,结论是这样的:点按钮那一步确实只要半小时,前面能卡你三周,后面还埋着两个 60 天的时限。其中一个到期,全部老用户的登录态就再也接不回来了。
这篇按那份 SOP 讲一遍。赶时间的话,先看这六条:
1)判断标准只有一条:这个 App 至少有一个版本正式上架过 App Store。纯 TestFlight 不算,哪怕外部测试跑了好几轮、Beta 审核也过了。见第一节。
2)接收方必须是组织类型的 Apple Developer Program(99 美元/年),不是 Enterprise Program(299 美元/年)。中文里两个都被叫"企业账号",选错这条路直接断。第二节。
3)App 转移完成后会从原账号彻底移除。销售报表、Analytics、内购配置在那之后原账号也查不到了,必须提前导出。第三节。
4)两个 60 天:接收方要在 60 天内接受;接受之后,Sign in with Apple 的用户标识要在 60 天内兑换完。第四节。
5)Bundle ID、评分评价、用户会跟着走;推送证书、Keychain、merchant ID、provisioning profile、TestFlight 全都不跟着走。第五节有速查表。
6)如果它真的从没上架过,原 Bundle ID 你永远拿不回来,这条目前没有例外。第六节。
一、决定整件事的,是"它上架过没有"
Apple 在转移资格里写得很直白:
The app must have at least one version released to the App Store.
出自 App Store Connect Help · App transfer criteria
只在 TestFlight 上跑过的 App 不满足这一条。外部测试做了十轮、Beta App Review 也过了,都不算。你点 Transfer App 会被直接拦住。
所以整件事的第一步不是准备材料,是回答这个问题:

这里有个容易被忽略的点。如果 App 还没上架,但你又特别想保住原来的 Bundle ID,其实还有第三条路:先在原主体下正式上架一个版本,让 App 满足转移资格,然后走标准转移。Bundle ID 就能完整带走。
代价是多走一次 App Review,加上整套转移流程。什么情况下值得:Bundle ID 有对外的品牌含义,或者第三方 SDK、服务端、支付渠道已经按它做了配置。
二、时间几乎全花在账号上,不是流程上
先说一个中文语境里特别容易翻车的坑。
接收方的账号必须是 Apple Developer Program 的组织(Organization)类型,99 美元一年。不是 Apple Developer Enterprise Program,299 美元那个。
Enterprise Program 只能做企业内部分发,它压根不具备 App Store 上架能力,所以也没资格当 App 转移的接收方。这两个在中文里经常被混称为"企业账号",谈之前务必问清楚对方注册的到底是哪个。
新主体账号如果还没注册,这就是整个周期里最大的变量。要准备的东西:
还有一条特别实际的:发起和接受,都只能由 Account Holder 本人操作,Admin 不行。提前确认这个人在、且愿意配合,别到那一步才发现人在休假。
整体时间是这样分布的:

三、发起之前,有些东西删了就再也找不回来
转移完成之后,这个 App 会从原账号彻底移除。到那时候再想取历史数据,来不及了。
必须提前导出到本地的:
另外有三样东西是必须清空的,不清空发起会被拦住:
1)TestFlight:移除这个 App 的所有构建和测试员,并且清空每种语言下 Test Information 的所有字段。语言多的时候这一步比想象中磨人,得一个个清。
2)Xcode Cloud 数据:在 Settings → Xcode Cloud 里删掉。注意这会丢失该 App 的构建历史。
3)Sign in with Apple 的 App 分组:如果为它做过跨平台分组,发起之前必须先解除。
顺便说一条最容易被忽略、又直接导致转移失败的:内购的 Product ID 不能和接收方账号里已有的重复。让接收方先导出一份现有内购 ID 清单,两边比对一遍,几分钟的事,能省掉一次白跑。
还有个顺序问题值得单独拎出来。如果 App 有自动续期订阅:
最后提醒一句:发起之后,转出方就不能再编辑元数据、改定价、调区域、动内购了,所有未结束的审核沟通也会被关闭。所以任何计划中的版本更新、调价、活动配置,要么在发起之前做完,要么推到转移完成后由接收方来做。
好消息是,在接收方点接受之前,双方任何一方都可以取消。一旦点了接受,就进入不可逆流程了。
四、两个 60 天,性质完全不同
第一个 60 天:接收方必须在 60 天内接受这次转移。逾期请求失效,前面那堆清理和备份要重来一遍。这个好理解,把截止日直接写进日历就行。
第二个 60 天藏在 Sign in with Apple 里,错过了没有补救办法。
Sign in with Apple 给你的用户标识 sub 是按团队隔离的。同一个人,在新团队底下会拿到一个完全不同的 sub。也就是说,转移完成之后,所有老用户登录进来都是全新账号,你数据库里的历史数据一条都对不上。
Apple 给的解法是一个中间桥梁:转移标识 transfer_sub。

转出方这边做两步。第一步,取一个 scope 为 user.migration 的访问令牌:
POST/auth/tokenHTTP/1.1Host:appleid.apple.comContent-Type:application/x-www-form-urlencodedgrant_type=client_credentials&scope=user.migration&client_id={App ID 或 Services ID,不含 Team ID}&client_secret={JWT,sub 声明为 bundle ID 或 Services ID}几个容易填错的地方:
client_id 里不要带 Team ID 前缀,只填 App ID 或 Services IDclient_secret 是你自己签的 JWT,它的 sub 声明填 bundle ID 或 Services IDaccess_token 只有 3600 秒有效期,用户量大的话要在批处理里处理好续期第二步,为每个用户换取转移标识:
POST/auth/usermigrationinfoHTTP/1.1Host:appleid.apple.comContent-Type:application/x-www-form-urlencodedAuthorization:Bearer {access_token}sub={该用户当前的 team-scoped 标识}&target={接收方 Team ID}&client_id={...}&client_secret={...}返回长这样:
{"transfer_sub":"760417.ebbf...1827"}这一步可以在接收方接受转移之前就开始跑,不用等。
第三步在接收方手里:转移完成后,用同一个接口把 transfer_sub 兑换成本团队的新 sub,写回自己的用户表。这一步的截止时间是接受转移之日起 60 天,逾期之后 transfer identifier 失效,用户关联再也接不回来。
用户量大的时候,要给批量跑批和失败重试留出时间,别拖到最后一周才开始。
顺带两条隐私上的硬要求,别踩:
transfer_sub,必须剔除原来的 team-scoped 标识和 Apple 私有中继邮箱地址五、转过去之后,哪些东西没跟着走
这一节是整份 SOP 里我认为最值钱的部分。因为它列的这些问题,通常要等到接收方提交下一个版本的时候才暴露出来,那时候排查起来非常被动。
| 需迁移 | ||
grp. 前缀、退回原始 ID,硬编码的地方要核查 | ||
| 需重建 | .p8 密钥的话新建或复用接收方已有的,两种都必须更新推送服务端配置 | |
| 需重建 | ||
这张表里我最想提醒两条。
一是 Keychain。它的共享只在 App 更新之前继续有效。等接收方发了转移后的第一个版本,App 就再也读不到原来 keychain 里的鉴权令牌了,所有用户会被登出一次。这件事本身没法避免,但你可以提前把它写进版本更新说明,或者做个应用内提示,别让用户一脸懵地以为账号丢了。
二是回归测试里的"从旧版本升级"。全新安装一般都会测,升级路径容易被跳过。但恰恰是升级这条路径,才会暴露 keychain 和用户标识的问题。真机上必须走一遍:全新安装、从旧版本升级、登录(含 Sign in with Apple)、内购与恢复购买、推送接收、iCloud 同步。
还有个建议:转移后的第一次提交,别攒成一个大版本。先用一个低风险小更新把签名、证书、隐私、内购配置整条链路验通,再发正经版本。
另外两个可能卡住流程的情况,提前知道能省几天:
六、要是它真的从没上架过
那就转不了,只能在新账号重建。
好消息是重建比转移轻得多:没有真实用户、没有评分评价、没有订阅收入要保全,也就不涉及 60 天时限、共享密钥交接、用户标识迁移那一整套。账号就绪的话,一周左右能恢复到当前的 TestFlight 状态(重建 2–4 天 + Beta 审核 1–3 天)。
坏消息只有一条,但它无解:原 Bundle ID 拿不回来。
Apple 关于删除 App ID 的规定是:已经上传到 App Store Connect 的 App,它的 explicit App ID 不能删除。你的 App 传过 TestFlight 构建,就属于"已上传到 App Store Connect"。这个 App ID 会永久留在原账号里,删不掉。而 Bundle ID 在所有开发者账号之间是唯一的,只要它还存在于原账号,新主体就注册不了同一个,只能换新的。
所以重建之前只有一个决策点:原 Bundle ID 的外部依赖有多少。逐项排查这几处:
apple-app-site-association依赖少、ID 也没什么对外含义,那就换个新的重建,几天的事。依赖多、或者 ID 带品牌含义,就回头看第一节的第三条路,先在原主体上架一个版本,再走标准转移。
重建时几个具体的点:
apple-app-site-association 里的 App ID 前缀(也就是 Team ID)变了,得重新部署到域名根目录transfer_sub 那套机制只在真正的 App Transfer 里可用,重建走不了。测试库里的账号会全部对不上,如果已经积累了有意义的数据,得提前设计用邮箱之类的字段做人工对齐最后
App 转移的难点不在操作,在排期。
给几个可以直接拿去排的数:双方账号都就绪、App 状态干净,转移动作 2–3 天能走完;算上前置清理、订阅密钥交接、Sign in with Apple 准备,预留 1–2 周比较稳;新主体账号还没注册的话,整体按一个月以上排。转移之后的收尾和验证,再算 1–2 周。
也说一句这份 SOP 覆盖不到的:资产转让协议、App 知识产权与商标归属、隐私政策和用户协议的主体变更、必要的用户告知。这些法务和商务的事,技术流程一件都解决不了,但它们往往才是真正的关键路径。技术这条线可以提前跑,别等法务落地了才开始准备账号。
上面所有规则都来自 Apple 官方文档(2026 年 8 月核对),主要是这几处:
标了"经验值"的时间估算不是官方口径,只作排期参考。Apple 的政策和界面路径会变,正式动手前建议再核对一次官方页面。
你们做过 App 主体转移吗?卡在哪一步了?评论区聊聊。如果手上正好有这个活儿,建议直接收藏这篇,或者转给负责账号的同事。尤其是第五节那张表,能帮他们少踩几个只在提交更新时才暴露的坑。
关于我
曾在多家大厂做 iOS 资深工程师,也长期坐在面试官这一侧。定位疑难问题、性能与项目优化、架构设计、底层原理,是我过去吃饭的手艺。
现在全力转行 AI 开发,一边从零手搓 Agent 把整条工程链路走通,一边啃 AI 运行框架和算法基础原理。转行路上踩的坑、想明白的事,都会写在这个号里。
如果你也在做 iOS,或者同样在从客户端往 AI 转,欢迎关注,评论区随时交流,一起进步。

夜雨聆风