// // KDIAPManager.m // KDChat // // Created by JYJ on 2019/6/14. // Copyright © 2019 dcjf. All rights reserved. // #import "KDIAPManager.h" #import #import "KDMainPresenter.h" @interface KDIAPManager() { NSString *_productID; IAPCompletionHandle _handle; NSString *_orderId; } /** presenter */ @property (nonatomic, strong) KDMainPresenter *presenter; @end @implementation KDIAPManager /** 单例模式 @return HZIAPManager */ + (instancetype)shareIAPManager { static KDIAPManager *IAPManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ IAPManager = [[KDIAPManager alloc] init]; }); return IAPManager; } - (instancetype)init { if (self = [super init]) { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } return self; } - (void)dealloc { [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; } #pragma mark -- Method /** 检测客户端与服务器漏单情况处理*/ + (void)checkOrderStatus { NSDictionary *orderInfo = [KDIAPManager getReceiptData]; if (orderInfo != nil) { NSString *orderId = orderInfo[@"orderId"]; NSString *receipt = orderInfo[@"receipt"]; [[KDIAPManager shareIAPManager] verifyPurchaseForServiceWithOrderId:orderId receipt:receipt]; } } #pragma mark -- 结束上次未完成的交易 - (void)removeAllUncompleteTransactionsBeforeNewPurchase { NSArray* transactions = [SKPaymentQueue defaultQueue].transactions; if (transactions.count >= 1) { for (SKPaymentTransaction* transaction in transactions) { if (transaction.transactionState == SKPaymentTransactionStatePurchased || transaction.transactionState == SKPaymentTransactionStateRestored) { [[SKPaymentQueue defaultQueue]finishTransaction:transaction]; } } } else { NSLog(@"没有历史未消耗订单"); } } - (void)startIAPWithOrderId:(NSString *)orderId productID:(NSString *)productID completeHandle: (IAPCompletionHandle)handle { _handle = handle; if(productID && productID.length > 0) { if ([SKPaymentQueue canMakePayments]) { [self removeAllUncompleteTransactionsBeforeNewPurchase]; _orderId = orderId; // 允许内购 _productID = productID; NSSet *set = [NSSet setWithObjects:productID, nil]; SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set]; request.delegate = self; // 获取内购项目信息 [request start]; } else { // 不允许内购 [self handleActionWithType:IAPResultNotArrow data:nil]; } } else { KDLog(@"内购项目ID错误"); [self handleActionWithType:IAPResultIDError data:nil]; } } - (void)handleActionWithType:(IAPResultType)type data:(NSData *)data{ switch (type) { case IAPResultSuccess: KDLog(@"购买成功"); break; case IAPResultFailed: KDLog(@"购买失败"); break; case IAPResultCancle: KDLog(@"用户取消购买"); break; case IAPResultVerFailed: KDLog(@"订单校验失败"); break; case IAPResultVerSuccess: KDLog(@"订单校验成功"); break; case IAPResultNotArrow: KDLog(@"不允许程序内付费"); break; default: break; } if(_handle){ _handle(type, data); } } #pragma mark -- SKProductsRequestDelegate /** 收到产品信息的回调 @param request 请求的信息 @param response 返回的产品信息 */ - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { // 商品所在数组 NSArray *productArr = response.products; if (productArr.count > 0) { // SKProduct *product = nil; for (SKProduct *p in productArr) { if ([p.productIdentifier isEqualToString:_productID]) { // product = p; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p]; payment.quantity = 1; // payment.applicationUsername // payment.applicationUsername = _orderId; // 发起内购 [[SKPaymentQueue defaultQueue] addPayment:payment]; break; } } } else { [self handleActionWithType:IAPResultIDError data:nil]; } } #pragma mark -- SKPaymentTransactionObserver - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { // 获取结果 // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号 for (SKPaymentTransaction *trans in transactions) { switch (trans.transactionState) { case SKPaymentTransactionStatePurchasing: KDLog(@"商品添加进列表"); break; case SKPaymentTransactionStatePurchased: KDLog(@"交易完成"); [self completeTransaction:trans]; [[SKPaymentQueue defaultQueue] finishTransaction:trans]; break; case SKPaymentTransactionStateFailed: KDLog(@"交易失败"); [self failedTransaction:trans]; [[SKPaymentQueue defaultQueue] finishTransaction:trans]; break; case SKPaymentTransactionStateRestored: KDLog(@"已经购买过商品"); [[SKPaymentQueue defaultQueue] finishTransaction:trans]; //消耗型商品不用写 break; case SKPaymentTransactionStateDeferred: break; default: break; } } } /** 内购完成 @param transaction 内购项目体 */ - (void)completeTransaction:(SKPaymentTransaction *)transaction { NSString *productIdentifier = transaction.payment.productIdentifier; NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receipt = [NSData dataWithContentsOfURL:recepitURL]; NSString *receiptString = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; NSString *receiptString1 = [self encodeString:receiptString]; if ([productIdentifier length] > 0 && receipt) { // 向自己的服务器验证购买凭证 [self saveReceiptData:@{@"receipt":receiptString1, @"orderId":_orderId}]; [self verifyPurchaseForServiceWithOrderId:_orderId receipt:receiptString1]; } else { [self handleActionWithType:IAPResultVerFailed data:nil]; } // // 自己向苹果发送验证 // [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO]; } - (NSString*)encodeString:(NSString*)unencodedString { // CharactersToBeEscaped = @":/?&=;+!@#$()~',*"; // CharactersToLeaveUnescaped = @"[]."; NSString *encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)unencodedString, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8)); return encodedString; } - (void)verifyPurchaseForServiceWithOrderId:(NSString *)orderId receipt:(NSString *)receiptString { if (orderId == nil && receiptString == nil) { [self handleActionWithType:IAPResultIDError data:nil]; return; } NSMutableDictionary *param = [NSMutableDictionary dictionary]; param[@"receipt-data"] = receiptString; param[@"order_no"] = orderId; #if DEBUG #endif [self.presenter requestGetIosVerifyWithParameters:param callBack:^(BOOL isSuccess, NSString * _Nonnull eMsg) { if (isSuccess) { [self handleActionWithType:IAPResultSuccess data:nil]; [self removeLocReceiptData]; } else { [self handleActionWithType:IAPResultVerFailed data:nil]; } }]; } - (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag { //交易验证 NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receipt = [NSData dataWithContentsOfURL:recepitURL]; NSError *error; NSDictionary *requestContents = @{@"receipt-data": [receipt base64EncodedStringWithOptions:0]}; NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error]; if (!requestData) { // 交易凭证为空验证失败 [self handleActionWithType:IAPResultVerFailed data:nil]; return; } //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt //In the real environment, use https://buy.itunes.apple.com/verifyReceipt NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt"; if (flag) { serverString = @"https://sandbox.itunes.apple.com/verifyReceipt"; } NSURL *storeURL = [NSURL URLWithString:serverString]; NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL]; [storeRequest setHTTPMethod:@"POST"]; [storeRequest setHTTPBody:requestData]; storeRequest.timeoutInterval = 20; [storeRequest addValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { // 无法连接服务器,购买校验失败 [self handleActionWithType:IAPResultVerFailed data:nil]; } else { NSError *error; NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (!jsonResponse) { // 苹果服务器校验数据返回为空校验失败 [self handleActionWithType:IAPResultVerFailed data:nil]; } // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器 NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]]; if (status && [status isEqualToString:@"21007"]) { [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES]; } else if(status && [status isEqualToString:@"0"]){ [self handleActionWithType:IAPResultVerSuccess data:nil]; } } }]; [task resume]; } /** 交易失败 @param transaction 内购项目体 */ - (void)failedTransaction:(SKPaymentTransaction *)transaction { if (transaction.error.code != SKErrorPaymentCancelled) { [self handleActionWithType:IAPResultFailed data:nil]; } else { [self handleActionWithType:IAPResultCancle data:nil]; } } #pragma mark -- 本地保存一次支付凭证 static NSString *const kSaveReceiptData = @"kSaveReceiptData"; - (void)saveReceiptData:(NSDictionary *)receiptData { [[NSUserDefaults standardUserDefaults] setValue:receiptData forKey:kSaveReceiptData]; [[NSUserDefaults standardUserDefaults]synchronize]; } + (NSDictionary *)getReceiptData { return [[NSUserDefaults standardUserDefaults] valueForKey:kSaveReceiptData]; } - (void)removeLocReceiptData { [[NSUserDefaults standardUserDefaults] removeObjectForKey:kSaveReceiptData]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (KDMainPresenter *)presenter { if (!_presenter) { self.presenter = [[KDMainPresenter alloc] init]; } return _presenter; } @end