iOS In-App Purchase 内购之 验证订单是沙盒环境还是真实环境

测试环境

在sandbox中验证receipt:https://sandbox.itunes.apple.com/verifyReceipt

在生产环境中验证receipt:https://buy.itunes.apple.com/verifyReceipt

那么如何自动的识别收据是否是sandbox receipt呢?
识别沙盒环境下收据的方法有两种:

  1. 根据收据字段 environment = sandbox。
  2. 根据收据验证接口返回的状态码。
    如果status=21007,则表示当前的收据为沙盒环境下收据, t进行验证。

苹果反馈的状态码:

  • 21000 App Store无法读取你提供的JSON数据
  • 21002 收据数据不符合格式
  • 21003 收据无法被验证
  • 21004 你提供的共享密钥和账户的共享密钥不一致
  • 21005 收据服务器当前不可用
  • 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
  • 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
  • 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

注意:

在verifyWithRetry方法中,首先向向真实环境验证票据,如果是21007则向沙盒环境验证;==但是在消耗品类型的测试中,使用沙盒票据在真实环境中验证票据得到返回码:21002.所以下面代码在真实环境运行时,沙盒测试消耗型商品得不到正确的验证结果==。

/*
    verifyWithRetry the receipt
*/
    IAPVerifier.verifyWithRetry = function(receipt, isBase64, cb) {
        var encoded = null, receiptData = {};
        if (isBase64) {
            encoded = receipt;
        } else {
            encoded = new Buffer(receipt).toString('base64');
        }
        receiptData['receipt-data'] = encoded;
        var options = this.requestOptions();
        return this.verify(receiptData, options, (function(_this) {
            return function(error, data) {
            if (error) return cb(error);
            if ((21007 === (data != null ? data.status : void 0)) && (_this.productionHost == _this.host)) {
                var options_this.requestOptions();
                // 指向沙盒测试环境再次验证
                options.host = 'sandbox.itunes.apple.com/verifyReceipt';
                return _this.verify(receiptData, options, function(err, data) {
                    return cb(err, data);
                });
            } else {
                return cb(err, data);
          }
        };
      })(this));
    };


    /*
      verify the receipt data
     */

    IAPVerifier.verify = function(data, options, cb) {
        var post_data, request;
        post_data = JSON.stringify(data);
        options.headers = {
            'Content-Type': 'application/x-www-form-urlencoded', 
            'Content-Length': post_data.length
            };
        var request = https.request(options, (function(_this) {
            return function(response) {
                var response_chunk = [];
                response.on('data', function(data) {
                if (response.statusCode !== 200) {
                    return cb(new Error("response.statusCode != 200"));
                }
                response_chunk.push(data);
            });
            return response.on('end', function() {
                var responseData, totalData;
                totalData = response_chunk.join('');
                try {
                    responseData = JSON.parse(totalData);
                } catch (_error) {
                    return cb(_error);
                }
                return cb(null, responseData);
            });
        };
      })(this));
      request.write(post_data);
      request.end();
      request.on('error', function (exp) {
          console.log('problem with request: ' + exp.message);
      });
    };
    
    
    IAPVerifier.requestOptions = function() {
      return options = {
        host: 'buy.itunes.apple.com',
        port: 443,
        path: '/verifyReceipt',
        method: "POST",
        rejectUnauthorized: false/*不加:返回证书不受信任CERT_UNTRUSTED*/
      };
    };

建议:

为保证审核的通过,需要在客户端或server进行双重验证,即,先以线上交易验证地址进行验证,如果苹果正式验证服务器的返回验证码code为21007,则再一次连接沙盒测试服务器进行验证即可。在应用提审时,苹果IAP提审验证时是在沙盒环境的进行的,即:苹果在审核App时,只会在sandbox环境购买,其产生的购买凭证,也只能连接苹果的测试验证服务器,如果没有做双验证,需要特别注意此问题,否则会被拒。


PS:上面代码是服务器的验证方式。客户端的验证方式的代码如下:

5、iOS7 客户端验证的订单状态

  • 苹果在iOS7提升了购买凭据的安全性,可以直接单独在客户端完成订单正确性的验证,但是处于金钱考虑,购买完成后,建议还是要做凭据的后台验证工作。

    [objc]  view plain  copy
     
      在CODE上查看代码片 派生到我的代码片
    1. #pragma mark 客户端验证购买凭据  
    2. - (void)verifyTransactionResult  
    3. {  
    4.     // 验证凭据,获取到苹果返回的交易凭据  
    5.     // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址  
    6.     NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];  
    7.     // 从沙盒中获取到购买凭据  
    8.     NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];  
    9.     // 传输的是BASE64编码的字符串  
    10.     /** 
    11.      BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性 
    12.      BASE64是可以编码和解码的 
    13.      */  
    14.     NSDictionary *requestContents = @{  
    15.                                       @"receipt-data": [receipt base64EncodedStringWithOptions:0]  
    16.                                       };  
    17.     NSError *error;  
    18.     // 转换为 JSON 格式  
    19.     NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents  
    20.                                                           options:0  
    21.                                                             error:&error];  
    22.     // 不存在  
    23.     if (!requestData) { /* ... Handle error ... */ }  
    24.       
    25.     // 发送网络POST请求,对购买凭据进行验证  
    26.     NSString *verifyUrlString;  
    27. #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG))  
    28.     verifyUrlString = @"https://sandbox.itunes.apple.com/verifyReceipt";  
    29. #else  
    30.     verifyUrlString = @"https://buy.itunes.apple.com/verifyReceipt";  
    31. #endif  
    32.     // 国内访问苹果服务器比较慢,timeoutInterval 需要长一点  
    33.     NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:verifyUrlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];  
    34.       
    35.     [storeRequest setHTTPMethod:@"POST"];  
    36.     [storeRequest setHTTPBody:requestData];  
    37.       
    38.     // 在后台对列中提交验证请求,并获得官方的验证JSON结果  
    39.     NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
    40.     [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue  
    41.                            completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  
    42.                                if (connectionError) {  
    43.                                    NSLog(@"链接失败");  
    44.                                } else {  
    45.                                    NSError *error;  
    46.                                    NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];  
    47.                                    if (!jsonResponse) {  
    48.                                        NSLog(@"验证失败");  
    49.                                    }  
    50.                                      
    51.                                    // 比对 jsonResponse 中以下信息基本上可以保证数据安全  
    52.                                    /* 
    53.                                     bundle_id 
    54.                                     application_version 
    55.                                     product_id 
    56.                                     transaction_id 
    57.                                     */  
    58.                                      
    59.                                    NSLog(@"验证成功");  
    60.                                }  
    61.                            }];  
    62.       
    63. }  


6、内购验证凭据返回结果状态码说明

  • 苹果反馈的状态码:

    [objc]  view plain  copy
     
      在CODE上查看代码片 派生到我的代码片
    1. 21000 App Store无法读取你提供的JSON数据  
    2. 21002 收据数据不符合格式  
    3. 21003 收据无法被验证  
    4. 21004 你提供的共享密钥和账户的共享密钥不一致  
    5. 21005 收据服务器当前不可用  
    6. 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中  
    7. 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证  
    8. 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证  

猜你喜欢

转载自blog.csdn.net/leemin_ios/article/details/77714784