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

你可能感兴趣的:(iOS,内付费)