Highlight
Apple 在 WWDC23 发布了 App Store Server Library 开源库,支持 Swift、Java、Node.js 和 Python 四种语言,提供 JWT 生成、JWS 签名数据验证、收据迁移和促销优惠签名四大核心能力,大幅降低 App Store Server API 的集成门槛。
核心内容
为什么需要这个库
(00:34)2021 年 Apple 推出了 StoreKit 2、App Store Server API 和 App Store Server Notifications V2。这些工具使用 JWS(JSON Web Signature)格式提供签名交易数据,比旧的 verifyReceipt 更安全、更灵活。但 JWS 的验证流程涉及证书链校验、OCSP 吊销检查、签名验证等多个复杂步骤,对不熟悉密码学协议的开发者来说门槛很高。
(01:33)App Store Server Library 就是为了解决这个问题而生。它将 JWT 生成、签名验证、收据解析等复杂操作封装成简单的 API 调用,让开发者专注于业务逻辑。
四大核心能力
(02:07)
1. App Store Server API 调用
库自动处理 JWT 生成。你只需要提供 issuerId、keyId、私钥、bundleId 和环境(sandbox/production),就能创建一个 API 客户端,直接调用所有 Server API 端点。
2. JWS 签名数据验证
验证从 App Store 收到的签名数据是否真实有效。这包括验证证书链、检查证书吊销状态、确认签名、以及验证应用标识符和环境是否匹配。
3. 收据迁移工具
从旧的 app receipt 中提取 transactionId,让你可以直接用 Server API 获取交易信息,绕过已废弃的 verifyReceipt 端点。
4. 促销优惠签名
用内购私钥自动生成订阅促销优惠的签名,省去手动实现签名逻辑的麻烦。
快速开始
(04:22)使用库之前,需要从 App Store Connect 和 Apple PKI 网站收集以下信息:
- Issuer ID:App Store Connect → Users and Access → Keys → In-App Purchase
- Key ID 和私钥:在同一页面生成新的私钥,下载私钥文件(只能下载一次)
- Apple Root Certificates:从 Apple PKI 网站下载根证书
- Bundle ID:你的应用 bundle identifier
详细内容
Java 示例:创建 API 客户端
(05:34)以 Java 为例,演示如何使用库调用 App Store Server API。
Gradle 依赖:
dependencies {
implementation 'com.apple.itunes.storekit:app-store-server-library:1.0.0'
}
创建客户端并调用 API:
import com.apple.itunes.storekit.client.AppStoreServerAPIClient;
import com.apple.itunes.storekit.model.Environment;
public class ExampleApp {
public static void main(String[] args) throws Exception {
String issuerId = "YOUR_ISSUER_ID";
String keyId = "YOUR_KEY_ID";
String privateKey = "-----BEGIN EC PRIVATE KEY-----\n...";
String bundleId = "com.example.yourapp";
Environment environment = Environment.SANDBOX;
// 创建 API 客户端
AppStoreServerAPIClient client = new AppStoreServerAPIClient(
privateKey, keyId, issuerId, bundleId, environment
);
// 请求测试通知
var response = client.requestTestNotification();
System.out.println("Test notification token: " + response.getTestNotificationToken());
}
}
关键点:
AppStoreServerAPIClient自动处理 JWT 的生成和刷新- 所有 Server API 端点都有对应的方法
- 环境参数区分 sandbox 和 production
验证 JWS 签名数据
(07:29)JWS 数据包含三个 Base64URL 编码的部分,用点号分隔:header、payload、signature。
Header 中包含算法(固定为 ES256)和证书链(x5c)。验证流程包括:
- 验证证书链中每个证书都被前一个证书签名
- 检查证书有效期和格式
- 使用 OCSP 检查证书是否被吊销
- 验证 leaf certificate 的用途是 App Store receipt signing
- 用 leaf certificate 的公钥验证 JWS 签名
- 检查 payload 中的 appAppleId 和 bundleId 是否匹配你的应用
(13:52)库中的 SignedDataVerifier 类封装了所有这些步骤:
import com.apple.itunes.storekit.verification.SignedDataVerifier;
import com.apple.itunes.storekit.model.Environment;
import java.util.Set;
// 加载 Apple 根证书
Set<String> rootCertificates = Set.of(
Files.readString(Path.of("AppleRootCA-G3.cer")),
Files.readString(Path.of("AppleRootCA-G2.cer"))
);
// 创建验证器
SignedDataVerifier verifier = new SignedDataVerifier(
rootCertificates, // Apple 根证书集合
null, // appAppleId,sandbox 环境传 null
bundleId, // 你的 bundle ID
environment, // SANDBOX 或 PRODUCTION
true // 是否执行在线吊销检查
);
// 验证通知
var result = verifier.verifyAndDecodeNotification(signedPayload);
System.out.println("Notification type: " + result.getNotificationType());
关键点:
onlineChecks参数:对于刚收到的通知设为true,对于几个月前的历史数据设为false(证书可能已过期)- 验证器会自动检查 appAppleId、bundleId 和 environment 是否匹配
- 验证通过后,可以安全地信任 payload 中的数据
从收据提取 transactionId
(16:52)ReceiptUtility 类帮助从旧的 app receipt 中提取 transactionId,实现从 verifyReceipt 到 Server API 的迁移:
import com.apple.itunes.storekit.util.ReceiptUtility;
// 从设备或 V1 通知获取的 app receipt
String appReceipt = "MII..."; // base64 编码的收据
ReceiptUtility utility = new ReceiptUtility();
String transactionId = utility.extractTransactionIdFromAppReceipt(appReceipt);
if (transactionId != null) {
// 使用 transactionId 调用 Get Transaction History
var request = new TransactionHistoryRequest();
request.setProductType(ProductType.CONSUMABLE);
request.setRevoked(false);
request.setSort(SortOrder.DESCENDING);
List<TransactionInfo> transactions = new ArrayList<>();
String revision = null;
do {
var response = client.getTransactionHistory(transactionId, revision, request);
transactions.addAll(response.getSignedTransactions());
revision = response.getRevision();
} while (Boolean.TRUE.equals(response.getHasMore()));
System.out.println("Found " + transactions.size() + " transactions");
}
关键点:
- 不是所有收据都包含 transactionId(用户可能没有购买记录)
- 提取出的 transactionId 可以是任意 transactionId,不限于 originalTransactionId
- Server API 的 Get Transaction History 等端点现在支持任意 transactionId 作为参数
- 保存返回的
revisiontoken,下次查询时传入可以增量获取新交易
核心启发
-
用官方库替换手写 JWT 和验证逻辑
- 做什么:将服务器上手动生成 JWT 和验证 JWS 的代码替换为 App Store Server Library
- 为什么值得做:库的验证逻辑经过 Apple 官方审核,比手写实现更可靠,且会自动跟进协议更新
- 怎么开始:根据你的后端语言选择对应的库(Swift/Java/Node.js/Python),按照 README 中的示例替换现有代码
-
建立服务端交易验证流水线
- 做什么:在服务器上部署完整的交易验证流程,包括接收通知、验证签名、更新用户权限
- 为什么值得做:JWS 签名数据让服务端验证变得标准化,ReceiptUtility 让旧设备也能接入新体系
- 怎么开始:使用 SignedDataVerifier 验证所有从客户端和通知收到的签名数据,用 ReceiptUtility 处理旧收据,建立统一的交易处理流程
-
规划 verifyReceipt 迁移路线图
- 做什么:制定从 verifyReceipt 到 App Store Server API 的迁移计划,支持新旧设备共存
- 为什么值得做:verifyReceipt 已废弃,新功能只在 JWS 体系中发布;ReceiptUtility 提供了平滑过渡方案
- 怎么开始:新代码直接使用 Server API,旧代码通过 ReceiptUtility 提取 transactionId 后调用 Server API,逐步淘汰 verifyReceipt
-
集成促销优惠签名生成
- 做什么:使用库的促销优惠签名功能,简化订阅优惠的实现
- 为什么值得做:手动实现签名容易出错,库提供了经过验证的实现
- 怎么开始:在生成促销优惠时调用库的签名方法,传入产品 ID、优惠 ID 和私钥
关联 Session
- What’s new in App Store server APIs — Server API 新端点和通知字段详解
- Explore testing in-app purchases — StoreKit Testing、Sandbox 和 TestFlight 测试指南
- Subscription Offers Best Practices — 订阅促销优惠的设计和实现
- What’s new in StoreKit 2 — StoreKit 2 客户端新特性
评论
GitHub Issues · utterances