Apple pay 服务端
曦子本文介绍 Apple 支付在服务端的处理.
支付流程
注意文中服务端指业务方/商家的服务端,而苹果的服务端叫 App Store Server。
- 客户端支付成功会保留支付凭据同时通知到 App Store Server
- 客户端上传凭据到服务端
- 这里拿到凭据可以关联用户信息
- 主动验证支付信息
- App Store Server 通过 URL 回调
- 服务端拿到支付信息并验证回调有效性
- 服务端发放权益
在服务端处理 Apple 支付需要实现一个回调 URL,这个 URL 由 Apple 服务器向其发出的通知请求时调用。可以使用此 URL 来验证收据、获取有关购买的其他信息。回调配置方法
验证票据方案选取
官方 提供了客户端和服务端不同的验证方式 ,如下:
- Locally, on the device. Validating receipts locally requires code that reads and validates a binary file that Apple encodes and signs as a PKCS #7 container. For more information, see Validating receipts on the device.
- On your server with the App Store. Validating receipts with the App Store requires secure connections between your app and your server, and between your server and the App Store. For more information, see Validating receipts with the App Store.
Capability | On-device validation | Server-side validation |
---|---|---|
Validates authenticity of receipt | Yes | Yes |
Includes subscription renewal transactions | Yes | Yes |
Includes additional subscription information | No | Yes |
Resistant to device clock change | No | Yes |
这里我们介绍服务端的验证。
获取票据
首先去苹果查询支付凭据自然需要客户端先上传支付凭据
/* Load the receipt from the app bundle. */
*receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSURL *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSData
if (!receipt) {
(@"no receipt");
NSLog/* No local receipt -- handle the error. */
} else {
/* Get the receipt in encoded format. */
*encodedReceipt = [receipt base64EncodedStringWithOptions:0];
NSString }
/* ... Send the receipt data to your server ... */
验证票据
这里可以查询到相关的请求信息和响应信息。
注意这里沙盒环境跟生产验证的 URL 不一致
// 这里仅展示必要的字段
{
"receipt-data": "", // 注意这里是 base64 后的支付凭据
"password": "" // 共享密钥
}
共享密钥的获取地址: https://appstoreconnect.apple.com/access/shared-secret
验证 App Store Server 回调
Api 详情,我们发现响应体里只有一个字段
{
"signedPayload": "" // "The payload in JSON Web Signature (JWS) format, signed by the App Store."
}
这里有个问题, 我们怎么验证苹果的回调请求是合法的,而不是别人伪造的呢?
JWT
苹果使用了 JWT 。(Calls to the API require JSON Web Tokens (JWTs) for authorization)
Header Field | Value |
---|---|
alg - Encryption Algorithm | ES256 |
All JWTs for App Store Connect API must be signed with ES256 encryption. | |
kid - Key Identifier | Your private key ID from App Store Connect; for example 2X9R4HXF34. |
typ - Token Type | JWT |
具体的实现细节可以参考 Apple 的官方文档: App Store Server Notifications API
然后我们发现 Apple 返回的 jwt header 里有以下字段:
{
"alg": "ES256",
"x5c": [
"MIIEMDCC...",
"MIIDFjCCApygAw...",
"MIICQzCCAcmgAw..."
]
}
可以看到 jwt 算法用的是 ES256 那么 x5c 又是什么呢?
X5C
x5c
是 JWT 头(header)中的一个字段,它是一个公钥证书链数组,用于验证 JWT 签名,x5c 具体可以看这里。需要根据 rfc5280 进行验证。
具体验证
这里我们需要从第一个证书验证 JWT 是否被篡改,
- 如果第一个证书是伪造的呢?
- 用第二个证书验证第一个
- 如果第二个也是伪造的呢?
- 一次用下一个如此反复
- 如果都是伪造的呢?
- 最后一个需要 Apple 根证书去验证 下载链接
验证结束我们就可以根据响应来处理具体业务需求了, 字段对应的意义可以从 依据官方文档处理, 包含了订阅付费信息等。
TODO:
参考代码
其他细节
参考致谢: