StoreKit 2 API的新特性是什么?苹果如何简化应用内购买的整合
Updated: October 10, 2023
5 min read
在最近举行的2021年全球开发者大会上,苹果推出了新版的StoreKit 2。这是一个负责在iOS中进行购买的框架。带有应用内购买(in-app purchase)功能和订阅功能的应用份额稳步增长,苹果通过发布StoreKit 2显著简化了应用内购买功能在应用(APP)中的整合。今天,我们将考虑在服务器端使用StoreKit 2,换句话说,就是在App Store Server API的帮助下进行。
请求认证
在当前的API版本中,您需要共享密钥来发送请求。这是一个您可以在App Store Connect中获得的秘密固定字符串。新版本的API使用JSON Web Token(JWT)标准请求认证。
密钥生成
首先,创建一个私钥,这个私钥将用于对请求进行授权。打开App Store Connect,前往“Users and Access”部分,然后到“Keys”选项卡。选择“In-App Purchase”密钥类型。下载一个新密钥。您还需要它的ID——您可以将其复制到App Store Connect API标签页中的发行者ID所在页面。
创建一个令牌
下一步是创建一个将用于对请求进行授权的令牌。这个过程在文档中有详细描述,所以没有理由对它进行过多的关注。下面是一个现成的Python实现示例。值得注意的是,为每个新请求生成新令牌是没有意义的。在创建令牌时,您将其生存期设置为至多60分钟,并在此期间使用相同的令牌。
import time, uuid
from authlib.jose import jwt
BUNDLE_ID = 'com.adapty.sample_app'
ISSUER_ID = '4336a124-f214-4d40-883b-6db275b5e4aa'
KEY_ID = 'J65UYBDA74'
PRIVATE_KEY = '''
-----BEGIN PRIVATE KEY-----
MIGTAgMGByqGSMBHkAQQgR/fR+3Lkg4...
-----END PRIVATE KEY-----
'''
issue_time = round(time.time())
expiration_time = issue_time + 60 * 60 # 1 hour expiration
header = {
'alg': 'ES256',
'kid': KEY_ID,
'typ': 'JWT'
}
payload = {
'iss': ISSUER_ID,
'iat': issue_time,
'exp': expiration_time,
'aud': 'appstoreconnect-v1',
'nonce': str(uuid.uuid4()),
'bid': BUNDLE_ID
}
token_encoded = jwt.encode(header, payload, PRIVATE_KEY)
token_decoded = token_encoded.decode()
authorization_header = {
'Authorization': f'Bearer {token_decoded}'
}
签署的交易
在API的新版本中,所有交易都以JSON Web签名(JWS)标准返回。这是一个由三部分组成的字符串,由点分割。
- Base64头部。
- Base64交易有效负载。
- 交易签名。
Base64(header) + "." + Base64(payload) + "." + sign(Base64(header) + "." + Base64(payload))
交易头部
需要一个头部来确保交易是真实的。Alg密钥包含加密算法,x5c密钥包含证书链。
{
"kid": "AMP/DEV",
"alg": "ES256",
"x5c": [
"MIIEO...",
"MIIDK..."
]
}
交易有效负载
{
"transactionId": "1000000831360853",
"originalTransactionId": "1000000806937552",
"webOrderLineItemId": "1000000063561721",
"bundleId": "com.adapty.sample_app",
"productId": "basic_subscription_1_month",
"subscriptionGroupIdentifier": "27636320",
"purchaseDate": 1624446341000,
"originalPurchaseDate": 1619686337000,
"expiresDate": 1624446641000,
"quantity": 1,
"type": "Auto-Renewable Subscription",
"appAccountToken": "fd12746f-2d3a-46c8-bff8-55b75ed06aca",
"inAppOwnershipType": "PURCHASED",
"signedDate": 1624446484882,
"offerType": 2,
"offerIdentifier": "basic_subscription_1_month.pay_as_you_go.3_months"
}
苹果改变并扩展了交易格式。在我看来,现在用它们工作更方便了。您可以在文档中了解有关新格式的详细信息。我会在下面描述一些最重要的变化。
- 苹果添加了appAccountToken字段,它包含系统的用户ID。这个ID必须是通用唯一标识符(UUID)格式,它是在初始化购买时在移动应用程序中设置的。如果已经设置,它将在这个链中的所有交易中返回(续订、账单问题等),您将很容易理解哪个用户进行了购买。
- 苹果还添加了offerType和offerIdentifier字段,用于包含使用过的价格信息(如果有的话)。下面是offerType字段的值:
- 1 ——试销优惠价(intro offer,仅适用于没有活跃订阅或过期订阅的用户)
- 2 ——优惠推广(promo offer,仅对当前和过期订阅有效)
- 3 ——优惠代码
如果使用了优惠推广或优惠代码,offerIdentifier键将包含所使用优惠的ID。在过去,不可能在服务器端(server-side)跟踪优惠的使用情况,这就让分析更难了。现在,您可以使用优惠代码进行分析。
- 苹果增加了inAppOwnershipType字段,这有助于了解用户是购买了产品还是通过家庭订阅访问了产品。可能的值:
- PURCHASED
- FAMILY_SHARED
- 另一个新字段——类型——包括交易类型。可能的值:
- 自动续订
- 非消耗性
- 消耗性
- 非续订
- Cancellation_date和cancellation_reason字段现在有了新的名称:revocationDate和revocationReason。在此提醒一下,它们包含由于退款而撤销订阅(subscription revocation)的日期和原因,因此,新名称看起来更符合逻辑。
- 所有的键都以camelCase格式返回(就像App Store Server API里所有的请求一样)。
- 所有日期都以Unix时间戳格式显示,以毫秒为单位。
2024 subscription benchmarks and insights
Get your free copy of our latest subscription report to stay ahead in 2024.
用户订阅状态
如需检查当前用户的订阅状态,请向https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}发送一个GET请求,其中{originalTransactionId}是该用户的任何交易链的ID。然后,您将为每一组订阅获得带有状态的交易。
{
"environment": "Sandbox",
"bundleId": "com.adapty.sample_app",
"data": [
{
"subscriptionGroupIdentifier": "39636320",
"lastTransactions": [
{
"originalTransactionId": "1000000819078552",
"status": 2,
"signedTransactionInfo": "eyJraWQiOi...",
"signedRenewalInfo": "eyJraWQiOi..."
}
]
}
]
}
状态键显示当前的订阅状态,基于此,您可以决定是否应该为用户提供应用程序的付费功能。可能的值:
- 1 —— 订阅是活跃的,用户必须能够访问付费功能。
- 2 —— 订阅已过期,用户必须无法访问付费功能。
- 3 —— 订阅是计费重试状态,这意味着用户没有取消订阅,但遇到了付费问题。苹果公司将尝试在60天内向该卡收费。用户必须无法访问付费功能。
- 4 —— 订阅处于宽限期,这意味着用户没有取消订阅,但遇到了支付问题。宽限期在App Store Connect中,所以用户必须能够访问付费功能。
- 5 —— 订阅因退款被取消,用户必须无法访问付费功能。
SignedTransactionInfo键包含链中最后一次交易的信息。您可以在上面找到关于其格式的详细信息。
续订(subscription renewal)信息
SignedRenewalInfo键包含有关续订的信息。
{
"expirationIntent": 1,
"originalTransactionId": "1000000819078552",
"autoRenewProductId": "basic_subscription_1_month",
"productId": "basic_subscription_1_month",
"autoRenewStatus": 0,
"isInBillingRetryPeriod": false,
"signedDate": 1624520884048
}
这些信息使我们能够了解在下一次付费期间订阅将会发生什么。例如,如果您看到用户取消了自动续订,您可以让他们切换到其他订阅计划或向他们提供优惠。在服务器通知的帮助下跟踪这类事件(event)很方便,我会很快告诉您具体的。
用户的交易历史
如需获取用户的交易历史记录,请将GET请求发送到https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId},其中{originalTransactionId}是该用户的任何交易链的ID。然后,您将得到一个按时间排序的交易数组。
{
"revision": "1625872984000_1000000212854038",
"bundleId": "com.adapty.sample_app",
"environment": "Sandbox",
"hasMore": true,
"signedTransactions": [
"eyJraWQiOiJ...",
"joiRVMyNeyX...",
"5MnkvOTlOZl...",
...
]
}
一个请求不能包含超过20个交易。如果用户拥有更多,则hasMore标志的值将为true。如果需要下一个交易页,则再次发送请求,其中包含修订GET参数,它将包含来自同一个键的值。
服务器交易通知
服务器通知(Server notification)可以帮助您获取关于新购买、续订、账单问题等的信息。这有助于建立更准确的分析,以及简化管理订阅者的状态。
现有的服务器通知(V1)可以解决大部分问题,但有时会带来不便。通常情况下,这是关于用户的一个操作获得多个通知的情况。例如,现在,当用户升级订阅时,苹果会发送两条通知:DID_CHANGE_RENEWAL_STATUS和INTERACTIVE_RENEWAL。当前为了处理这种情况,您需要以某种方式保存状态,并检查是否发送了第二个通知。在新版本的服务器通知(V2)中,对于用户的一个操作只有一个通知,这样就方便多了。
服务器通知的第二个版本提供了新的事件——OFFER_REDEEMED,EXPIRED和GRACE_PERIOD_EXPIRED。它们使管理订阅者状态变得更加容易。SUBSCRIBED和PRICE_INCREASE事件是第一版的改进事件。
通知类型
通知现在有了类型,因此,对于用户的任何操作,一个通知就足以理解发生了什么。
通知类型
{
"notificationType": "SUBSCRIBED",
"subtype": "INITIAL_BUY",
"version": 2,
"data": {
"environment": "Sandbox",
"bundleId": "com.adapty.sample_app",
"appAppleId": 739104078,
"bundleVersion": 1,
"signedTransactionInfo": "eyJraWQiOi...",
"signedRenewalInfo": "eyJraWQiOi..."
}
}
服务器通知包含我前面描述的JWS格式的交易和更新信息。
使用沙盒(Sandbox)环境
您需要使用沙盒环境的链接来测试购买情况:https://api.storekit-sandbox.itunes.apple.com。
新版本的服务器通知还不能进行测试。一旦它可用,就可以为生产和沙盒通知指定不同链接。您可以为沙盒选择V2,为生产选择V1进行测试。
此外,App Store Connect现在允许:
- 清除沙盒用户的购买历史,这意味着您不必再创建一个新账户来做这件事了。
- 更改沙盒用户的存储国家。
- 更改沙盒订阅续订期,例如,您可以每月购买1小时而不是5分钟。
结论
苹果在服务器端显著改善了应用内购买和订阅功能。在我看来,以下是最有用的新功能:
- 完善的优惠推广和优惠代码支持;
- 更简单、信息更丰富的服务器通知;
- 有机会通过一个简单的API调用了解当前的订阅状态;
- 清除用户的沙盒购买历史。
切换到一个新的API并不困难,为每个收据获得originalTransactionId就足够了,它可能已经包含在了您的数据库中。
总之,将订阅整合到移动应用中最困难的部分是构建分析系统和优化经济。Adapty可以很好地解决这些问题:
- 内置分析让我们能够快速理解主要应用的参数。
- 群组分析(Cohort analysis)有助于理解经济是否存在任何问题。归因
- A/B测试(A/B testing)能够提高应用的盈利能力。
- 与外部系统的集成允许将交易发送到归因和产品分析服务。
- 促销活动可以减少受众的流失。
- 开源软件开发工具包(SDK)让您可以在几个小时内将订阅整合到应用程序中。
了解更多关于这些功能的信息,以便更快地在您的应用中实施订阅,更快、更好地为您的应用创收。