作者:iHTCboy
关于 App Store 用户退款时并没有通知开发者,直到 2020 年 6 月苹果提供了退款通知,但是因为不是 API 方式,导致开发者不一定能收到退款通知。另外像用户充值成功但 app 没有提供金币或服务等,开发者一般无法判断用户是否真的付款了。综上,苹果在 WWDC21 带来了全新强大的 App Store Server API,本文让我们从了解到实践的过程,全面认识 App Store Server API。
一、前言
大家好,我们在去年在 WWDC21 后 6 月 17 日发表了总结文章《苹果iOS内购三步曲 - WWDC21》。当时只是根据苹果的演讲内容进行了梳理,当时的很多接口和功能并没有上线,比如根据玩家的发票订单号查询用户的苹果收据,查询历史订单接口等,当时文章并没有深入的分析,而如今都 2022 年了,苹果 App Store Server API 已经上线,所以,今天我们一起来了解一下,相关 API 的具体使用实践吧~
二、App Store Server API
首先,我们先列一下,WWDC21 苹果提供了那些 Server API,然后我们在看看怎么实践这些接口,最后在总结一下注意事项。
2.1 API 简介
查询用户订单的收据
GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}
- Look Up Order ID:使用订单ID从收据中获取用户的应用内购买项目收据信息。
查询用户历史收据
GET https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}
- Get Transaction History:获取用户在您的 app 的应用内购买交易历史记录。
查询用户内购退款
GET https://api.storekit.itunes.apple.com/inApps/v1/refund/lookup/{originalTransactionId}
- Get Refund History:获取 app 中为用户退款的所有应用内购买项目的列表。
查询用户订阅项目状态
GET https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}
- Get All Subscription Statuses:获取您 app 中用户所有订阅的状态。
提交防欺诈信息
PUT https://api.storekit.itunes.apple.com/inApps/v1/transactions/consumption/{originalTransactionId}
Send Consumption Information:当用户申请退款时,苹果通知(CONSUMPTION_REQUEST)开发者服务器,开发者可在12小时内,提供用户的信息(比如游戏金币是否已消费、用户充值过多少钱、退款过多少钱等),最后苹果收到这些信息,协助“退款决策系统” 来决定是否允许用户退款。
延长用户订阅的时长
PUT https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/extend/{originalTransactionId}
Extend a Subscription Renewal Date:使用原始交易标识符延长用户有效订阅的续订日期。(相当于免费给用户增加订阅时长)
2.2 接口参数说明
App Store Server API 是苹果提供给开发者,通过服务器来管理用户在 App Store 应用内购买的一套接口(REST API)。
URL
线上环境的 URL:
https://api.storekit.itunes.apple.com/
沙盒环境测试:
https://api.storekit-sandbox.itunes.apple.com/
JWT 简介
调用这些 API 需要 JWT(JSON Web Token
)进行授权。那么什么是 JWT 呢?
JWT
是一个开放式标准(规范文件 RFC 7519),用于在各方之间以 JSON 对象安全传输信息。有两种实现,一种基于 JWS
的实现使用了BASE64URL
编码和数字签名的方式对传输的Claims
提供了完整性保护,也就是仅仅保证传输的Claims
内容不被篡改,但是会暴露明文。另一种是基于 JWE
实现的依赖于加解密算法、BASE64URL
编码和身份认证等手段提高传输的Claims
内容被破解的难度。
JWS
(规范文件 RFC 7515):JSON Web Signature
,表示使用JSON
数据结构和BASE64URL
编码表示经过数字签名或消息认证码(MAC
)认证的内容。JWE
(规范文件 RFC 7516):JSON Web Encryption
,表示基于JSON
数据结构的加密内容。
目前苹果 JWT 相关的内容,都是基于 JWS 实现,所以下文的 JWT 默认指 JWS。
JWT(JWS) 由三部分组成:
base64(header) + '.' + base64(payload) + '.' + sign( Base64(header) + "." + Base64(payload) )
- header:主要声明了 JWT 的签名算法;
- payload:主要承载了各种声明并传递明文数据;
- signture:拥有该部分的 JWT 被称为 JWS,也就是签了名的 JWS。
JWT 的内容示例:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidGVhbSI6IjM35omL5ri45oqA5pyv6L-Q6JCl5Zui6ZifIiwiYXV0aG9yIjoiaUhUQ2JveSIsImlhdCI6MTUxNjIzOTAyMn0.dL5U_t_DcfLTY9WolmbU-j81jrZqs1HhHqYKM6HSxVgWCGUAKLzwVrnLuuMCnSRnrW9vmGKNqNvrzG8cEwxvAg
详细的 JWT 内容,这里就略过了,大家可以自动搜索了解更多。
组装 JWT
知道了基本的 JWT 知识,我们就可以开工啦。要生成签名的 JWT 有三步:
- 创建 JWT 标头。
- 创建 JWT 有效负载。
- 在 JWT 上签名。
JWT header 示例:
{
"alg": "ES256",
"kid": "2X9R4HXF34",
"typ": "JWT"
}
JWT payload 示例:
{
"iss": "57246542-96fe-1a63e053-0824d011072a",
"iat": 1623085200,
"exp": 1623086400,
"aud": "appstoreconnect-v1",
"nonce": "6edffe66-b482-11eb-8529-0242ac130003",
"bid": "com.example.testbundleid2021"
}
以上是苹果要求的字段规范,所以不同的 JWT 字符和内容并一样,所以,我们看看苹果对这些字段的定义:
字段 | 字段说明 | 字段值说明 |
---|---|---|
alg |
Encryption Algorithm,加密算法 | 默认值:ES256。App Store Server API 的所有 JWT 都必须使用 ES256 加密进行签名。 |
kid |
Key ID,密钥ID | 您的私钥ID,值来自 App Store Connect,下文会讲解。 |
typ |
Token Type,令牌类型 | 默认值:JWT |
iss |
Issuer,发行人 | 您的发卡机构ID,值来自 App Store Connect 的密钥页面,下文会讲解。 |
iat |
Issued At,发布时间 | 秒,以 UNIX 时间(例如:1623085200)发布令牌的时间 |
exp |
Expiration Time,到期时间 | 秒,令牌的到期时间,以 UNIX 时间为单位。在iat中超过 60 分钟过期的令牌无效(例如:1623086400) |
aud |
Audience,受众 | 固定值:appstoreconnect-v1 |
nonce |
Unique Identifier,唯一标识符 | 您仅创建和使用一次的任意数字(例如: “6edffe66-b482-11eb-8529-0242ac130003”)。可以理解为 UUID 值。 |
bid |
Bundle ID,套装ID | 您的 app 的套装ID(例如:“com.example.testbundleid2021”) |
其中 kid
和 iss
值是从 App Store Connect 后台创建和获取。
生成密钥 ID(kid)
要生成密钥,您必须在 App Store Connect 中具有管理员角色或帐户持有人角色。登录 App Store Connect 并完成以下步骤:
- 选择 “用户和访问”,然后选择 “密钥” 子标签页。
- 在 “密钥类型” 下选择 “App内购买项目”。
- 单击 “生成API内购买项目密钥”(如果之前创建过,则点击 “添加(+)” 按钮新增。)。
- 输入密钥的名称。该名称仅供您参考,名字不作为密钥的一部分。
- 单击 “生成”。
生成的密钥,有一列名为 “密钥 ID” 就是 kid
的值,鼠标移动到文字就会显示 拷贝密钥 ID
,点击按钮就可以复制 kid 值。
生成 Issuer(iss)
同理,iss
值的生成,类似:
issuer ID 值就是 iss
的值。
生成和签名 JWT
获取到这里参数后,就需要签名,那么还需要签名的密钥文件。
下载并保存密钥文件
App Store Connect 密钥文件,在刚才生成 kid
时,列表右边有 下载 App 内购买项目密钥
按钮(仅当您尚未下载私钥时,才会显示下载链接。):
此私钥只能一次性下载!
另外 Apple 不保留私钥的副本,将您的私钥存放在安全的地方。
注意:将您的私钥存放在安全的地方。不要共享密钥,不要将密钥存储在代码仓库中,不要将密钥放在客户端代码中。如果您怀疑私钥被盗,请立即在 App Store Connect 中撤销密钥。有关详细信息,请参阅 撤销API密钥。
API密钥有两个部分:苹果保留的公钥和您下载的私钥。开发者使用私钥对授权 API 在 App Store 中访问数据的令牌进行签名。
需要注意的是,App Store Server API 密钥是 App Store Server API 所独有的,不能用于其他 Apple 服务(比如 Sign in with Apple 服务或 App Store Connet API 服务等。)。
为 API 请求生成令牌
最终,JWT Header 和 payload 示例:
{
"alg": "ES256",
"kid": "2X9R4HXF34",
"typ": "JWT"
}
{
"iss": "57246542-96fe-1a63-e053-0824d011072a",
"iat": 1623085200,
"exp": 1623086400,
"aud": "appstoreconnect-v1",
"nonce": "6