// // KDIAPManager.m // KDChat // // Created by JYJ on 2019/6/14. // Copyright © 2019 dcjf. All rights reserved. // #import "KDIAPManager.h" #import //#import "KDMainPresenter.h" #import "XSNetwork.h" @interface KDIAPManager() { IAPCompletionHandle _handle; } @property (nonatomic, strong) NSString* productId; @property (nonatomic, strong) 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"]; id 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]; self.orderId = orderId; // 允许内购 self.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 { NSLog(@"内购项目ID错误"); [XSNetwork errorWithCode:-3 message:@"产品暂时不可用,请检查苹果后台"]; [self handleActionWithType:IAPResultIDError data:nil]; } } - (void)handleActionWithType:(IAPResultType)type data:(NSData *)data{ switch (type) { case IAPResultSuccess: NSLog(@"购买成功"); break; case IAPResultFailed: NSLog(@"购买失败"); break; case IAPResultCancle: NSLog(@"用户取消购买"); break; case IAPResultVerFailed: NSLog(@"订单校验失败"); break; case IAPResultVerSuccess: NSLog(@"订单校验成功"); break; case IAPResultNotArrow: NSLog(@"不允许程序内付费"); 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 *product in productArr) { if ([product.productIdentifier isEqualToString:self.productId]) { // product = p; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; payment.quantity = 1; // payment.applicationUsername // payment.applicationUsername = _orderId; // 发起内购 [[SKPaymentQueue defaultQueue] addPayment:payment]; break; } } } else { [self handleActionWithType:IAPResultIDError data:nil]; } // if ([response.invalidProductIdentifiers containsObject:self.productId]) { // self.callback(nil, [XSNetwork errorWithCode:-3 message:@"产品暂时不可用,请检查苹果后台"]); // return; // } // // for (SKProduct* product in response.products) { // // SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:product];; // payment.applicationUsername = self.orderId; // [SKPaymentQueue.defaultQueue addPayment:payment]; // } } #pragma mark -- SKPaymentTransactionObserver - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { // 获取结果 // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号 for (SKPaymentTransaction *trans in transactions) { switch (trans.transactionState) { case SKPaymentTransactionStatePurchasing: NSLog(@"商品添加进列表"); break; case SKPaymentTransactionStatePurchased: NSLog(@"交易完成"); [self completeTransaction:trans]; [[SKPaymentQueue defaultQueue] finishTransaction:trans]; break; case SKPaymentTransactionStateFailed: NSLog(@"交易失败"); [self failedTransaction:trans]; [[SKPaymentQueue defaultQueue] finishTransaction:trans]; break; case SKPaymentTransactionStateRestored: NSLog(@"已经购买过商品"); [[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 *data = [NSData dataWithContentsOfURL:recepitURL]; id receiptString = [data base64EncodedStringWithOptions:0]; if ([productIdentifier length] > 0 && data) { if (receiptString && self.orderId.length > 0) { //1.保存订单号和报文到本地. [self saveReceiptData:@{@"receipt":receiptString, @"orderId":self.orderId ?:@""}]; //2.向自己的服务器验证购买凭证. [self verifyPurchaseForServiceWithOrderId:self.orderId receipt:receiptString]; } } else { [self handleActionWithType:IAPResultVerFailed data:nil]; } // // 自己向苹果发送验证 // [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO]; } ///服务器验证购买凭证. - (void)verifyPurchaseForServiceWithOrderId:(NSString *)orderId receipt:(id)receiptString { if (orderId == nil && !receiptString) { [self handleActionWithType:IAPResultIDError data:nil]; return; } [XSNetwork payWithReceipt:receiptString orderId:orderId complete:^(id object, NSError *error) { //TTOO:网络异常,服务器校验receiptString失败,就会出现掉单问题.需要保存订单号到本地,下次启动app,继续校验服务器. if (error != nil) { //订单校验失败. [self handleActionWithType:IAPResultVerFailed data:nil]; }else{ //订单校验成功,删除本地保存的订单号和苹果返回的data凭证. [self handleActionWithType:IAPResultSuccess data:nil]; [self removeLocReceiptData]; } }]; } /** 交易失败 @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]; } /** - (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]; } */ //- (KDMainPresenter *)presenter { // if (!_presenter) { // self.presenter = [[KDMainPresenter alloc] init]; // } // return _presenter; //} @end