App Store Server API 使用指南

4,725 阅读5分钟

说明

App Store Server API 是苹果提供给开发者,通过服务器来管理用户在 App Store 应用内购买的一套接口(REST API)。

App Store 使用 JSON Web Signature(JWS) 规范对此 API 返回的事务和订阅续订信息进行签名。

App Store Server API 独立于应用在客户设备上的安装状态。App Store 服务器根据客户的 app 内购买历史记录返回信息,无论客户是否在其设备上安装、移除或重新安装 app。

若要使用此 API 请求事务和订阅状态信息,请提供属于客户的任何原始事务标识符。 事务历史记录 API 使用完整的事务列表进行响应,一次 20 个,从最旧的开始。订阅状态 API 返回客户所有订阅的状态,按其订阅组标识符进行组织。

URL

线上环境:

https://api.storekit.itunes.apple.com/

沙盒环境:

https://api.storekit-sandbox.itunes.apple.com/

接口

推出时间接口说明路径
WWDC21Look Up Order ID查询用户订单的收据,使用订单ID从收据中获取用户的应用内购买项目收据信息。GET /inApps/v1/lookup/{orderId}
WWDC21Get Transaction History查询用户历史收据,获取用户在您的 app 的应用内购买交易历史记录。GET /inApps/v1/history/{originalTransactionId}
WWDC21Get Refund History查询用户内购退款,获取 app 中为用户退款的所有应用内购买项目的列表。GET /inApps/v1/refund/lookup/{originalTransactionId}
WWDC21Get All Subscription Statuses查询用户订阅项目状态,获取您 app 中用户所有订阅的状态。GET /inApps/v1/subscriptions/{originalTransactionId}
WWDC21Extend a Subscription Renewal Date延长用户订阅的时长,使用原始交易标识符延长用户有效订阅的续订日期。(相当于免费给用户增加订阅时长)PUT /inApps/v1/subscriptions/extend/{originalTransactionId}
WWDC22Request a Test Notification测试 App Store 服务器通知,让 App Store 服务器通知向开发者服务器发送测试通知。POST /inApps/v1/notifications/test
WWDC22Get Test Notification Status获取 App Store 服务器通知的测试结果,获取发送到开发者服务器的 App Store 服务器测试通知的检查状态。GET /inApps/v1/notifications/test/{testNotificationToken}
WWDC22Get Notification History获取 App Store 服务器通知的历史通知,获取 App Store 服务器尝试发送到开发者服务器的通知列表。POST /inApps/v1/notifications/history

应用场景

Look Up Order ID接口举例,目前这个接口只有线上环境的,不支持沙盒环境。

因为,这个接口参数必须是用户使用内购后,从收到苹果的发票里获取的订单号,沙盒账号充值是无法提供的。

以前是无法与开发者从苹果获取到的交易订单号transactionId进行映射关联,而现在,可以通过这个接口查询。

响应的数据格式:

这个接口的作用,当用户客诉(充值不到账)时,让玩家提供订单ID,然后通过这个接口查询订单对应的状态。

如果有未消耗的收据(transactionId)时,可以为用户进行补发或者服务支持。

(因为能查到 transactionId,说明玩家这个充值订单是有效!至于是否消耗,需要服务端来检查是否有未消耗的收据。)

status=0时,表示为有效的订单号。

{
    'status': 0, 
    'signedTransactions': [
            'eyJhbGciOiJFUz.............',                       
            'eyJ0eXAiOiJKV1.............',               
            'eyJ0eXAiJhbGci.............'
    ]
}

生成JWT(JSON Web Token)

可以使用Python3脚本生成,首先安装依赖库PyJWTcryptography

pip3 install PyJWT
pip3 install cryptography
#!/usr/bin/evn python3
# coding=UTF-8

import jwt
import time

# 读取密钥文件证书内容
f = open("/Users/admin/Downloads/SubscriptionKey_X529C7MV32.p8")
key_data = f.read()
f.close()

# JWT Header

header = {
    "alg": "ES256", # 加密算法,默认值:ES256
    "kid": "", # 秘钥ID,需在App Store Connect生成
    "typ": "JWT" # 令牌类型,默认值:JWT
}

