WWDC Quick Look 💓 By SwiftGGTeam
Dive into App Store server APIs for In-App Purchase

Dive into App Store server APIs for In-App Purchase

观看原视频

Highlight

这场 Session 围绕 App Store 服务端 API 的三个核心职责展开:管理内购、签名请求、参与退款决策。主讲人 Riyaz 逐个介绍了今年的实质性更新。


核心内容

订阅类应用的服务端工程师都遇到过这个麻烦:用户在一个 app 里同时买了月度新闻订阅和年度赛事订阅,两条订阅各有自己的 transactionIdoriginalTransactionId,要在自家系统里判断「这两条订阅属于同一个客户账号」,得自己额外维护映射表。Family Sharing 的交易又拿不到 appAccountToken。如果用户在 app 之外完成购买,比如兑换 offer code 或者从 App Store 直接 promoted purchase,连 appAccountToken 都没法在 StoreKit 里设置。

Apple 这次给了一个一站式标识符:appTransactionId。它对每个 Apple Account per app 全局唯一,存在于 AppTransactionJWSTransactionJWSRenewalInfo 三个对象里,跨 redownload、refund、repurchase、storefront 切换都保持稳定(08:30)。Family Sharing 下每个家庭成员各拿一个独立的 appTransactionId,且家庭共享交易也能用它做关联——这是 appAccountToken 做不到的。配合新增的 Set App Account Token 端点,开发者还可以为已发生但当时没法设 token 的交易补设或更新 token。


详细内容

三个交易标识符的分工06:52):

  • transactionId:识别一次具体购买事件(购买、恢复、续期都各有一个)。
  • originalTransactionId:自动续期订阅的生命周期主键,跨续期保持不变。
  • appAccountToken:开发者自己生成的 UUID,把 App Store 交易和自家用户账号关联起来。
  • appTransactionId:今年新加的字段,每个 Apple Account per app 唯一,跨所有交易对象稳定。

Set App Account Token 端点05:11)。在 path 里传 originalTransactionId,body 里传新的 UUID,覆盖该交易(或订阅的最新一笔)的 appAccountToken。对自动续期订阅,新设置的 token 会延续到后续续期、升级、降级。适用所有产品类型,也用于修复账号归属变更带来的关联错位。

Get App Transaction Info 端点10:25)。第一次允许在服务端直接获取 AppTransaction,无需走设备。path 里可传 originalTransactionIdtransactionIdappTransactionId,响应是 JWS signedAppTransactionInfo。注意:这个端点不返回 device verification 相关字段,那些仍要走设备端。

统一的 JWS 签名格式12:17)。promotional offer、introductory offer、Advanced Commerce API 在内的所有签名场景统一为 JWS。下面是用 App Store Server Library(Java)生成 promotional offer 签名的写法(13:48):

PromotionalOfferV2SignatureCreator signatureCreator =
    new PromotionalOfferV2SignatureCreator(privateKey, keyId, issuerId, bundleId);

String transactionId = "...";   // 可选;指定后该 offer 仅限该客户使用
String productId     = "com.example.subscription";
String offerId       = "PROMO_OFFER_2025";

String signedJws = signatureCreator.createSignature(productId, offerId, transactionId);

关键点:

  • PromotionalOfferV2SignatureCreator 用 App Store Connect 下载的私钥、keyId、issuerId、bundleId 实例化;这四个值用于防止开发者之外的人发放 promo offer。
  • transactionId 是可选项,传任意属于该客户的交易标识(包括 appTransactionId)即可把 offer 锁定到该客户;省略则不限定客户。
  • 新签名比旧版输入更少——旧版是 promotional offer signature,今年的 V2 用 JWS 后字段更精简。
  • createSignature 返回的就是可以直接传给 StoreKit 的 JWS 字符串。

