乐于分享
好东西不私藏

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

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

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 转移的接收方。这两个在中文里经常被混称为"企业账号",谈之前务必问清楚对方注册的到底是哪个。

新主体账号如果还没注册,这就是整个周期里最大的变量。要准备的东西:

• 
D-U-N-S 编码,组织类型注册的必需项。已经有就直接用;从零申请一般要几个工作日到两周(这是经验值,Apple 没公布过官方时限)
• 
可公开访问的公司官网 + 企业域名邮箱。社交媒体主页、域名注册商的占位页都不行,注册联系邮箱也得用这个域名
• 
可签约的法律实体名称。不能是商号、别名、DBA、分支机构。这个名字会作为 App Store 上的销售者名称显示出去
• 
有权代表公司签协议的人。所有者、创始人、管理层、资深项目负责人,或者拿到高层授权的员工,Apple 会核验

还有一条特别实际的:发起和接受,都只能由 Account Holder 本人操作,Admin 不行。提前确认这个人在、且愿意配合,别到那一步才发现人在休假。

整体时间是这样分布的:

三、发起之前,有些东西删了就再也找不回来

转移完成之后,这个 App 会从原账号彻底移除。到那时候再想取历史数据,来不及了。

必须提前导出到本地的:

导出什么
为什么现在就得做
各语言的元数据
名称、副标题、描述、关键词、更新日志、截图与预览视频原始文件、图标、年龄分级答卷、隐私清单填写内容
定价与可用区域
各区域价格档、历史价格计划、上架国家清单
Sales and Trends 与财务报告
历史数据不随 App 转移,转移后原账号也无法按该 App 查询
App Analytics
留存、转化、来源,同样不带走
内购完整配置
Product ID、类型、价格、各语言展示文案、订阅群组结构、升降级关系、促销优惠、优惠码
Game Center 配置
排行榜与成就的 ID、本地化文案、分组关系、多人兼容矩阵、matchmaking 规则

另外有三样东西是必须清空的,不清空发起会被拦住:

1)TestFlight:移除这个 App 的所有构建和测试员,并且清空每种语言下 Test Information 的所有字段。语言多的时候这一步比想象中磨人,得一个个清。

2)Xcode Cloud 数据:在 Settings → Xcode Cloud 里删掉。注意这会丢失该 App 的构建历史。

3)Sign in with Apple 的 App 分组:如果为它做过跨平台分组,发起之前必须先解除。

顺便说一条最容易被忽略、又直接导致转移失败的:内购的 Product ID 不能和接收方账号里已有的重复。让接收方先导出一份现有内购 ID 清单,两边比对一遍,几分钟的事,能省掉一次白跑。

还有个顺序问题值得单独拎出来。如果 App 有自动续期订阅:

• 
❌ 先发起转移,再想起来交接共享密钥,订阅收据校验会断档
• 
✅ 先生成 App 专用共享密钥(app-specific shared secret)交给接收方 → 接收方更新服务端、完成联调 → 再发起转移 → 转移完成后接收方立刻重新生成一个新密钥,让组织外的人不再持有有效凭据

最后提醒一句:发起之后,转出方就不能再编辑元数据、改定价、调区域、动内购了,所有未结束的审核沟通也会被关闭。所以任何计划中的版本更新、调价、活动配置,要么在发起之前做完,要么推到转移完成后由接收方来做。

好消息是,在接收方点接受之前,双方任何一方都可以取消。一旦点了接受,就进入不可逆流程了。

四、两个 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}

几个容易填错的地方:

1. 
client_id 里不要带 Team ID 前缀,只填 App ID 或 Services ID
2. 
client_secret 是你自己签的 JWT,它的 sub 声明填 bundle ID 或 Services ID
3. 
返回的 access_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 私有中继邮箱地址
• 
如果需要邮件通知用户,得在转移之前用私有中继邮箱发完。转移完成后,只要用户没在用你团队的其他 App,你就再也联系不上他们了

五、转过去之后,哪些东西没跟着走

这一节是整份 SOP 里我认为最值钱的部分。因为它列的这些问题,通常要等到接收方提交下一个版本的时候才暴露出来,那时候排查起来非常被动。

项目
是否转移
后续动作
评分与评价
保留
历史评价不清零
App Store 上架状态
保留
转移过程中持续可下载,用户正常收更新
Bundle ID
保留
不可更改,新主体会长期用着带旧公司命名的 ID
iCloud 容器 / KVS 标识
转移
随 App 一并过去
Sign in with Apple Service ID
转移
默认跟着走。不想转就得在开始前解除关联
Sign in with Apple 用户标识
需迁移
走 transfer identifier 兑换,接受后 60 天内
排行榜与成就
转移但会变
并入过分组的单独排行榜会丢掉 grp. 前缀、退回原始 ID,硬编码的地方要核查
Game Center 分组 / matchmaking 规则
不转移
接收方在完成后重新加入、重建
APNs 证书或密钥
需重建
原证书到期前仍有效,之后在新团队生成。用 .p8 密钥的话新建或复用接收方已有的,两种都必须更新推送服务端配置
Apple Pay merchant ID
不转移
原证书有效期内交易仍成功,但提交更新时必须在新账号建新的
Keychain sharing
需重建
只在 App 更新之前有效。更新发布后用户要重新登录一次
Provisioning profiles
需重建
在新账号新建,关联该 App ID 与分发证书
共享的 CloudKit 容器
有副作用
原账号里其他 App 会失去对该容器的读写能力,有共用必须先做拆分方案
Wallet 卡券
需重发
需要 App 或 Web 服务推送更新的卡券,必须用新标识重新签发
TestFlight / Xcode Cloud 数据
须先清空
转移后由接收方从零重建
销售 / 下载 / Analytics 历史
不转移
提前导出归档
App Group
手动迁移
完成后从转出方删除、在接收方注册,不影响 App 可用性

