NewiPhonePurchaseHelper.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // NewiPhonePurchaseHelper.m
  3. // MingMen
  4. //
  5. // Created by 罗云飞 on 2017/4/27.
  6. // Copyright © 2017年 罗云飞. All rights reserved.
  7. //
  8. #import "NewiPhonePurchaseHelper.h"
  9. @implementation NewiPhonePurchaseHelper
  10. NSString* productId = nil;//内购商品id
  11. NSString* out_trade_no = nil;//内购商品订单号
  12. static NewiPhonePurchaseHelper *sharedObj = nil;
  13. + (instancetype)sharedInstance
  14. {
  15. static dispatch_once_t onceToken = 0;
  16. dispatch_once(&onceToken, ^{
  17. sharedObj = [[super allocWithZone: NULL] init];
  18. });
  19. return sharedObj;
  20. }
  21. + (id) allocWithZone:(struct _NSZone *)zone
  22. {
  23. return [self sharedInstance];
  24. }
  25. - (id) copyWithZone:(NSZone *) zone
  26. {
  27. return self;
  28. }
  29. #pragma mark - 添加一个交易队列观察者
  30. - (void)setiPhonePurchase
  31. {
  32. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  33. }
  34. - (void)iPhonePurchasePay:(NSDictionary *)orderDictionary
  35. {
  36. productId = nil;
  37. out_trade_no = nil;
  38. productId = orderDictionary[@"productId"];
  39. out_trade_no = orderDictionary[@"out_trade_no"];
  40. //productId = [NSString stringWithFormat:@"%@%@",[UserEntity sharedInstance].account,@"f58a98a9_7886_4a27_b296_34c7402ebbfa"];
  41. if ([SKPaymentQueue canMakePayments]) {
  42. [self requestProductData:productId];
  43. }else{
  44. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"暂不支持购买该商品!"):nil;
  45. //DLog(@"不允许程序内付费");
  46. }
  47. }
  48. #pragma mark - 去苹果服务器请求产品信息
  49. - (void)requestProductData:(NSString *)productIds {
  50. // [SVProgressHUD show];
  51. NSArray *productArr = [[NSArray alloc]initWithObjects:productIds, nil];
  52. NSSet *productSet = [NSSet setWithArray:productArr];
  53. SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
  54. request.delegate = self;
  55. [request start];
  56. }
  57. #pragma mark - SKProductsRequestDelegate 收到产品返回信息
  58. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
  59. NSArray *productArr = response.products;
  60. if ([productArr count] == 0) {
  61. // [SVProgressHUD dismiss];
  62. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"没有该商品"):nil;
  63. //DLog(@"没有该商品");
  64. return;
  65. }
  66. NSLog(@"\n产品id: %@\n付费数量: %zd",response.invalidProductIdentifiers,productArr.count);
  67. SKProduct *p = nil;
  68. for (SKProduct *pro in productArr) {
  69. NSLog(@"\ndescription:%@\nlocalizedTitle:%@\nlocalizedDescription:%@\nprice:%@\nproductIdentifier:%@",[pro description],[pro localizedTitle],[pro localizedDescription],[pro price],[pro productIdentifier]);
  70. if ([pro.productIdentifier isEqualToString:productId]) {
  71. p = pro;
  72. }
  73. }
  74. // 发送购买请求
  75. if (p != nil) {
  76. //SKPayment *payment = [SKPayment paymentWithProduct:p];
  77. //使用下面这个可以自定义applicationUsername属性,将其与后台返回的订单id串联起来,从而保证用户与订单的准确性
  78. SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p];
  79. if (ISIOS7) {
  80. //applicationUsername属性是iOS7才出来 如果不加这个属性可能会有漏单现象
  81. payment.applicationUsername = out_trade_no;
  82. }
  83. //发送内购请求
  84. [[SKPaymentQueue defaultQueue] addPayment:payment];
  85. }
  86. }
  87. #pragma mark - SKRequestDelegate
  88. - (void)requestDidFinish:(SKRequest *)request {
  89. //[SVProgressHUD dismiss];
  90. }
  91. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
  92. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"支付失败"):nil;
  93. //[SVProgressHUD showErrorWithStatus:@"支付失败"];
  94. }
  95. #pragma mark - SKPaymentTransactionObserver 监听购买结果
  96. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
  97. for (SKPaymentTransaction *tran in transactions) {
  98. switch (tran.transactionState) {
  99. case SKPaymentTransactionStatePurchased: //交易完成
  100. NSLog(@"交易完成 订单号: %@",tran.payment.applicationUsername);
  101. // 发送到苹果服务器验证凭证 这里要调上传订单状态接口(APP服务器的接口)
  102. [self verifyPurchaseWithPaymentTrasaction:tran];
  103. break;
  104. case SKPaymentTransactionStatePurchasing: //商品添加进列表
  105. NSLog(@"商品已经添加进列表 订单号: %@",tran.payment.applicationUsername);
  106. break;
  107. case SKPaymentTransactionStateRestored: //购买过
  108. // 发送到苹果服务器验证凭证 //这里要调上传订单状态接口(APP服务器的接口) 按照购买成功处理
  109. NSLog(@"已经购买过该商品 订单号: %@",tran.payment.applicationUsername);
  110. [self verifyPurchaseWithPaymentTrasaction:tran];
  111. break;
  112. case SKPaymentTransactionStateFailed: //交易失败
  113. NSLog(@"购买失败 订单号: %@",tran.payment.applicationUsername);
  114. //通知后台销毁订单号 这里要调上传订单状态接口(APP服务器的接口)
  115. //后台说暂时不用销毁,让前端自行处理。。。。。汗~
  116. [[SKPaymentQueue defaultQueue]finishTransaction:tran];
  117. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"购买失败"):nil;
  118. break;
  119. default:
  120. break;
  121. }
  122. }
  123. }
  124. #pragma mark - 发送到苹果服务器验证凭证
  125. - (void)verifyPurchaseWithPaymentTrasaction:(SKPaymentTransaction *)tran
  126. {
  127. // [SVProgressHUD show];
  128. // 验证凭据,获取到苹果返回的交易凭据
  129. // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
  130. NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
  131. // 从沙盒中获取到购买凭据
  132. NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
  133. // 发送网络POST请求,对购买凭据进行验证
  134. //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
  135. //正式验证地址:https://buy.itunes.apple.com/verifyReceipt
  136. /*
  137. 21000 App Store 不能读取你提供的JSON对象
  138. 21002 receipt-data 域的数据有问题
  139. 21003 receipt 无法通过验证
  140. 21004 提供的 shared secret 不匹配你账号中的 shared secret
  141. 21005 receipt 服务器当前不可用
  142. 21006 receipt 合法, 但是订阅已过期. 服务器接收到这个状态码时, receipt 数据仍然会解码并一起发送
  143. 21007 receipt 是 Sandbox receipt, 但却发送至生产系统的验证服务
  144. 21008 receipt 是生产 receipt, 但却发送至 Sandbox 环境的验证服务
  145. */
  146. NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
  147. NSMutableDictionary *parameters = NewMutableDictionaryInit;
  148. [parameters safeSetObject:encodeStr forKey:@"receipt_data"];
  149. [parameters safeSetObject:tran.payment.applicationUsername forKey:@"out_trade_no"];
  150. /*
  151. [NetworkRequestManager requestGetWithInterfacePrefix:MM_apply_pay_callback parameters:parameters onSuccess:^(id data){
  152. NSMutableDictionary *requestdict = [[[SBJsonParser alloc] init] objectWithString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
  153. [SVProgressHUD dismiss];
  154. if (![requestdict[@"return_code"] isEqualToString:@"00"]) {
  155. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil;
  156. }else {
  157. [[SKPaymentQueue defaultQueue]finishTransaction:tran];
  158. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易完成"):nil;
  159. }
  160. } onFailure:^{
  161. [SVProgressHUD dismiss];
  162. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil;
  163. }];
  164. */
  165. }
  166. /*
  167. // 验证购买
  168. - (void)verifyPurchaseWithPaymentTrasaction {
  169. [SVProgressHUD show];
  170. // 验证凭据,获取到苹果返回的交易凭据
  171. // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
  172. NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
  173. // 从沙盒中获取到购买凭据
  174. NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
  175. // 发送网络POST请求,对购买凭据进行验证
  176. //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
  177. //正式验证地址:https://buy.itunes.apple.com/verifyReceipt
  178. // 21000 App Store 不能读取你提供的JSON对象
  179. // 21002 receipt-data 域的数据有问题
  180. // 21003 receipt 无法通过验证
  181. // 21004 提供的 shared secret 不匹配你账号中的 shared secret
  182. // 21005 receipt 服务器当前不可用
  183. // 21006 receipt 合法, 但是订阅已过期. 服务器接收到这个状态码时, receipt 数据仍然会解码并一起发送
  184. // 21007 receipt 是 Sandbox receipt, 但却发送至生产系统的验证服务
  185. // 21008 receipt 是生产 receipt, 但却发送至 Sandbox 环境的验证服务
  186. //根据接口环境判断获取内购验证环境
  187. //[SERVER_URL isEqualToString:@"http://119.23.143.32/mmapp/"] ? Pay_SANDBOX : Pay_AppStore
  188. NSURL *url = [NSURL URLWithString:Pay_SANDBOX];
  189. NSMutableURLRequest *urlRequest =
  190. [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
  191. urlRequest.HTTPMethod = @"POST";
  192. NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
  193. NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
  194. NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
  195. urlRequest.HTTPBody = payloadData;
  196. // 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法
  197. NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
  198. [SVProgressHUD dismiss];
  199. // 官方验证结果为空
  200. if (result == nil) {
  201. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易凭据验证失败"):nil;
  202. //NSLog(@"验证失败");
  203. return;
  204. }
  205. // 比对字典中以下信息基本上可以保证数据安全
  206. NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
  207. if (dict != nil) {
  208. // bundle_id , application_version , product_id , transaction_id
  209. _NewiPhonePurchaseCallback?_NewiPhonePurchaseCallback(@"交易完成"):nil;
  210. }
  211. NSLog(@"内购验证信息: %@",dict);
  212. }
  213. */
  214. #pragma mark - 移除支付观察者
  215. - (void)dealloc {
  216. NSLog(@"界面销毁");
  217. [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  218. }
  219. @end