Send Consumption Information V215:55)。退款决策端点从 12 个字段砍到 5 个(3 必填 + 2 可选):

  • customerConsented(必填):用户是否同意把消费数据发给 App Store。设为 false 时请求会被拒绝;用户没同意就别调这个端点。
  • sampleContentProvided(必填):购买前是否给了试用内容。
  • deliveryStatus(必填):内容是否成功交付,DELIVERED 或对应的 UNDELIVERED 状态。
  • refundPreference(可选):新增 GRANT_PRORATED,配合 full / no refund 三选一。
  • consumptionPercentage(可选,但选 GRANT_PRORATED 且产品为 consumable / non-consumable / non-renewing subscription 时必填):以毫百分比表示,25000 表示 25%。

REFUND 通知新增字段20:00):

  • refundPercentage:实际退款比例,例如 75 表示退了 75%。
  • revocationTypeREFUND_FULLREFUND_PRORATEDFAMILY_REVOKEREFUND_PRORATED 时只回收对应比例的内容(如虚拟币按比例扣减);REFUND_FULLFAMILY_REVOKE 立即全部回收。auto-renewable subscription 的按比例退款按全额退款的逻辑处理即可——查当前订阅状态、做相应动作。

V1 端点已 deprecated 但仍接受请求;V2 与 Get App Transaction Info 端点都将在今年晚些时候上线(18:48)。


核心启发

1. 把 appTransactionId 作为客户账号关联的主索引

为什么值得做:跨产品(多订阅 + 内购)、跨 Family Sharing、跨 storefront 都能稳定关联到同一个 Apple Account per app。originalTransactionId 只覆盖单个订阅组,appAccountToken 在 family shared 交易里直接缺失。

怎么开始:在 app 启动时取 AppTransaction.appTransactionId,发给服务端绑定到客户账号;后续 JWSTransaction / JWSRenewalInfo 进来时用同一字段查表。Get App Transaction Info 端点上线后,服务端可以直接查询而无需依赖 app 上报。

2. 用 Set App Account Token 端点补齐 app 外购买的归属

为什么值得做:用户兑换 offer code、在 App Store 里 promoted purchase 时 StoreKit 不参与,过去这部分交易拿不到 appAccountToken。账号合并、归属变更也常见。

怎么开始:监听到 originalTransactionId 的新交易后,若发现没有 appAccountToken 或 token 与当前账号不一致,调用 Set App Account Token 把它修正过来;自动续期订阅设一次即可,token 会延续到后续续期、升级、降级。

3. 接入 V2 退款 endpoint 并实现 prorated 处理

为什么值得做:V2 字段从 12 减到 5,集成成本大幅下降;GRANT_PRORATED + refundPercentage 让你能精准回收已消费内容(如虚拟币按比例扣减),既保住用户体验也减少账目损失。

怎么开始:用 App Store Server Library 调 Send Consumption Information V2,必填三项填齐、按场景给出 prorated 偏好;REFUND 通知里读 revocationTyperefundPercentage,对 REFUND_PRORATED 单独写一条按比例回收逻辑。

4. 全部产品类型都接退款通知

为什么值得做:V2 把 non-consumable 和 non-renewing subscription 也纳入支持范围。漏掉这些产品类型意味着这部分退款发生时服务端没动作,账面和实际权益对不上。

怎么开始:审一遍 CONSUMPTION_REQUEST 和 REFUND 的处理分支,确保每种产品类型都有对应的回收逻辑;REFUND_DECLINED 不需要动作,但要记录便于排查。

5. 用统一 JWS 签名简化签名服务

为什么值得做:promotional offer、introductory offer、Advanced Commerce API 的签名格式统一后,服务端只需维护一套签名生成逻辑。

怎么开始:直接用 App Store Server Library 提供的 PromotionalOfferV2SignatureCreator 等类生成 JWS,不要自己拼签名。私钥、keyId、issuerId 来自 App Store Connect,bundleId 来自项目配置。


关联 Session

评论

GitHub Issues · utterances