Highlight
这场 Session 围绕 App Store 服务端 API 的三个核心职责展开:管理内购、签名请求、参与退款决策。主讲人 Riyaz 逐个介绍了今年的实质性更新。
核心内容
订阅类应用的服务端工程师都遇到过这个麻烦:用户在一个 app 里同时买了月度新闻订阅和年度赛事订阅,两条订阅各有自己的 transactionId 和 originalTransactionId,要在自家系统里判断「这两条订阅属于同一个客户账号」,得自己额外维护映射表。Family Sharing 的交易又拿不到 appAccountToken。如果用户在 app 之外完成购买,比如兑换 offer code 或者从 App Store 直接 promoted purchase,连 appAccountToken 都没法在 StoreKit 里设置。
Apple 这次给了一个一站式标识符:appTransactionId。它对每个 Apple Account per app 全局唯一,存在于 AppTransaction、JWSTransaction、JWSRenewalInfo 三个对象里,跨 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 里可传 originalTransactionId、transactionId 或 appTransactionId,响应是 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 V2(15: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%。revocationType:REFUND_FULL、REFUND_PRORATED或FAMILY_REVOKE。REFUND_PRORATED时只回收对应比例的内容(如虚拟币按比例扣减);REFUND_FULL和FAMILY_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 通知里读 revocationType 和 refundPercentage,对 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
- What’s new in StoreKit and In-App Purchase — 今年 StoreKit 与内购的整体更新,本场提到的客户端配套改动在这里展开。
- Explore App Store server APIs for In-App Purchase — WWDC24 的服务端 API 入门,搭配本场作为基础铺垫。
- Discover Apple-Hosted Background Assets — 服务端运营资产分发的另一条线,配合内购数据可做差异化体验。
- Enhance child safety with PermissionKit — 涉及未成年人购买场景时的合规配套,处理退款时常需要联动。
评论
GitHub Issues · utterances