客户端IAP二次验证
1、首先苹果IAP把每次购买抽象成了一个事务(SKPaymentTransaction),
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response;
每次在上述方法中收到反馈信息之后添加的[[SKPaymentQueue defaultQueue] addPayment:payment];
就相当于在事务管理中添加了一个事务,只有事务被正常结束(finishTransaction:)该次支付行为才算完成。
即使一次支付中途被中断,这次事务也并没有丢失。假设支付没有完成App就退出了(比如突然崩溃了),那么当下次App重启之后(调用了addTransactionObserver:),之前被中断的事务会接着进行。
这个的缺点就是中间等待重启的时间可能会很长,影响用户体验。
2、在购买成功后的客户端二次验证。
每次购买成功之后都会把encodeStr存到本地,验证成功再进行删除。以后每次打开应用或打开充值会先查看是否有未完成的验证
//交易结果 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ NSLog(@"5.交易结果"); for (SKPaymentTransaction *transaction in transactions){ switch (transaction.transactionState){ case SKPaymentTransactionStatePurchased:{//交易完成 NSLog(@"交易完成"); [self completeTransaction:transaction]; } break; case SKPaymentTransactionStateFailed://交易失败 { [self failedTransaction:transaction]; NSLog(@"-----交易失败 --------"); [SVProgressHUD dismiss]; UIAlertView *alerView2 = [[UIAlertView alloc] initWithTitle:@"提示" message:@"购买失败,请重新尝试购买" delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil]; [alerView2 show]; }break; case SKPaymentTransactionStateRestored://已经购买过该商品 [self restoreTransaction:transaction]; NSLog(@"-----已经购买过该商品 --------"); case SKPaymentTransactionStatePurchasing: //商品添加进列表 NSLog(@"-----商品添加进列表 --------"); break; default: break; } } } - (void)completeTransaction: (SKPaymentTransaction *)transaction{ NSString * productIdentifier = transaction.payment.productIdentifier; if ([productIdentifier length] > 0) { [SVProgressHUD dismiss]; //添加IAPModel到本地 //获取验证凭证 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; //从沙盒中获取购买凭证 NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; //进行Base64加密 NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; WekeIAPModel *model = [[WekeIAPModel alloc]initWithReceiptStr:encodeStr user_id:[AppSingle sharedAppSingle].user_id]; [[AppSingle sharedAppSingle].IAPArray addObject:model]; [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP]; [self verifyPruchaseWithEncodeStr:encodeStr]; [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; }else{ [SVProgressHUD dismiss]; UIAlertView *alerView2 = [[UIAlertView alloc] initWithTitle:@"提示" message:@"购买失败,未能连接到苹果服务器" delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil]; [alerView2 show]; } } //验证购买凭证 - (void)verifyPruchaseWithEncodeStr :(NSString *)encodeStr{ //验证URL POST请求 NSURL *url = [NSURL URLWithString:Buy_Verify_Receipt_Url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f]; request.HTTPMethod = @"POST"; NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr]; NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding]; request.HTTPBody = payloadData; // 提交验证请求,并获得官方的验证JSON结果 NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; // 官方验证结果为空 if (result == nil) { NSLog(@"验证失败"); }else{ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil]; NSLog(@"线上环境验证结果%@",dict); if ([[dict objectForKey:@"status"] integerValue] == 21007) { //如果是沙盒环境 [self verifyPruchaseInSANDBOXWithEncodeStr:encodeStr]; }else if ([[dict objectForKey:@"status"] integerValue] == 0){ NSLog(@"线上环境验证成功,进行增加积分等操作"); //验证成功 删除IAP数组中的该Model for (WekeIAPModel *model in self.IAPArray) { if ([model.encodeStr isEqualToString:encodeStr]) { [[AppSingle sharedAppSingle].IAPArray removeObject:model]; [self.IAPArray removeObject:model]; [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP]; } } [self postGrede]; }else{ NSLog(@"线上环境验证失败"); UIAlertController *alertCT = [UIAlertController alertControllerWithTitle:@"提示" message:@"充值验证失败,请重试" preferredStyle:(UIAlertControllerStyleAlert)]; UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]; UIAlertAction *care = [UIAlertAction actionWithTitle:@"重试"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self verifyPruchaseWithEncodeStr:encodeStr]; }]; [alertCT addAction:cancel]; [alertCT addAction:care]; [self presentViewController:alertCT animated:YES completion:nil]; } } } //在沙盒中验证 - (void)verifyPruchaseInSANDBOXWithEncodeStr:(NSString *)encodeStr{ //验证URL POST请求 NSURL *url = [NSURL URLWithString:SANDBOX_VERIFY_RECEIPT_URL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f]; request.HTTPMethod = @"POST"; NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr]; NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding]; request.HTTPBody = payloadData; // 提交验证请求,并获得官方的验证JSON结果 NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; if (result== nil) { NSLog(@"沙盒验证失败"); UIAlertController *alertCT = [UIAlertController alertControllerWithTitle:@"提示" message:@"充值验证失败,请重试" preferredStyle:(UIAlertControllerStyleAlert)]; UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]; UIAlertAction *care = [UIAlertAction actionWithTitle:@"重试"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self verifyPruchaseInSANDBOXWithEncodeStr:encodeStr]; }]; [alertCT addAction:cancel]; [alertCT addAction:care]; [self presentViewController:alertCT animated:YES completion:nil]; }else{ NSLog(@"沙盒验证成功"); [self postGrede]; //验证成功 删除IAP数组中的该Model for (WekeIAPModel *model in self.IAPArray) { if ([model.encodeStr isEqualToString:encodeStr]) { [[AppSingle sharedAppSingle].IAPArray removeObject:model]; [self.IAPArray removeObject:model]; [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP]; } } } }