iOS 内购详细步骤
参考:iOS 内购验证订单方法
无法判断是沙盒环境还是正式环境,思路:先验证正式环境,再验证沙盒环境!
在sandbox中验证receipt
https://sandbox.itunes.apple.com/verifyReceipt
在生产环境中验证receipt
https://buy.itunes.apple.com/verifyReceipt
验证方法:
#define SANDBOX_VERIFY_RECEIPT_URL @"https://sandbox.itunes.apple.com/verifyReceipt"
#define BUY_VERIFY_RECEIPT_URL @"https://buy.itunes.apple.com/verifyReceipt"
#pragma mark - 客户端验证订单的方法
-(void)verifyFinishedWithTransaction:(SKPaymentTransaction *)transaction{
if(transaction.transactionState == SKPaymentTransactionStatePurchased){ // 交易完成
// NSData *transactionReceipt = transaction.transactionReceipt;
NSData *receiptData;
#ifdef NSFoundationVersionNumber_iOS_7_0
//如果NSFoundation的版本在7.0之上,包括7.0
NSURLRequest *urlRequest =[NSURLRequest requestWithURL:[[NSBundle mainBundle] appStoreReceiptURL]];
NSError *error = nil;
receiptData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&error];
#else
//注意transaction.transactionReceipt在iOS7以后被弃用
receiptData = transaction.transactionReceipt;
#endif
// 获取到加密后的transactionReceipt数据,用于发送给server端(可以Post给自己的服务器,让服务器去验证)
NSString* receipent = [self encode:(uint8_t *)receiptData.bytes
length:receiptData.length];
// 在APP客户端做验证(先正式,后沙盒)
NSDictionary *requestContents = @{@"receipt-data":receipent};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:nil];
int status = [self verifyCodeWithURL:BUY_VERIFY_RECEIPT_URL requestData:requestData];
if (status == 0) {
NSLog(@"success ---------------- 正式 ---------------- ");
}else if (status == 21007){
// 沙盒环境订单(在正式环境下验证了,需要验证沙盒)
status = [self verifyCodeWithURL:SANDBOX_VERIFY_RECEIPT_URL requestData:requestData];
if(status == 0){
NSLog(@"success ---------------- 沙盒 ---------------- ");
}else{
NSLog(@"error---------------- 沙盒 ---------------- ");
}
}else{
NSLog(@"error---------------- 正式 ---------------- ");
}
}
}
#pragma mark - 向苹果服务器发起请求,获取状态码
- (int)verifyCodeWithURL:(NSString *)checkURLStr requestData:(NSData *)requestData{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:checkURLStr]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:requestData];
NSError* err;
NSURLResponse *theResponse = nil;
NSData *data=[NSURLConnection sendSynchronousRequest:request
returningResponse:&theResponse
error:&err];
NSError *jsonParsingError = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError];
NSLog(@"requestDict: %@", dict);
if ([dict[@"status"] intValue] == 0) {
NSLog(@"success");
NSString *productId = nil;
#ifdef NSFoundationVersionNumber_iOS_7_0
if ([dict[@"status"] intValue] == 0) {
NSLog(@"success");
}
NSArray* products = dict[@"receipt"][@"in_app"];
for (NSDictionary *item in products) {
if ([item[@"product_id"] isEqualToString:@“实际支付的productId”) {
productId = item[@"product_id"];
break;
}
}
if(productId == nil){
productId = products.lastObject[@"product_id"];
}
#else
productId = dict[@"receipt"][@"product_id"];
#endif
if(productId && [productId isEqualToString:@“实际支付的productId”){
return 0;
}else{
NSLog(@"error----------------商品ID不一致");
return -1;
}
}
return [dict[@"status"] intValue];
}
#pragma mark - 加密字符串方法
- (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t *output = (uint8_t *)data.mutableBytes;
for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger index = (i / 3) * 4;
output[index + 0] = table[(value >> 18) & 0x3F];
output[index + 1] = table[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
沙盒环境下的订单用沙盒环境链接 https://sandbox.itunes.apple.com/verifyReceipt 验证结果:正常返回
沙盒环境下的订单用正式环境链接 https://buy.itunes.apple.com/verifyReceipt 验证结果:异常
苹果反馈的状态码status:
0 成功
21000 App Store无法读取你提供的JSON数据
21002 收据数据不符合格式
21003 收据无法被验证
21004 你提供的共享密钥和账户的共享密钥不一致
21005 收据服务器当前不可用
21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
21007 收据信息是沙盒测试中使用(sandbox),但却被发送到正式环境中验证
21008 收据信息是正式环境中使用,但却被发送到测试环境中验证
所以,可以根据苹果反馈的状态码,判断是沙盒环境还是正式环境:
21007 收据信息是沙盒测试中使用(sandbox),但却被发送到正式环境中验证
21008 收据信息是正式环境中使用,但却被发送到测试环境中验证
验证订单步骤:先正式,后沙盒
如果是沙盒环境,去正式URL下验证订单会返回21008,再用沙盒URL验证,即可!
如果是正式环境,去正式URL下验证订单会正常返回!