Explore App Store server APIs for In-App Purchase
App Store Distribution & Marketing 进阶 20m

深入探索 App Store Server API 与内购服务端集成

Explore App Store server APIs for In-App Purchase

2024年6月10日

在 Apple 官方观看视频

一句话判断

如果你在做什么内购系统却还没用上 App Store Server API 和 Server Library,这个 session 就是你的必读清单——尤其是退款决策和订阅续期的新能力,直接关系到钱。

这场 Session 讲了什么

Session 聚焦 App Store Server 的三大组成部分:App Store Server API、App Store Server Notifications、以及 App Store Server Library。前两者分别解决”你的服务器主动查询 App Store”和”App Store 主动通知你的服务器”两个方向的信息流问题,后者则是官方提供的多语言 SDK,把签名验证、数据解码、promotional offer 签名等繁琐逻辑封装成开箱即用的接口。

演讲者 Alex 先梳理了内购的完整生命周期——从用户在 App 内购买 consumable,到设备收到 signed transaction、你的服务器验证并发放内容、再到用户申请退款时 App Store 发出 CONSUMPTION_REQUEST 通知、你回复消费信息、最终收到 REFUND 或 REFUND_DECLINED 的完整链路。这条链路是 Server API 最典型的使用场景之一。

随后 Ian 讲了今年的新功能:App Store Server Notifications V2 增加了更多通知类型和字段,subscription 相关交易新增了环境标识(sandbox vs production)、offer 类型、价格触发区间等字段,让你在服务端对订阅状态的理解更精准。

值得深挖的点

退款决策中的 Consumption Information 机制

很多人不知道 Apple 在退款审核时会考虑开发者提供的信息。当用户发起退款请求,App Store 会向你发送 CONSUMPTION_REQUEST 通知,你需要在限定时间内调用 Send Consumption Information 接口,告诉 Apple 这个用户到底消费了多少内容。字段包括:是否已消耗、消耗的平台(Apple 设备还是其他)、用户的消费意愿是否正常等。这套机制意味着你的服务器需要对每笔交易有清晰的状态记录——不是”交易完成就忘了”,而是持续跟踪用户对内容的消费进度。如果你的 App 有消耗型商品,这个机制直接帮你减少不必要的退款损失。

App Store Server Library 的签名验证体系

Server Library 内置了 SignedDataVerifier,能统一处理来自设备端(StoreKit)、Server API 响应、Server Notifications 三种来源的 JWS(JSON Web Signature)数据。这个设计解决了过去需要自己实现 Apple 根证书信任链验证的痛点。Library 定期从 Apple 服务器拉取最新的根证书和中间证书,自动完成 X.509 证书链验证和 CRL(证书吊销列表)检查。对于还在用老 receipt 的项目,Library 也提供了从 deprecated receipt 中提取 transaction 信息的迁移路径,帮你无痛从 Verify Receipt 迁移到新的 JWS 体系。

代码片段

使用 App Store Server Library 处理退款消费请求(Java)

// 创建验证器和 API 客户端
SignedDataVerifier verifier = new SignedDataVerifier(
    rootCerts, bundleId, appleRootCert, environment
);
AppStoreServerAPIClient client = new AppStoreServerAPIClient(
    signingKey, keyId, issuerId, bundleId, environment
);

// 验证并解码通知
DecodedNotification notification = verifier.verifyAndDecodeNotification(
    signedNotification
);

// 检查是否是消费请求通知
if (notification.getNotificationType() == NotificationType.CONSUMPTION_REQUEST) {
    String signedTransactionInfo = notification.getData()
        .getSignedTransactionInfo();
    
    // 验证并提取交易信息
    DecodedTransaction transaction = verifier.verifyAndDecodeTransaction(
        signedTransactionInfo
    );
    String transactionId = transaction.getTransactionId();
    
    // 构建消费信息并发送
    ConsumptionRequest request = new ConsumptionRequest();
    request.setCustomerConsented(true);
    request.setConsumptionStatus(ConsumptionStatus.CONSUMED);
    request.setDeliveryStatus(DeliveryStatus.DELIVERED);
    request.setPlatform(Platform.APPLE);
    
    client.sendConsumptionInformation(transactionId, request);
}

坑点:Send Consumption Information 必须在收到 CONSUMPTION_REQUEST 后尽快发送,超时 Apple 会按默认信息做退款决策,对你可能不利。另外,environment 参数必须与实际环境匹配——sandbox 环境用了 production 证书会直接验证失败。

用 Server Library 创建 Promotional Offer 签名

// Swift 版本:为订阅促销优惠生成签名
let signer = PromotionalOfferSigner(
    signingKey: privateKey,
    keyIdentifier: keyId,
    bundleIdentifier: bundleId
)

let signature = try signer.generatePromotionalOfferSignature(
    productIdentifier: "com.example.subscription.premium",
    offerIdentifier: "summer_promo_2024",
    applicationUsername: userId,
    nonce: UUID()
)
// 将签名传回客户端,用于 StoreKit 的购买流程

坑点:签名中的 nonce 必须是 UUID 且不能重复使用,applicationUsername 应该是你系统的用户唯一标识,不要用 Apple Account 的信息。

从旧的 receipt 迁移到 JWS 体系

# Python 版本:处理旧版 receipt
from appstoreserverlibrary.receipt_util import ReceiptUtility

utility = ReceiptUtility()
# 从 deprecated receipt 中提取 transaction ID
transaction_id = utility.extract_transaction_id_from_receipt(
    receipt="base64编码的receipt数据"
)
# 拿到 transaction_id 后,用 API 查询完整的交易历史
response = client.get_transaction_history(transaction_id)

坑点:这个迁移路径是临时方案,Apple 明确说了 Verify Receipt 已 deprecated,建议尽快切换到 JWS 方案。旧的 receipt 不包含新增的字段(如 offer 类型、环境标识),长期来看会丢失信息。

最佳实践

如果你还在用 Verify Receipt 或只依赖客户端 StoreKit 验证交易,今年就该切换到 Server API + Server Library 方案了。具体迁移路径:先把 App Store Server Library 引入你的后端服务(支持 Java、Python、Node.js、Swift),用 SignedDataVerifier 统一处理所有签名数据,然后逐步将验证逻辑从客户端移到服务端。客户端只负责发起购买和展示内容,所有敏感的验证和状态管理都交给服务器。这种架构下,即使客户端被篡改,你的服务端仍然掌握真实的交易状态。

对于订阅类 App,务必实现 Server Notifications V2 的全套订阅生命周期通知处理——从 DID_RENEW 到 DID_FAIL_TO_RENEW 到 GRACE_PERIOD_EXPIRED,每个状态转换都需要服务端同步你的权益系统。不要只依赖客户端的 StoreKit subscription status 检查,网络环境和设备状态的不确定性会让你的权益判断出纰漏。

还有什么值得关注

  • App Store Server Library 已经开源在 GitHub,可以直接提交 PR 和 issue 反馈问题,社区响应速度不错。
  • visionOS 和 watchOS 的内购现在也纳入了 Server API 的管理范围,如果你在为这些平台开发内购功能,API 是通用的。
  • Apple 推荐用 Server Notifications V2 的 notificationHistory 接口来排查通知丢失的问题,比之前的方案可靠得多。
WWDC 2024