美文网首页
iOS 自动订阅的一些问题 升级套餐 || 降级套餐

iOS 自动订阅的一些问题 升级套餐 || 降级套餐

作者: traxes | 来源:发表于2020-03-26 23:44 被阅读0次

    交易流程的处理

    主要是自动订阅开发是内购中比较麻烦的,细节问题比较多,很多问题需要确定。
    例如要保证凭证的上传,漏单问题处理,后台如何判断订单的有效期,和订单的类型等等。

    • 凭证上传,漏单处理问题。
      因为内购整个流程涉及到三方,客户端、苹果服务器和后台,所以要把这个三方的一个内购行为做成一个原子交易,就比较麻烦了。

    • 一般的流程:请求苹果服务器->交易成功回调->获取本地凭证上传后台->后台校验(后台再去远程调用苹果服务)->校验成功后回调客户端->客户端获得权限。
      其中有几个可能出现的问题
      1.上传凭证到后台失败
      2.后台请求苹果失败或网络等一系列原因导致回调客户端失败。

      为了内购流程的闭环,就是一定要收到后台的回调结果才算完成。所以对应 1 、2 两点,可以通过凭证保存到本地,然后再次上传,直到服务器回调有结果。这一点很多文章都说过了,但好像有些细节问题没说,凭证就算不保存,再次获取本地凭证也是可以的,因为凭证中保存了所有的交易记录,但如果切换了苹果ID,就无法通过再次获取本地凭证来完成之前的交易了。所以稳妥起见,还是要保存凭证,同时可考虑保存到keychain中避免app卸载导致的凭证丢失问题(考虑keyChain的容容量问题,数量上应该有所限制,实现一个队列来的处理可能会比较好)。

    客户端调试

    客户端配合服务器端就比较麻烦了。客户端可以通过凭证向想苹果服务器获取信息验证收据。

    文档上有指导:

    - (void)query:(NSString *)receipt {
    # ifdef DEBUG
    //    NSData *receipt; // Sent to the server by the device
        // Create the JSON object that describes the request
        NSError *error;
        NSDictionary *requestContents = @{
        @"receipt-data": receipt,
        @"password" : @"xxxxxxxxxxxxxxxxxxxxxx",//内购的秘钥
        @"exclude-old-transactions":@(YES)
        };
        NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
        options:0
        error:&error];
        if (!requestData) { /* ... Handle error ... */ }
        // Create a POST request with the receipt data.
        NSURL *storeURL = [NSURL
        URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
        NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
        [storeRequest setHTTPMethod:@"POST"];
        [storeRequest setHTTPBody:requestData];
        // Make a connection to the iTunes Store on a background queue.
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        ///NSURLConnection 已不推荐使用,如果在线上使用要切换到新的api
        [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                               completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
            if (connectionError) {
                
            } else {
                NSError *error;
                NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                options:0 error:&error];
                if (!jsonResponse) { /* ... Handle error ...*/ } else {
                    
                }
                /* ... Send a response back to the device ... */
                }
        }];
    # endif
    }
    

    如果仅仅在测试中使用,那么上线前记得要屏蔽代码。

    同一个订阅组中更换订阅 xxx_2 升级到 xxx_3

    这个更换订阅,目前发现两种情况,两种情况有一个共同点,就是都会出现
    pending_renewal_info字段,此字段记录了自动续期的待处理信息,例如订阅的变更等。

    参考:https://developer.apple.com/documentation/appstorereceipts/responsebody/pending_renewal_info

    • 同一个订阅组升级订阅
      pending_renewal_info 形如
     "pending_renewal_info" =     (
                    {
                "auto_renew_product_id" = "xxxx_3";//变更的目标订阅
                "auto_renew_status" = 1;
                "original_transaction_id" = 1000000xxxxxx;
                "product_id" = "xxx_2";//原订阅
            }
        );
    

    由便宜的订阅升级到贵的订阅,因为下个订阅会在当前订阅结束才会具体执行变更。所以如果这个时候获取最后一条交易信息,对应的product_id 依然是当前的,如"product_id" = "xxx_2";
    此时最后一条记录为

    "latest_receipt_info" =     (
                    {
                "expires_date" = "2020-03-26 03:43:20 Etc/GMT";
                "expires_date_ms" = 1585194200000;
                "expires_date_pst" = "2020-03-25 20:43:20 America/Los_Angeles";
                "is_in_intro_offer_period" = false;
                "is_trial_period" = false;
                "original_purchase_date" = "2020-03-26 02:54:07 Etc/GMT";
                "original_purchase_date_ms" = 1585191247000;
                "original_purchase_date_pst" = "2020-03-25 19:54:07 America/Los_Angeles";
                "original_transaction_id" = 1000000643447076;
                "product_id" = "xxx_2";
                "purchase_date" = "2020-03-26 03:28:20 Etc/GMT";
                "purchase_date_ms" = 1585193300000;
                "purchase_date_pst" = "2020-03-25 20:28:20 America/Los_Angeles";
                quantity = 1;
            ....省略
            }
        );
    

    如果仅仅判断当前的产品是否生效,判断latest_receipt_info -> expires_date_ms 即可。

    ............然后有一天到期了,就会变更到目标订阅,此时会收到自动续订,再去获取信息,latest_receipt_info 和 pending_renewal_info 会变化如下:

    /// auto_renew_product_id 和  product_id 一致 订阅已经切换过去了。
     "pending_renewal_info" =     (
                    {
                "auto_renew_product_id" = "xxx_3";
                "auto_renew_status" = 1;
                "original_transaction_id" = 1000000643xxxx;
                "product_id" = "xxx_3";
            }
        );
        
        //此为变更后的第一次续订,同时这个续订是真正把订阅切换过去
          "latest_receipt_info" =     (
                    {
                "expires_date" = "2020-03-26 04:43:20 Etc/GMT";
                "expires_date_ms" = 1585197800000;
                "expires_date_pst" = "2020-03-25 21:43:20 America/Los_Angeles";
                "is_in_intro_offer_period" = false;
                "is_trial_period" = false;
                "original_purchase_date" = "2020-03-26 02:54:07 Etc/GMT";
                "original_purchase_date_ms" = 1585191247000;
                "original_purchase_date_pst" = "2020-03-25 19:54:07 America/Los_Angeles";
                "original_transaction_id" = 1000000643447076;
                "product_id" = "xxx_3";
                "purchase_date" = "2020-03-26 03:43:20 Etc/GMT";
                "purchase_date_ms" = 1585194200000;
                "purchase_date_pst" = "2020-03-25 20:43:20 America/Los_Angeles";
                quantity = 1;
              ....省略
            }
        );
    
    

    这样的一个方式,根据苹果的内购提示都是很一致的 “当前xxx订阅到期时,将支付新订阅的费用。”。意思不是立刻补钱,也不是立刻切换,而是下次变更订阅的同时,变更收费。

    • 同一个订阅组降级套餐 xxx_3 降级到 xxx_2
      降级订阅与升级订阅有所不同,会立即生效并按照一定的计算方式退费(可能按未使用时间折算返回),这样的意思就是说这个降级订阅会立即产生一条记录,并且pending_renewal_info 里面的product_id 和 auto_renew_product_id 会一致。具体如下:
    "latest_receipt_info" =     (
                    {
                "expires_date" = "2020-03-26 04:23:23 Etc/GMT";
                "expires_date_ms" = 1585196603000;
                "expires_date_pst" = "2020-03-25 21:23:23 America/Los_Angeles";
                "is_in_intro_offer_period" = false;
                "is_trial_period" = false;
                "original_purchase_date" = "2020-03-26 02:54:07 Etc/GMT";
                "original_purchase_date_ms" = 1585191247000;
                "original_purchase_date_pst" = "2020-03-25 19:54:07 America/Los_Angeles";
                "original_transaction_id" = 1000000643447076;
                "product_id" = "xxx_2";
                "purchase_date" = "2020-03-26 04:08:23 Etc/GMT";
                "purchase_date_ms" = 1585195703000;
                "purchase_date_pst" = "2020-03-25 21:08:23 America/Los_Angeles";
                quantity = 1;
             ......省略
            }
        );
        "pending_renewal_info" =     (
                    {
                "auto_renew_product_id" = "xxx_2";
                "auto_renew_status" = 1;
                "original_transaction_id" = 100000064344xxxx;
                "product_id" = "xxxx_2";
            }
        );
    

    因为在这个过程中可能存在这样的疑惑,升级订阅,怎么不产生新的记录?而latest_receipt_info的记录还是老的production_id,让人费解,实际上变化的过程如上面那样。而这个记录是准确的,同时如果记录依然生效读取这个expires_date_ms过期时间也是准确的。

    所以在确定订阅生效的情况下,以latest_receipt_info的数据来计算是否过期即可。

    以上说明,仅仅测试开发中的分析分享,仅供参考,不一定准确!!!!

    参考链接:
    官方字段说明
    中文文档参考

    相关文章

      网友评论

          本文标题:iOS 自动订阅的一些问题 升级套餐 || 降级套餐

          本文链接:https://www.haomeiwen.com/subject/nwckuhtx.html