iOS连续订阅IAP自动续订服务端接入流程

业务逻辑

iOS连续订阅IAP自动续订服务端接入流程_第1张图片

 注意:

  1. 同一Apple 账号生成续订订单的原始交易ID(original_transaction_id)一致

  2. 服务端处理交易过程 :要确定一个交易ID(transaction_id)只能完成一笔订单,处理完该交易的订单之后,该交易ID记录标识为处理完成状态

  3. 服务端可以通过用户购买凭证(receipt_data)查询用户所有交易记录 查询到的数据有in_app、latest_receipt_info、pending_renewal_info

  4. 连续订阅主要用到数据是latest_receipt_info,里面有所有的续订记录。

  5. 如果里面的交易有cancellation_date字段,说明该交易已经被退款。

  6. pending_renewal_info里面的auto_renew_status字段用于标识用户是否开通自动订阅;0:已关闭;1:已开通。

在接收 App Store的续订、取消、退款通知时,因为选择的版本2(version 2 notification)的通知,版本二通知是jwt编码实现。

所以需要解码,方法如下:

getContent();
        $data = json_decode($post_data,true);
        $data = $data['signedPayload'];
        $data = $this->verifyToken($data);
        $data['signedTransactionInfo'] = $this->verifyToken($data['data']['signedTransactionInfo']);
        $data['signedRenewalInfo'] = $this->verifyToken($data['data']['signedRenewalInfo']);
        if($data) {
             /*通知类型
             https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
             CONSUMPTION_REQUEST 表示客户针对消耗品内购发起退款申请
             DID_CHANGE_RENEWAL_PREF 对其订阅计划进行了更改 如果subtype是UPGRADE,则用户升级了他们的订阅;如果subtype是DOWNGRADE,则用户将其订阅降级或交叉分级
             DID_CHANGE_RENEWAL_STATUS 通知类型及其subtype指示用户对订阅续订状态进行了更改
             DID_FAIL_TO_RENEW 一种通知类型及其subtype指示订阅由于计费问题而未能续订
             DID_RENEW 一种通知类型,连同其subtype指示订阅成功续订
             EXPIRED 一种通知类型及其subtype指示订阅已过期
             GRACE_PERIOD_EXPIRED 表示计费宽限期已结束,无需续订,因此您可以关闭对服务或内容的访问
             OFFER_REDEEMED 一种通知类型,连同其subtype指示用户兑换了促销优惠或优惠代码。 subtype DID_RENEW
             PRICE_INCREASE 一种通知类型,连同其subtype指示系统已通知用户订阅价格上涨
             REFUND 表示 App Store 成功为消耗性应用内购买、非消耗性应用内购买、自动续订订阅或非续订订阅的交易退款
             REFUND_DECLINED 表示 App Store 拒绝了应用开发者发起的退款请求
             RENEWAL_EXTENDED 表示 App Store 延长了开发者要求的订阅续订日期
             REVOKE表示 用户有权通过家庭共享获得的应用内购买不再通过共享获得
             SUBSCRIBED 一种通知类型,连同其subtype指示用户订阅了产品
             1. 用户主动取消订阅notificationType:DID_CHANGE_RENEWAL_STATUS
             2. 用户取消订阅,又重新开通连续订阅notificationType: SUBSCRIBED  subtype: RESUBSCRIBE
             3. 用户首次开通订阅notificationType: SUBSCRIBED  subtype: INITIAL_BUY
             */
            $notification_type = $data['notificationType'];
            $transactionData = $data['signedTransactionInfo'];
            $product_id = $transactionData['productId'];
            $sub_type = isset($data['subtype']) ? $data['subtype'] : '';
            $original_transaction_id = $transactionData['originalTransactionId'];  // //原始交易ID
            $transaction_id = $transactionData['transactionId'];  //  //交易的标识
            $expires_date = date('Y-m-d H:i:s',$transactionData['expiresDate']/1000);
            //todo 记录通知log

            //查询原始交易绑定的用户ID
            if (in_array($notification_type, ['DID_RENEW','SUBSCRIBED'])) {
                //开通成功以及续订成功处理交易
            }

            //用户退款处理交易
            if (in_array($notification_type, ['REFUND'])) {
                
            }

            //用户取消订阅或者订阅过期
            if (in_array($notification_type, ['EXPIRED','DID_FAIL_TO_RENEW'])
                || ($notification_type == 'DID_CHANGE_RENEWAL_STATUS')) {
                   $is_renew = 0;
                   if(($notification_type == 'DID_CHANGE_RENEWAL_STATUS') && $sub_type == 'AUTO_RENEW_ENABLED')
                   {
                       //开通订阅成功
                   }elseif (($notification_type == 'DID_CHANGE_RENEWAL_STATUS') && $sub_type == 'AUTO_RENEW_DISABLED'){
                       //取消订阅成功
                   }
                   //更新用户订阅状态
            }
        }
    }

 

    /**
     * 验证token是否有效,默认验证exp,nbf,iat时间
     * @param string $Token 需要验证的token
     * @return bool|string
     */
    public static function verifyToken($Token)
    {
        $tokens = explode('.', $Token);
        if (count($tokens) != 3)
            return false;

        list($base64header, $base64payload) = $tokens;

        //获取jwt算法
        $base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
        if (empty($base64decodeheader['alg']) || $base64decodeheader['alg'] != 'ES256')
            return false;

        $payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);

        return $payload;
    }

    /**
     * base64UrlEncode   https://jwt.io/  中base64UrlEncode编码实现
     * @param string $input 需要编码的字符串
     * @return string
     */
    private static function base64UrlEncode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * base64UrlEncode  https://jwt.io/  中base64UrlEncode解码实现
     * @param string $input 需要解码的字符串
     * @return bool|string
     */
    private static function base64UrlDecode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input .= str_repeat('=', $addlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

}

参考文章:IAP 自动续费后端接入指南_theCrucian的博客-CSDN博客

iOS自动续订订阅开发----验证收据和状态回调JSON解析 - 简书

你可能感兴趣的:(iOS,PHP,iap,连续订阅,v2notification,jwt,iOS)