这张表里我最想提醒两条。

一是 Keychain。它的共享只在 App 更新之前继续有效。等接收方发了转移后的第一个版本,App 就再也读不到原来 keychain 里的鉴权令牌了,所有用户会被登出一次。这件事本身没法避免,但你可以提前把它写进版本更新说明,或者做个应用内提示,别让用户一脸懵地以为账号丢了。

二是回归测试里的"从旧版本升级"。全新安装一般都会测,升级路径容易被跳过。但恰恰是升级这条路径,才会暴露 keychain 和用户标识的问题。真机上必须走一遍:全新安装、从旧版本升级、登录(含 Sign in with Apple)、内购与恢复购买、推送接收、iCloud 同步。

还有个建议:转移后的第一次提交,别攒成一个大版本。先用一个低风险小更新把签名、证书、隐私、内购配置整条链路验通,再发正经版本。

另外两个可能卡住流程的情况,提前知道能省几天:

• 
出口合规文件。如果 App 需要出口合规文档,状态会停在 Waiting for Export Compliance,Apple 会直接联系接收方要材料。带加密功能的 App 尤其容易触发,材料提前备好
• 
巴西的固定赔率博彩类 App。接受转移之后,会立即在巴西 App Store 下架。想恢复只有一条路:提交一个带新主体有效巴西 SPA 博彩牌照的版本更新

六、要是它真的从没上架过

那就转不了,只能在新账号重建。

好消息是重建比转移轻得多:没有真实用户、没有评分评价、没有订阅收入要保全,也就不涉及 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 的外部依赖有多少。逐项排查这几处:

• 
第三方 SDK 控制台的配置(推送、统计、地图、登录)
• 
服务端的白名单与校验逻辑
• 
支付、风控渠道登记的应用标识
• 
Universal Links 的 apple-app-site-association
• 
对外文档或合同里写明的标识

依赖少、ID 也没什么对外含义,那就换个新的重建,几天的事。依赖多、或者 ID 带品牌含义,就回头看第一节的第三条路,先在原主体上架一个版本,再走标准转移。

重建时几个具体的点:

1. 
顺序不能反:先注册 App ID 并开齐 Capabilities,再建 App 记录,最后才上传构建。漏开一项 entitlement,要到构建上传时才报错
2. 
内购 Product ID 可以原样复用。原来的从没上架过,又属于不同账号,不构成冲突
3. 
Universal Links 会静默失效apple-app-site-association 里的 App ID 前缀(也就是 Team ID)变了,得重新部署到域名根目录
4. 
提前通知测试员。Bundle ID 变了,新版本会作为另一个 App 安装,不是覆盖升级,旧版会继续留在设备上。这句话不说清楚,你会收到一堆"怎么装了两个"的反馈
5. 
Sign in with Apple 的用户标识这次没有补救办法transfer_sub 那套机制只在真正的 App Transfer 里可用,重建走不了。测试库里的账号会全部对不上,如果已经积累了有意义的数据,得提前设计用邮箱之类的字段做人工对齐

最后

App 转移的难点不在操作,在排期。

给几个可以直接拿去排的数:双方账号都就绪、App 状态干净,转移动作 2–3 天能走完;算上前置清理、订阅密钥交接、Sign in with Apple 准备,预留 1–2 周比较稳;新主体账号还没注册的话,整体按一个月以上排。转移之后的收尾和验证,再算 1–2 周。

也说一句这份 SOP 覆盖不到的:资产转让协议、App 知识产权与商标归属、隐私政策和用户协议的主体变更、必要的用户告知。这些法务和商务的事,技术流程一件都解决不了,但它们往往才是真正的关键路径。技术这条线可以提前跑,别等法务落地了才开始准备账号。

上面所有规则都来自 Apple 官方文档(2026 年 8 月核对),主要是这几处:

• 
App Store Connect Help 的 Overview of app transfer / App transfer criteria / Initiate an app transfer / Accept an app transfer
• 
Sign in with Apple 的 Transferring your apps and users to another team
• 
Apple Developer Account Help 的 Delete an App ID(Bundle ID 无法释放的依据)

标了"经验值"的时间估算不是官方口径,只作排期参考。Apple 的政策和界面路径会变,正式动手前建议再核对一次官方页面。

你们做过 App 主体转移吗?卡在哪一步了?评论区聊聊。如果手上正好有这个活儿,建议直接收藏这篇,或者转给负责账号的同事。尤其是第五节那张表,能帮他们少踩几个只在提交更新时才暴露的坑。


关于我

曾在多家大厂做 iOS 资深工程师,也长期坐在面试官这一侧。定位疑难问题、性能与项目优化、架构设计、底层原理,是我过去吃饭的手艺。

现在全力转行 AI 开发,一边从零手搓 Agent 把整条工程链路走通,一边啃 AI 运行框架和算法基础原理。转行路上踩的坑、想明白的事,都会写在这个号里。

如果你也在做 iOS,或者同样在从客户端往 AI 转,欢迎关注,评论区随时交流,一起进步。