Apple 当前允许每个 App 配置最多 10 个 Webhook,并可在近期交付记录中查看最多 20 条、最近一周内的投递状态。(官方 Webhooks 管理说明)
症状:远程 Mac 已经完成 Archive 和 IPA 上传,但你仍要反复刷新 App Store Connect,无法判断构建究竟是 Processing、Complete,还是已经可以进入 TestFlight。 最快解法:用 App Store Connect Webhooks 触发通知,服务端保存事件,再用 App Store Connect API 或页面二次确认;不要让远程 Mac 轮询页面,也不要把 Webhook 当成唯一事实来源。
这篇教程适合三类人:独立开发者想在上传完成后自动收到状态通知;远程 Mac 维护者需要把构建、上传、回调和失败恢复串起来;小型团队希望多人共享同一份发版记录,避免重复上传和误判。
先把发版状态拆成三条时间线
App Store Connect 的几个状态解决的是不同问题,不能混成一个“上传成功”:
- 构建上传状态:关注 Apple 是否已经处理完上传,典型状态包括 Processing、Failed、Complete。
- Beta 构建状态:关注构建是否已经满足 TestFlight 测试条件,例如 Ready to Test、Testing、Waiting for Review。
- App 版本状态:关注版本是否进入 Ready for Review、Waiting for Review、In Review 或发布相关阶段。
因此,远程 Mac 上的上传命令返回成功,只能证明“文件已经交给上传工具处理”,不能证明“构建已经可以在 TestFlight 中测试”。Apple 还会根据 Bundle ID 和版本号,把上传的构建关联到 App Store Connect 中的 App 和版本记录。
建议你在业务系统中保留以下最小字段:
event_id:<EVENT_ID>
app_id:<APP_ID>
bundle_id:<BUNDLE_ID>
version:<VERSION>
build_number:<BUILD_NUMBER>
event_type:<EVENT_TYPE>
event_created_at:<TIMESTAMP>
received_at:<TIMESTAMP>
current_state:<STATE>
其中 event_id 用于去重,版本号和 Build 号用于关联本次构建,两个时间戳用于判断事件是否乱序或延迟到达。不要把完整 Secret、JWT、API Key 私钥或 Payload URL 写进普通日志。
第一次配置:先准备公网接收端点
App Store Connect 的配置入口在 Users and Access → Integrations → Webhooks。创建 Webhook 时,需要准备配置名称、Payload URL、Secret、目标 App 和至少一个事件类型;创建权限通常涉及 Account Holder、Admin 或 App Manager。(官方配置文档)
你可以按下面步骤完成第一次配置:
- 在服务端创建一个可从公网访问的 HTTPS endpoint,例如
/webhooks/app-store-connect。 - 让 endpoint 能接收 HTTP
POST,并保存未经修改的原始请求体。 - 在 App Store Connect 的 Webhooks 页面新增配置。
- 填写脱敏后的
<PAYLOAD_URL>和随机生成的<WEBHOOK_SECRET>。 - 先选择
BUILD_UPLOAD_STATE_UPDATED,再按需要增加 Beta 构建状态或 App 版本状态事件。 - 点击 Test,确认你的服务端能收到
Ping测试事件。 - 在 Recent Deliveries 中记录测试事件的状态和响应。
服务端收到请求后,应尽快返回成功响应,再把后续解析和 API 查询放入队列或后台任务。这样做不是为了追求某个未经验证的延迟数字,而是为了避免构建状态已经产生,却因为你的业务处理过重导致投递被判定为失败。**注意:**不要把真实密钥、完整 JWT、私钥内容或公开可复用的回调地址写入教程、Issue、截图和普通应用日志。示例统一使用
<SECRET>、<KEY_ID>、<ISSUER_ID>和<PAYLOAD_URL>。
首次接收:先落库,再校验和执行业务动作
Apple 的官方文档要求接收端使用 HMAC 校验 Webhook 请求,验证请求确实来自 App Store Connect,并且使用了你配置的 Secret。(官方 Webhook 事件说明)
推荐把接收流程固定成以下顺序。
1.保存原始请求和接收时间
先保存原始 Body、请求头、接收时间和候选事件 ID。原始数据用于排查签名错误、字段变化和事件解析问题,但日志中应对 Secret、令牌和其他敏感字段脱敏。
2.验证 HMAC 和时间戳
使用 <WEBHOOK_SECRET> 验证官方提供的 HMAC Header。验证失败时,不要触发重新上传、删除构建或切换生产流程,只记录安全审计信息并返回失败。
同时检查事件时间戳与服务器接收时间。时间差异常时,先进入待确认状态,不要直接覆盖数据库中的最新状态。
3.执行事件幂等去重
以 event_id 建立唯一约束。如果同一个事件已经处理过,第二次收到时可以返回成功,但不能再次发送通知、再次触发上传或重复修改发版状态。
Apple 支持对部分 Failed 或 Success 的交付重新发送。新交付记录与原始业务事件不是同一个概念,因此数据库必须同时保存交付记录和事件 ID。
4.按事件类型解析字段
当前 Webhook 事件覆盖构建上传状态、Beta 构建状态、App 版本状态、Apple 托管资源包状态和部分 TestFlight 反馈。(官方事件类型文档)
处理时不要只读取一个通用的 status 字段,而应根据事件类型分别映射:
BUILD_UPLOAD_STATE_UPDATED:关联 App、版本号、Build 号和上传处理状态。BUILD_BETA_DETAIL_EXTERNAL_BUILD_STATE_UPDATED:关联外部测试构建状态。APP_STORE_VERSION_APP_VERSION_STATE_UPDATED:关联 App 版本在提交与审核流程中的状态。
5.通过 API 或页面确认最终状态
Webhook 的作用是告诉你“有状态变化发生”,而不是替你完成所有事实核验。收到事件后,服务端应根据 App、版本号和 Build 号查询当前资源,再把最终结果写回状态表。
如果你启用了 API 查询,服务端需要使用 App Store Connect API Key 生成 JWT。团队 API Key 和个人 API Key 的权限边界不同,私钥只能保存于服务端或受控的远程 Mac 环境,不能提交到代码仓库。(API Key 官方说明)
首次发版:把远程 Mac 的任务状态拆开
远程 Mac 上一次完整的 iOS 发版,不应只记录“命令执行成功”或“命令执行失败”。更可靠的状态链路是:
任务开始
→ Archive 完成
→ Export 完成
→ IPA 已上传
→ Apple 处理中
→ 构建处理完成
→ TestFlight 可测试
→ 需要人工处理或进入下一阶段
这里至少要区分以下三件事:
- IPA 已上传:上传工具完成传输,不等于 Apple 已处理完。
- 构建处理完成:Build Uploads 显示 Complete,意味着构建已处理并准备测试。
- TestFlight 可测试:还要检查 Beta 构建状态、合规信息和测试范围。
远程 Mac 的任务表可以这样设计:
upload_job_id:<JOB_ID>
bundle_id:<BUNDLE_ID>
version:<VERSION>
build_number:<BUILD_NUMBER>
archive_path:<REDACTED_PATH>
upload_started_at:<TIMESTAMP>
upload_finished_at:<TIMESTAMP>
apple_state:PROCESSING / COMPLETE / FAILED
testflight_state:READY_TO_TEST / TESTING / NEEDS_REVIEW
manual_action:NONE / REQUIRED
当 Webhook 到达时,先用 Bundle ID、版本号和 Build 号寻找对应任务。如果找不到匹配项,不要新建一条看似成功的任务记录,而应转入“关联失败”队列,再通过 API 或 App Store Connect 页面核验。
这也是 iOS 打包服务器 与普通脚本机器的区别:服务器不只负责执行 xcodebuild 或上传命令,还要保留任务上下文、处理断线、接受异步状态,并能让你在第二天继续判断一条构建到底走到了哪一步。
如果你需要持续在线的 macOS 构建环境,可以先参考 远程 Mac 持续打包环境,再决定是否把上传和回调接收拆到不同服务中。
失败恢复:只重试可恢复问题
Webhook 交付状态包括 Success、Pending 和 Failed。Apple 支持查看近期交付、打开事件详情,并对部分 Failed 或 Success 的交付执行重新发送。
建议将失败分成两类。
可自动恢复的投递失败
这类问题通常发生在你的接收服务暂时不可用,例如:
- 服务端短暂返回 5xx;
- 网络连接超时;
- 部署期间 endpoint 暂时不可访问;
- 队列或数据库短暂故障。
不应自动重试的业务失败
以下问题不应通过无限重发解决:
- HMAC 校验失败;
- 缺少 Bundle ID、版本号或 Build 号;
- 构建显示 Invalid Binary;
- 缺少出口合规信息;
- API Key 权限不足;
- 远程 Mac 上的 Archive 本身不可用。
**经验:**先查看 Recent Deliveries,再检查 API 或页面中的构建状态。否则很容易制造重复构建和难以解释的 Build 号记录。
用对比表确定你的监控方案
如果你只是偶尔上传一个 App,邮件和页面查看可能已经够用;如果你维护远程 Mac 或持续集成,单一方案的缺点会很快暴露。
| 方案 | 触发速度 | 最终状态确认 | 重复事件处理 | 适合场景 | 评分 |
|---|---|---|---|---|---|
| 手动刷新 App Store Connect | 低 | 人工确认 | 依赖个人记忆 | 偶发上传、单人维护 | 2/5 |
| 远程 Mac 轮询页面 | 中 | 容易受登录和页面变化影响 | 需要自行设计 | 临时脚本、短期验证 | 2/5 |
| 只接 App Store Connect Webhooks | 高 | 不能覆盖所有最终判断 | 需要自行幂等 | 通知、轻量提醒 | 3/5 |
| Webhooks +服务端落库 | 高 | 可追踪事件和历史 | 可稳定处理 | 独立开发者、小团队 | 4/5 |
| Webhooks +服务端落库+API 查询 | 高 | 事件触发后再次确认 | 可处理重发和乱序 | 持续集成、远程 Mac 发版 | **5/5** |
首次真实验收:用一条构建验证完整闭环
正式接入前,至少用一条脱敏的真实构建任务完成以下验收:
- 远程 Mac 完成 Archive 和 Export。
- 上传 IPA 后,记录任务 ID、版本号和 Build 号。
- 接收到 Webhook,并保存事件 ID、事件类型和原始请求。
- HMAC 校验通过,重复发送同一事件不会重复触发业务动作。
- 服务端通过 API 或页面确认 Processing、Complete 和 Beta 状态。
- 人为制造一次接收端 5xx,确认交付能显示 Failed。
- 修复接收端后重发交付,确认业务记录仍只有一条有效动作。
- 检查日志中没有出现
<SECRET>、JWT、私钥或完整回调地址。 - 对照 App Store Connect 页面最终状态,确认数据库没有把上传完成误记为 TestFlight 可测试。
常见问题
App Store Connect Webhooks 可以只监听一个 App 吗?
可以。通过网页配置时,一个 Webhook 只能绑定一个 App;多个 App 应分别创建配置或通过 API 管理不同的关联关系。不要把账号级别的 API Key 理解成账号级别的 Webhook 事件总线。
Webhook 能直接触发远程 Mac 重新上传吗?
技术上可以把 Webhook 转换成内部队列任务,但不建议收到一次 Failed 事件后无条件重传。你应先判断失败属于网络投递问题、Apple 处理问题、签名问题还是业务审核问题,只有明确可恢复且通过幂等检查时,才允许触发动作。
Processing 多久需要人工介入?
官方构建上传状态说明指出,如果 Processing 持续超过 24 小时,可能存在问题,应提交 Feedback Assistant 或联系支持。这个时间点适合设为人工告警阈值,但不代表所有构建都必须等待同样时长。
回调丢失后应该依赖邮件吗?
邮件可以保留,作为人工兜底,但不应替代事件记录和 API 查询。你应定期查看 Webhook 交付状态,并为 Pending、Failed 和长时间未确认的构建建立告警。
小团队是否需要 API 二次查询?
如果只是发送提醒,可以暂时不接 API;但只要你要自动标记发布成功、触发后续任务或多人共享状态,就建议加入 API 二次查询。它能帮助你区分“事件已到达”和“App Store Connect 当前最终状态”。
结论:什么时候值得采用这套方案?
如果你的发版任务只发生在本地电脑上,手动确认和邮件通知仍然可以工作;但当构建交给远程 Mac 或 iOS 打包服务器 长时间运行时,App Store Connect Webhooks +服务端状态记录+API 二次查询才是更稳妥的组合。
相比本地临时执行,持续运行的远程 Mac 方案也有真实代价:你需要维护公网回调、处理 SSH 或 VNC 断线、保存构建上下文,还要为长期在线的 macOS 环境承担固定资源成本。若你只做一次性发版,租用方案未必划算;但如果你需要 7×24 小时在线的上传、回调和失败恢复环境,按周或按月租用 MACGPU 的远程 Mac,通常比专门购买一台只负责打包的 Mac 更灵活。