// // NewiPhonePurchaseHelper.m // MingMen // // Created by 罗云飞 on 2017/4/27. // Copyright © 2017年 罗云飞. All rights reserved. // #import "NewiPhonePurchaseHelper.h" @implementation NewiPhonePurchaseHelper NSString* productId = nil;//内购商品id NSString* out_trade_no = nil;//内购商品订单号 static NewiPhonePurchaseHelper *sharedObj = nil; + (instancetype)sharedInstance { static dispatch_once_t onceToken = 0; dispatch_once(&onceToken, ^{ sharedObj = [[super allocWithZone: NULL] init]; }); return sharedObj; } + (id) allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } - (id) copyWithZone:(NSZone *) zone { return self; } #pragma mark - 添加一个交易队列观察者 - (void)setiPhonePurchase { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } - (void)iPhonePurchasePay:(NSDictionary *)orderDictionary { productId = nil; out_trade_no = nil; productId = orderDictionary[@"productId"]; out_trade_no = orderDictionary[@"out_trade_no"]; //productId = [NSString stringWithFormat:@"%@%@",[UserEntity sharedInstance].account,@"f58a98a9_7886_4a27_b296_34c7402ebbfa"]; if ([SKPaymentQueue canMakePayments]) { [self requestProductData:productId]; }else{ _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"暂不支持购买该商品!"):nil; //DLog(@"不允许程序内付费"); } } #pragma mark - 去苹果服务器请求产品信息 - (void)requestProductData:(NSString *)productIds { // [SVProgressHUD show]; NSArray *productArr = [[NSArray alloc]initWithObjects:productIds, nil]; NSSet *productSet = [NSSet setWithArray:productArr]; SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet]; request.delegate = self; [request start]; } #pragma mark - SKProductsRequestDelegate 收到产品返回信息 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSArray *productArr = response.products; if ([productArr count] == 0) { // [SVProgressHUD dismiss]; _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"没有该商品"):nil; //DLog(@"没有该商品"); return; } NSLog(@"\n产品id: %@\n付费数量: %zd",response.invalidProductIdentifiers,productArr.count); SKProduct *p = nil; for (SKProduct *pro in productArr) { NSLog(@"\ndescription:%@\nlocalizedTitle:%@\nlocalizedDescription:%@\nprice:%@\nproductIdentifier:%@",[pro description],[pro localizedTitle],[pro localizedDescription],[pro price],[pro productIdentifier]); if ([pro.productIdentifier isEqualToString:productId]) { p = pro; } } // 发送购买请求 if (p != nil) { //SKPayment *payment = [SKPayment paymentWithProduct:p]; //使用下面这个可以自定义applicationUsername属性,将其与后台返回的订单id串联起来,从而保证用户与订单的准确性 SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p]; if (ISIOS7) { //applicationUsername属性是iOS7才出来 如果不加这个属性可能会有漏单现象 payment.applicationUsername = out_trade_no; } //发送内购请求 [[SKPaymentQueue defaultQueue] addPayment:payment]; } } #pragma mark - SKRequestDelegate - (void)requestDidFinish:(SKRequest *)request { //[SVProgressHUD dismiss]; } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"支付失败"):nil; //[SVProgressHUD showErrorWithStatus:@"支付失败"]; } #pragma mark - SKPaymentTransactionObserver 监听购买结果 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *tran in transactions) { switch (tran.transactionState) { case SKPaymentTransactionStatePurchased: //交易完成 NSLog(@"交易完成 订单号: %@",tran.payment.applicationUsername); // 发送到苹果服务器验证凭证 这里要调上传订单状态接口(APP服务器的接口) [self verifyPurchaseWithPaymentTrasaction:tran]; break; case SKPaymentTransactionStatePurchasing: //商品添加进列表 NSLog(@"商品已经添加进列表 订单号: %@",tran.payment.applicationUsername); break; case SKPaymentTransactionStateRestored: //购买过 // 发送到苹果服务器验证凭证 //这里要调上传订单状态接口(APP服务器的接口) 按照购买成功处理 NSLog(@"已经购买过该商品 订单号: %@",tran.payment.applicationUsername); [self verifyPurchaseWithPaymentTrasaction:tran]; break; case SKPaymentTransactionStateFailed: //交易失败 NSLog(@"购买失败 订单号: %@",tran.payment.applicationUsername); //通知后台销毁订单号 这里要调上传订单状态接口(APP服务器的接口) //后台说暂时不用销毁,让前端自行处理。。。。。汗~ [[SKPaymentQueue defaultQueue]finishTransaction:tran]; _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"购买失败"):nil; break; default: break; } } } #pragma mark - 发送到苹果服务器验证凭证 - (void)verifyPurchaseWithPaymentTrasaction:(SKPaymentTransaction *)tran { // [SVProgressHUD show]; // 验证凭据,获取到苹果返回的交易凭据 // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; // 从沙盒中获取到购买凭据 NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; // 发送网络POST请求,对购买凭据进行验证 //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt //正式验证地址:https://buy.itunes.apple.com/verifyReceipt /* 21000 App Store 不能读取你提供的JSON对象 21002 receipt-data 域的数据有问题 21003 receipt 无法通过验证 21004 提供的 shared secret 不匹配你账号中的 shared secret 21005 receipt 服务器当前不可用 21006 receipt 合法, 但是订阅已过期. 服务器接收到这个状态码时, receipt 数据仍然会解码并一起发送 21007 receipt 是 Sandbox receipt, 但却发送至生产系统的验证服务 21008 receipt 是生产 receipt, 但却发送至 Sandbox 环境的验证服务 */ NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; NSMutableDictionary *parameters = NewMutableDictionaryInit; [parameters safeSetObject:encodeStr forKey:@"receipt_data"]; [parameters safeSetObject:tran.payment.applicationUsername forKey:@"out_trade_no"]; /* [NetworkRequestManager requestGetWithInterfacePrefix:MM_apply_pay_callback parameters:parameters onSuccess:^(id data){ NSMutableDictionary *requestdict = [[[SBJsonParser alloc] init] objectWithString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; [SVProgressHUD dismiss]; if (![requestdict[@"return_code"] isEqualToString:@"00"]) { _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil; }else { [[SKPaymentQueue defaultQueue]finishTransaction:tran]; _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易完成"):nil; } } onFailure:^{ [SVProgressHUD dismiss]; _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil; }]; */ } /* // 验证购买 - (void)verifyPurchaseWithPaymentTrasaction { [SVProgressHUD show]; // 验证凭据,获取到苹果返回的交易凭据 // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; // 从沙盒中获取到购买凭据 NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; // 发送网络POST请求,对购买凭据进行验证 //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt //正式验证地址:https://buy.itunes.apple.com/verifyReceipt // 21000 App Store 不能读取你提供的JSON对象 // 21002 receipt-data 域的数据有问题 // 21003 receipt 无法通过验证 // 21004 提供的 shared secret 不匹配你账号中的 shared secret // 21005 receipt 服务器当前不可用 // 21006 receipt 合法, 但是订阅已过期. 服务器接收到这个状态码时, receipt 数据仍然会解码并一起发送 // 21007 receipt 是 Sandbox receipt, 但却发送至生产系统的验证服务 // 21008 receipt 是生产 receipt, 但却发送至 Sandbox 环境的验证服务 //根据接口环境判断获取内购验证环境 //[SERVER_URL isEqualToString:@"http://119.23.143.32/mmapp/"] ? Pay_SANDBOX : Pay_AppStore NSURL *url = [NSURL URLWithString:Pay_SANDBOX]; NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f]; urlRequest.HTTPMethod = @"POST"; NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr]; NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding]; urlRequest.HTTPBody = payloadData; // 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法 NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil]; [SVProgressHUD dismiss]; // 官方验证结果为空 if (result == nil) { _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil; //NSLog(@"验证失败"); return; } // 比对字典中以下信息基本上可以保证数据安全 NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil]; if (dict != nil) { // bundle_id , application_version , product_id , transaction_id _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易完成"):nil; } NSLog(@"内购验证信息: %@",dict); } */ #pragma mark - 移除支付观察者 - (void)dealloc { NSLog(@"界面销毁"); [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; } @end