背景
Apple 在 WWDC 2021 (iOS 15) 上推出了 StoreKit 2,它是一套全新的 Swift APIs,充分利用了 Swift 的语言特性。经历了一年,在 WWDC 2022 上也迎来了 StoreKit2 的更新,更新了收据(receipts)和交易(transactions)以及添加了更多的 API。
考虑到 StoreKit 2 相对 StoreKit 1 从实际支付链路到代码设计都更加健壮,在 iOS 16 发布后,我们决定更新支持 StoreKit 2。
StoreKit 1 现状
在 StoreKit1 中,我们使用 applicationUsername 来关联 APP 订单和 IAP 订单,具体流程如下图所示。
客户端在调起 IAP 支付前会先请求服务端生成预支付订单,并将 userId 与 orderId 进行编码生成 applicationUsername,在 IAP 支付过程中,会全程透传 applicationUsername 参数。
IAP 支付成功后,客户端将 applicationUsername 和 receipts 传给服务端,服务端解析验证 receipts,并通过 applicationUsername 找到关联订单完成发货。
由于解析 receipts 得到的数据不会包含 applicationUsername 信息,所以客户端传递的 applicationUsername 是否正确便至关重要,但是非常遗憾的是 applicationUsername 并不稳定,客户端在透传过程中可能会丢失。当传递的 applicationUsername 为空时,服务端就无法找到关联订单了(这就是常说的掉单问题),当遇到这种情况,服务端会创建新的订单并绑定给当前用户,接下去我们来看一个掉单的具体场景。
同一用户可能会有多个账号,如果用户在账号A下使用 IAP 购买课程并完成支付,但由于某些原因未能完成验证,此时切换到账号B,客户端会再次调用接口验证未完成的交易,但此时 applicationUsername 是丢失的。服务端在处理成功后会创建新的订单并绑定给B,原本属于A的订单却绑定给了B。对于A来说这笔订单丢失了,但是从全局来看A、B可以认为是同一用户(使用了同一个 Apple ID 进行支付),我们的 IAP 支付场景主要是购买课程,不存在类似游戏代充的情况,基本上都是给自己的账号购买课程,所以遇到这种情况也可以接受。
以上问题在 StoreKit 1 中没有很好的解决办法,好在 Apple 在 StoreKit 2 中对此进行了优化,引入了新字段 appAccountToken,该字段会出现在苹果的交易信息中,不会像 applicationUsername 在某些情况下会丢失,可以完美解决双方订单的关联问题,这也是我们决定把 StoreKit1 升级到 StoreKit2 的一个重要原因。
Meet StoreKit 2
StoreKit 2 主要聚焦在五个方面:
• Products:有关在 App Store Connect 中配置的内购品项的信息
• Purchases:更新购买品项接口的可选参数,可绑定用户ID
• Transaction info:更新交易信息的内容格式
• Transaction history:提供查询交易历史记录的接口
• Subscription status:提供订阅品项的状态查询接口
我们结合这五个方面,对原本的交易流程进行改造,主要涉及两个流程:购买支付、订单找回。
购买支付
原先的交易信息验证接口依赖 applicationUsername + receipts,交易信息可以从 receipts 解析得到。在 StoreKit 2 中,可以通过Get Transaction History[1]接口获取历史交易信息,并使用 appAccountToken 关联 IAP 订单和用户订单,整个购买支付流程如下图所示。
苹果在推出 StoreKit 2 的同时对 applicationUsername 也进行了兼容,如果传递的值是 UUID 的话,则会被透传到 receipts 的 app_account_token 字段,但是只适用于自动订阅类型,并且也不是那么稳定。
我们原先传递的 applicationUsername 格式不是 UUID,如果修改为 UUID 的话,虽然可以解决一部分掉单问题,但是需要改造服务端和客户端的处理逻辑,并且很长一段时间都需要兼容两种格式的 applicationUsername。上述改造的代价和风险远大于带来的收益,所以我们决定保留原有逻辑,客户端判断 iOS 版本,如果大于15就调用新的验证接口,否则调用老的验证接口。
订单找回
订单找回可以帮助用户找回支付成功但未发货的异常订单,减少了由于掉单引起的客诉,我们把该功能放在客户端订单中心位置。
原先订单找回的时候客户端会把 receipts 上报,后端会依次处理包含的交易记录,通过 TransactionId 进行判断,如果未找到相关记录则生成新的订单并发货,如果找到对应订单但是未完成则继续处理后续逻辑。其中新生成的订单会绑定给上报 receipts 的账号,如果用户是用另一个账号下单的, 那么订单找回的账号就不符合预期了,对于另一个账号来说就出现了掉单问题。
现在 StoreKit 2 的 appAccountToken 可以用来确定订单的归属,并且还提供了获取用户所有交易记录的接口,结合这两块我们升级了原先的订单找回功能,使其更加完善精确。
App Store Server 还提供了订单收据查询接口[2],该接口需要用户提供 Apple 通知邮件中的 OrderId,我们将其定义为精确找回订单,它可以帮助用户更精确地找回订单。
消息通知 V2
通知服务进行了更新,去除了一些通知,也加入了一些通知(主要是针对订阅的场景):
当开发者服务出现故障,在故障期间 App Store Server Notification 无法到达时,App Store Server 会进行重试:
• App Store Server Notifications V1:重试三次;在上次尝试后 6、24 和 48 小时。
• App Store Server Notifications V2:重试五次;在上次尝试后 1、12、24、48 和 72 小时。
如果服务还没有恢复,将会错过这个通知。开发者可以调用 App Store Server API 获取历史通知(最多存放6个月): Get Notification History[3]。
在众多消息通知中退款消息通知必须处理,其余的我们可以根据自己的业务场景针对性地进行处理。
展望
目前的 IAP 支付流程,不同于支付宝和微信,后两者在支付成功后都有服务端消息通知,即使失败也有基于指数退避算法的通知重试,而 IAP 依赖于客户端上报,支付成功未发货的概率相比之下比较高。Apple 近两年也在对 App Store 不断进行优化迭代,我们团队也会持续跟进迭代,为用户带来更好的购买体验。
References
[1]
Get Transaction History: https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history[2]
订单收据查询接口: https://developer.apple.com/documentation/appstoreserverapi/look_up_order_id[3]
Get Notification History: https://developer.apple.com/documentation/appstoreserverapi/get_notification_history
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...