iOS 苹果内购-自动订阅处理 踩坑集

1.自动订阅验证失败(21004)

自动订阅必须采用密码的方式才能进行验证;如果苹果验证返回21004的话,就加上password字段去验证。

NSMutableDictionary *parameters_M = [NSMutableDictionary dictionary];
[parameters_M setObject:encodeStr forKey:@"receipt-data"];
[parameters_M setObject:AppPurchasePwd forKey:@"password"];

然后将字典转化为json后再转化为NSData 放在请求HTTPBody里面,关于AppPurchasePwd 内购密钥的申请方式,这篇文章已经解释的很清楚,推荐大家去看一下 https://www.jianshu.com/p/abd2ba4deb54

2.内购验证返回数据 - 说明

以下是通过沙盒验证地址返回的数据

{
environment = Sandbox; //沙盒环境
"latest_receipt" = "---一大串base64数据---";
"latest_receipt_info" =     ( //最新收据
            {
        "expires_date" = "2019-11-21 06:21:20 Etc/GMT";
        "expires_date_ms" = 1574317280000;
        "expires_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:16:20 Etc/GMT";
        "purchase_date_ms" = 1574316980000;
        "purchase_date_pst" = "2019-11-20 22:16:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595277992;
        "web_order_line_item_id" = 1000000048412170;
    }
);
"pending_renewal_info" =     (
            {
        "auto_renew_product_id" = "内购产品ID";
        "auto_renew_status" = 1;
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
    }
);
receipt =     {  //收据
    "adam_id" = 0;
    "app_item_id" = 0;
    "application_version" = 104;
    "bundle_id" = "内购产品ID";
    "download_id" = 0;
    "in_app" =         (
                    {
            "expires_date" = "2019-11-21 06:21:20 Etc/GMT";
            "expires_date_ms" = 1574317280000;
            "expires_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:16:20 Etc/GMT";
            "purchase_date_ms" = 1574316980000;
            "purchase_date_pst" = "2019-11-20 22:16:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595277992;
            "web_order_line_item_id" = 1000000048412170;
        }
    );
    "original_application_version" = "1.0";
    "original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT";
    "original_purchase_date_ms" = 1375340400000;
    "original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles";
    "receipt_creation_date" = "2019-11-21 06:16:21 Etc/GMT";
    "receipt_creation_date_ms" = 1574316981000;
    "receipt_creation_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
    "receipt_type" = ProductionSandbox;
    "request_date" = "2019-11-21 06:16:26 Etc/GMT";
    "request_date_ms" = 1574316986159;
    "request_date_pst" = "2019-11-20 22:16:26 America/Los_Angeles";
    "version_external_identifier" = 0;
};
status = 0;
}
  1. 验证返回字段中,“ status”字段数值为0 代表验证成功,因为自动订阅可能重视的就是到期时间,所以应该关注的是“ expires_date”、“ expires_date_ms”、“ expires_date_pst”这三个字段,expires_date表示过期时间,expires_date_ms 表示过期时间毫秒数值,expires_date_pst表示的是太平洋时间。如果英语足够好的话,这些字段也应该一目了然,英语差一点的话,也可以通过翻译清楚每一个字段代表的意思。

  2. 由于 expires_date 表示的是美国时间,所以一般都采用expires_date_ms数值来进行验证有效期,验证方法为

NSDate *date = [NSDate dateWithTimeIntervalSince1970:[expiresDate longLongValue]/1000];
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyy-MM-dd"];
NSString *the_date_str = [formatter stringFromDate:date];
  1. expiresDate是expires_date_ms的具体数值,值得注意的是这里dateWithTimeIntervalSince1970 方法后的参数不是直接将expiresDate转化为双精度浮点型[expiresDate doubleValue],而是 [expiresDate longLongValue]/1000],不然你得到的时间将超乎你的想象!

你以为这样就结束了,其实不然,当用户进行多次购买或者续订的时候,验证返回的数据将发生改变,其通过沙盒验证地址返回的数据如下:

