IOS库:IAP StoreKit2 服务器

2022-06-02  本文已影响0人  时光啊混蛋_97boy

苹果服务器与开发者服务器之间的通讯,包括苹果通知、开发者主动请求苹果服务器、新的验证收据流程等。

构建开发者服务器可以实现以下几个功能:


一、验证票据

开发者可以用 receipt 收据或者使用StoreKit v2 新的 signed transactiond 来验证订单。

Receipt 收据验证方式:

服务器端在通过/verifyReceipt接口验证票据时,新 API 的数据结构也发生了变化。例如统一了购买时间、过期时间、原始购买时间格式,新增了 appAcountToken 字段、内购类型字段、退款时间、退款原因、促销优惠类型等。

旧的 receipts收据内容如下图:

新的 JWS 格式的交易格式内容,如下图。对比 receipts 收据,可以知道有那些变化。旧的有 GMT(格林威治标准时间)、PST(太平洋标准时间)、Unix timestampUnix 时间戳),新的格式,只保留了 Unix 时间戳,并且字段做了更新。

内购的类型,也有返回了。

这个就是上面提到的关系的用户信息的 UUID。这里苹果用 appAccountToken 字段。

这个是用户退款时间和退款原因的字段。从之前的 cancellation_date 改成现在的 revocationData

最后是促销优惠的类型。


二、新增了一些 API

内购历史订单查询 API

获取交易历史记录:get_transaction_history,只需要用户任意一个交易里的originalTransactionId即可获取所有的历史记录。需要注意的是,这个返回的数据有一个字段 hasMoreture,表示历史订单有更新。


查找收据发票 API

这个就是前言提到的用户收到苹果的收据发票时,无法与开发者的订单匹配的问题!现有有新的 API 来解决了,这个新的接口,可以让用户提供的发票上的ORDER ID查到对应的transaction交易信息。整个流程,主是这样,用户客诉,提供订单 ID,查询到状态,为用户进行补单或者支持服务。开发者服务器记得保存对应的用户订单 ID 做好映射。


查找该客户过去的退款 API

除了苹果服务器的通知退款REFUND后,开发者现在可以主动通过inApps/v1/history接口,查询用户的所有交易订单,来确认订单的状态是不是退款(撤销)。

苹果提供了查询所有内购订单的接口,但是不可能让开发者查一次,然后再判断那些是退款订单吧!所以,苹果提供了另一个接口。这个接口也是一样,通过用户的任一个 originalTransactionId 可以查到这个用户的所有退款记录订单。用户退款有一个单独的 refundDate 字段,如果有内容时间就表示是退款。


三、通过通知跟踪状态

回调通知通过notificationTypesubType两个字段区分,自动续期订阅相关通知如下。

notificationType subType 说明
DID_CHANGE_RENEWAL_PREF DOWNGRADE 降级,降级下个周期生效
DID_CHANGE_RENEWAL_PREF UPGRADE 升级,升级马上生效
DID_CHANGE_RENEWAL_STATUS AUTO_RENEW_ENABLED 开启自动续期

...
理解不同类型通知中交易信息的含义。每个通知中都会携带交易信息,位于 data -> signedTransactionInfo字段,它总是当时(发送通知那一刻)生效的交易,比如初次购买时,携带的购买成功的交易信息。

总结来说,App Store server notifications V2 提供了多达20多种通知类型!当然,这里变动这么多,苹果不可能在原来的接口直接改啊!所以是有 v1 和 v2 接口,开发者可以设置。最后就是通知返回的内容,多了一个 subtype子类型,还有对应的 version 为 2 表示 App Store server notifications V2 版本。


四、购买流程变化

如果有服务端,权益发放在服务端,则流程如下:

客户端流程
服务端流程1
服务端流程2
服务端流程3

其中,服务端流程1和流程2是分开并行存在的,且流程1可选(回调通知较慢,加上流程1能够提升用户体验);流程2必须有,是唯一能够及时知悉所有交易发生的时机;流程3用于补单或者恢复购买。

注意事项:对于服务端流程3,由于交易历史查询接口需要输入originalTransactionId,所以如果用户的首次交易被遗漏,是没有办法查询的。所以它需要客户端协助:客户端获取当前Apple ID的任意一条交易上传服务端,服务端取其中的originalTransactionId查询交易历史,再取交易历史的最近一条交易作为发放权益的依据。


五、服务器迁移到 JWS 格式交易验证

对于 StoreKit 2,苹果已经废弃了用 receipt 收据验证逻辑,只需要提供交易的 originalTransactionId 即可获取到完整的交易信息。那么如何从 StoreKit 1 升级到 StoreKit 2 呢?

如果开发者需要兼容 StoreKit v1 版本,那么还可以使用receipt 收据通过苹果接口 /verifyReceipt 验证收据,收据中是包含 originalTransactionId 的,所以,开发者可以通过inApps/v1/history 接口,随时了解交易的状态。


六、注意事项

业务用户识别

Apple服务的回调通知中,并不会携带Apple ID信息,因此无法区分该通知属于哪个具体业务用户。Apple的提供的方式是appAccountToken字段,该字段在客户端发起支付时指定,在通知中携带,以便业务后端区分。关于它注意几个点。

JWS签名验证

StoreKit2的一个重要变化是,大部分信息都采用JWS进行组织。TransactionJWS、通知也是JWS。上面说的交易流程中,客户端交易成功上传Transaction时,后端需要验证其有效性;接收到通知时,也要验证其有效性。

客户端上传交易信息的验证

对于后端的处理,官方并没有规定一定要将客户端得到的Transaction传到服务端,加这一步只是我们为了实时性而做的。此时的签名就要我们自己来处理了。其实是有两种方式进行确认的

通知的签名验证

这一点是最多人搞不清楚的,我们观察任意一个通知的Header如下。没有kid字段,取而代之的是x5c,这代表提供验证公钥的是一个证书X.509证书链。我们需要先验证证书链的正确性,再用证书链提供的公钥验证整个JWS的正确性。

{
  "alg": "ES256",
  "x5c": [
    "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"
  ]
}

验证逻辑如下

  1. Apple官网下载根证书
  2. 取证书链的最后一个,和上述下载的根证书对比,如果不同则验证失败
  3. 验证证书链:第一个证书用第二个证书验证、第二个用第三个验证、以此类推,全都成功才算通过
  4. 从第一个证书取得公钥
  5. 用上一步得到的公钥验证整个JWS
服务器宕机

如果因为服务端宕机或代码bug等原因,未能正确处理Apple Server通知,Apple Server会进行重试。重试时间分别是:在上一次尝试的基础上间隔1, 12, 24, 48, 72小时。也就是说,6天13小时后,将放弃通知重试,这期间还没能正确处理通知,将发生掉单。

此外,如果等不及通知重试,也可以主动查询交易历史进行补单。注意:交易历史查询接口需要originalTransactionId作为路径参数,所以如果是丢了初次购买的交易信息,是无法补单的。

通知乱序

理论上通知存在乱序的可能:初次订阅,此时服务器未能正确处理交易信息,接着客户马上在设置界面升级,触发升级通知。会出现先收到升级通知,再收到初次订阅通知的情况。正确的处理方式是以升级通知的交易为准,忽略初次订阅通知。

为保证无论什么时候来通知,都能正确处理,可以在每次回调时都先查询交易历史,如果通知中的交易信息是最新的,则处理,否则忽略。

上一篇 下一篇

猜你喜欢

热点阅读