# JWT Payload
payload = {
    "iss": "", # issuer ID,需在App Store Connect生成
    "aud": "appstoreconnect-v1", # 受众,固定值appstoreconnect-v1
    "iat": int(time.time()), # 发布时间
    "exp": int(time.time()) + 60 * 60, # 到期时间,60 minutes timestamp
    "nonce": "6edffe66-b482-11eb-8529-0242ac130003", # 唯一标识符
    "bid": "" # Bundle ID
}

# JWT token
token = jwt.encode(headers=header, payload=payload, key=key_data, algorithm="ES256")

print("JWT Token:", token)

生成kid

要生成密钥,需在 App Store Connect 中具有管理员角色或帐户持有人角色。登录 App Store Connect 并完成以下步骤:

  1. 选择 “用户和访问”,然后选择 “密钥” 子标签页。
  2. 在 “密钥类型” 下选择 “App内购买项目”。
  3. 单击 “生成API内购买项目密钥”(如果之前创建过,则点击 “添加(+)” 按钮新增。)。
  4. 输入密钥的名称。该名称仅供您参考,名字不作为密钥的一部分。
  5. 单击 “生成”。

截屏2022-10-21 15.07.57.png

99AFE8EF-62E1-4060-AB4C-351BCA50BC15.png

生成Issuer ID(iss)

生成iss与生成kid类似。

截屏2022-10-21 15.10.45.png

CA76DA30-DB86-4668-ABBB-C94FE2FE8F4E.png

同时下载密码文件,以作为生成JWT的秘钥使用。

7827512F-D640-4685-890F-C176786C0765.png

有了以上这些参数及秘钥,我们就可以生成JWT,下面我们来看如何调用该API呢?

调用示例

curl -v -H 'Authorization: Bearer [signed token]' "https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{original_transaction_id}"

这里用JWT生成的token放到App Store Server API请求链接的header部分。 keyAuthorizationvalueBearer [signed token]。

解析JWT

接口返回的也是JWT,因此我们需要对JWT进行解析。 如果只是想获取 JWT 的有效负载 Payload 参数,可以直接 base64 Decode Payload 参数就行了,但是如果你需要验证签名,则必须使用到 Signture, Header。 首先,我们需要获取公钥,前往Apple PKI网页,点击Apple Root CA - G3 Root即可获得一个.pem证书,再使用以下命令,即可获得我们需要的公钥文件。

openssl x509 -pubkey -noout -in cert.pem > pubkey.pem
``

![95DD5C62-577D-4E41-8A64-82E478F4082E.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0be4fb1fc2fa4f5898bd2de0da8ea0a4~tplv-k3u1fbpfcp-watermark.image?)

有了公钥,我们就可以对返回的JWT进行解码。

这里仍以Python为例。
```python
import jwt

# 读取公钥文件证书内容
f = open("/Users/admin/Downloads/AppleRootCA-G3.pem")
pub_key_data = f.read()
f.close()

jwt_token = " eyJhbGciOiJFUzI1NiIsImtpZCI6Ilg1MjlDN01WMzIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJkZTA5OGNmMy05ZWJhLTQ3ODUtOThlZC01YzdjMjFhOWQxOGEiLCJhdWQiOiJhcHBzdG9yZWNvbm5lY3QtdjEiLCJpYXQiOjE2NjYzMzUxNjQsImV4cCI6MTY2NjMzODc2NCwibm9uY2UiOiI2ZWRmZmU2Ni1iNDgyLTExZWItODUyOS0wMjQyYWMxMzAwMDMiLCJiaWQiOiJjb20uY2NobC5taGJ1eXUifQ.fgqsl_Uo-O-RTnwhymh0r_aIBQ30l7K61Q8NGHyjk2yxjArIUX31VQba9Q-IcuO7erzCKSJVIVeqJR2oTeUmaQ"
data = None

try:
  data = jwt.decode(token, pub_key_data, algorithms=['ES256'])
except Exception as e:
  # 如果 jwt 被篡改过; 或者算法不正确; 如果设置有效时间, 过了有效期; 或者密钥不相同; 都会抛出相应的异常
  print(e)

# 解析出来的就是 payload 内的数据
print(data)
# 输出: {'iat': '***', 'name': '***', ...}

到这里,我们就可以针对本文最初提供的API表格中的API接口按需要进行访问,获取我们需要的信息,来帮助我们解决内购中遇到的问题,也有助于完善我们的内购服务端系统。