{
environment = Sandbox;
"latest_receipt" = "---一大串base64数据---";
"latest_receipt_info" =     (
            {
        "expires_date" = "2019-11-21 06:21:20 Etc/GMT";
        "expires_date_ms" = 1574317280000;
        "expires_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:16:20 Etc/GMT";
        "purchase_date_ms" = 1574316980000;
        "purchase_date_pst" = "2019-11-20 22:16:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595277992;
        "web_order_line_item_id" = 1000000048412170;
    },
            {
        "expires_date" = "2019-11-21 06:26:20 Etc/GMT";
        "expires_date_ms" = 1574317580000;
        "expires_date_pst" = "2019-11-20 22:26:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:21:20 Etc/GMT";
        "purchase_date_ms" = 1574317280000;
        "purchase_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595280033;
        "web_order_line_item_id" = 1000000048412171;
    },
            {
        "expires_date" = "2019-11-21 06:31:20 Etc/GMT";
        "expires_date_ms" = 1574317880000;
        "expires_date_pst" = "2019-11-20 22:31:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:26:20 Etc/GMT";
        "purchase_date_ms" = 1574317580000;
        "purchase_date_pst" = "2019-11-20 22:26:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595283022;
        "web_order_line_item_id" = 1000000048412281;
    },
            {
        "expires_date" = "2019-11-21 06:36:20 Etc/GMT";
        "expires_date_ms" = 1574318180000;
        "expires_date_pst" = "2019-11-20 22:36:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:31:20 Etc/GMT";
        "purchase_date_ms" = 1574317880000;
        "purchase_date_pst" = "2019-11-20 22:31:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595285412;
        "web_order_line_item_id" = 1000000048412441;
    },
            {
        "expires_date" = "2019-11-21 06:41:20 Etc/GMT";
        "expires_date_ms" = 1574318480000;
        "expires_date_pst" = "2019-11-20 22:41:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:36:20 Etc/GMT";
        "purchase_date_ms" = 1574318180000;
        "purchase_date_pst" = "2019-11-20 22:36:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595289391;
        "web_order_line_item_id" = 1000000048412586;
    },
            {
        "expires_date" = "2019-11-21 06:46:20 Etc/GMT";
        "expires_date_ms" = 1574318780000;
        "expires_date_pst" = "2019-11-20 22:46:20 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 06:41:20 Etc/GMT";
        "purchase_date_ms" = 1574318480000;
        "purchase_date_pst" = "2019-11-20 22:41:20 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595292096;
        "web_order_line_item_id" = 1000000048412737;
    },
            {
        "expires_date" = "2019-11-21 08:21:13 Etc/GMT";
        "expires_date_ms" = 1574324473000;
        "expires_date_pst" = "2019-11-21 00:21:13 America/Los_Angeles";
        "is_in_intro_offer_period" = false;
        "is_trial_period" = false;
        "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
        "original_purchase_date_ms" = 1574316981000;
        "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
        "purchase_date" = "2019-11-21 07:21:13 Etc/GMT";
        "purchase_date_ms" = 1574320873000;
        "purchase_date_pst" = "2019-11-20 23:21:13 America/Los_Angeles";
        quantity = 1;
        "subscription_group_identifier" = 20569938;
        "transaction_id" = 1000000595319377;
        "web_order_line_item_id" = 1000000048412870;
    }
);
"pending_renewal_info" =     (
            {
        "auto_renew_product_id" = "内购产品ID";
        "auto_renew_status" = 1;
        "original_transaction_id" = 1000000595277992;
        "product_id" = "内购产品ID";
    }
);
receipt =     {
    "adam_id" = 0;
    "app_item_id" = 0;
    "application_version" = 104;
    "bundle_id" = "内购产品ID";
    "download_id" = 0;
    "in_app" =         (
                    {
            "expires_date" = "2019-11-21 06:21:20 Etc/GMT";
            "expires_date_ms" = 1574317280000;
            "expires_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:16:20 Etc/GMT";
            "purchase_date_ms" = 1574316980000;
            "purchase_date_pst" = "2019-11-20 22:16:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595277992;
            "web_order_line_item_id" = 1000000048412170;
        },
                    {
            "expires_date" = "2019-11-21 06:26:20 Etc/GMT";
            "expires_date_ms" = 1574317580000;
            "expires_date_pst" = "2019-11-20 22:26:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:21:20 Etc/GMT";
            "purchase_date_ms" = 1574317280000;
            "purchase_date_pst" = "2019-11-20 22:21:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595280033;
            "web_order_line_item_id" = 1000000048412171;
        },
                    {
            "expires_date" = "2019-11-21 06:31:20 Etc/GMT";
            "expires_date_ms" = 1574317880000;
            "expires_date_pst" = "2019-11-20 22:31:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:26:20 Etc/GMT";
            "purchase_date_ms" = 1574317580000;
            "purchase_date_pst" = "2019-11-20 22:26:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595283022;
            "web_order_line_item_id" = 1000000048412281;
        },
                    {
            "expires_date" = "2019-11-21 06:36:20 Etc/GMT";
            "expires_date_ms" = 1574318180000;
            "expires_date_pst" = "2019-11-20 22:36:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:31:20 Etc/GMT";
            "purchase_date_ms" = 1574317880000;
            "purchase_date_pst" = "2019-11-20 22:31:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595285412;
            "web_order_line_item_id" = 1000000048412441;
        },
                    {
            "expires_date" = "2019-11-21 06:41:20 Etc/GMT";
            "expires_date_ms" = 1574318480000;
            "expires_date_pst" = "2019-11-20 22:41:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:36:20 Etc/GMT";
            "purchase_date_ms" = 1574318180000;
            "purchase_date_pst" = "2019-11-20 22:36:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595289391;
            "web_order_line_item_id" = 1000000048412586;
        },
                    {
            "expires_date" = "2019-11-21 06:46:20 Etc/GMT";
            "expires_date_ms" = 1574318780000;
            "expires_date_pst" = "2019-11-20 22:46:20 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 06:41:20 Etc/GMT";
            "purchase_date_ms" = 1574318480000;
            "purchase_date_pst" = "2019-11-20 22:41:20 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595292096;
            "web_order_line_item_id" = 1000000048412737;
        },
                    {
            "expires_date" = "2019-11-21 08:21:13 Etc/GMT";
            "expires_date_ms" = 1574324473000;
            "expires_date_pst" = "2019-11-21 00:21:13 America/Los_Angeles";
            "is_in_intro_offer_period" = false;
            "is_trial_period" = false;
            "original_purchase_date" = "2019-11-21 06:16:21 Etc/GMT";
            "original_purchase_date_ms" = 1574316981000;
            "original_purchase_date_pst" = "2019-11-20 22:16:21 America/Los_Angeles";
            "original_transaction_id" = 1000000595277992;
            "product_id" = "内购产品ID";
            "purchase_date" = "2019-11-21 07:21:13 Etc/GMT";
            "purchase_date_ms" = 1574320873000;
            "purchase_date_pst" = "2019-11-20 23:21:13 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000595319377;
            "web_order_line_item_id" = 1000000048412870;
        }
    );
    "original_application_version" = "1.0";
    "original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT";
    "original_purchase_date_ms" = 1375340400000;
    "original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles";
    "receipt_creation_date" = "2019-11-21 07:21:14 Etc/GMT";
    "receipt_creation_date_ms" = 1574320874000;
    "receipt_creation_date_pst" = "2019-11-20 23:21:14 America/Los_Angeles";
    "receipt_type" = ProductionSandbox;
    "request_date" = "2019-11-21 07:21:16 Etc/GMT";
    "request_date_ms" = 1574320876419;
    "request_date_pst" = "2019-11-20 23:21:16 America/Los_Angeles";
    "version_external_identifier" = 0;
};
status = 0;
}
  1. 看到这些数据其实我内心是拒绝的!当latest_receipt_info集合存在多个数值的时候,如果你只拿latest_receipt_info 集合里面的第一个字典进行验证的话,你得到的会员将永远是过期的,这坑不大不小,还是的踩啊!通过时间排查,你会发现其实只有最后一个字典是有效的,也就是说最后一个字典的expires_date_ms 大于当前时间,而且苹果内购订阅自带拦截,会员没到期是不能进行再次购买!所以取数字的时候记得 取NSArray 的lastObject(最后一个字典)

  2. 当然这里还有一个坑是在沙盒环境下时间缩短


    截屏2019-11-21下午4.24.16.png

所以当自己看见过期时间很短时,请不要认为是自己代码下的有问题

3.订阅有效期和过期时间验证

即使拿到最后一个数据,也是需要进行过期时间验证,因为内购还有一个功能叫恢复购买,验证方法也简单,就将两个时间进行对比即可

NSDate *new = [NSDate date];
NSTimeInterval timeBetween = [date timeIntervalSinceDate:new];

date 代表过期时间

4.总结

  1. 当然如果有服务器支持的话,我们只需要把base64数据传给服务器,让服务器来帮忙验证,并不需要客服端计算过期时间。但内购自动订阅本身就不需要App进行用户登陆来购买,也不支持跨 Apple ID 进行消费,所以我们采用了本地验证的方式!

  2. 感谢下列网站提供的帮助: