XYAppStorePayHelpler.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. //
  2. // XYAppStorePayHelpler.m
  3. // Starbuds
  4. //
  5. // Created by 翟玉磊 on 2020/1/18.
  6. // Copyright © 2020 翟玉磊. All rights reserved.
  7. //
  8. #import "XYAppStorePayHelpler.h"
  9. #import <StoreKit/StoreKit.h>
  10. #define PayErrorCode 999 //支付默认操作code
  11. @interface XYAppStorePayHelpler ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
  12. /// 当前购买的商品id
  13. @property (nonatomic, readwrite, copy) NSString *itemId;
  14. @property (nonatomic, readwrite, copy) NSString *orderId;
  15. /// 支付成功的回调
  16. @property (nonatomic, readwrite, copy) PayHelplerSuccessBlock successHandler;
  17. /// 支付成功的回调
  18. @property (nonatomic, readwrite, copy) PayHelplerFailureBlock failureHandler;
  19. /// 当前选中的支付类型
  20. @property (nonatomic, readwrite, assign) NSInteger currentPayType;
  21. /// 当前的支付状态
  22. @property (nonatomic, readwrite, assign) NSInteger payStatus;
  23. @end
  24. @implementation XYAppStorePayHelpler
  25. - (void)dealloc {
  26. [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  27. }
  28. - (instancetype)init {
  29. if (self = [super init]) {
  30. // 开启监听苹果支付
  31. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  32. }
  33. return self;
  34. }
  35. - (void)initValue {
  36. self.currentPayType = 101;
  37. self.payStatus = PayErrorCode;
  38. self.orderId = nil;
  39. self.itemId = nil;
  40. }
  41. /// 回去订单支付信息和支付参数
  42. /// @param orderId 订单Id
  43. /// @param payType 支付方式 101苹果支付 201支付宝 301微信
  44. /// @param itemId 商品id
  45. /// @param successHandler 请求成功
  46. /// @param failureHandler 请求失败
  47. - (void)addPayOrderWithOrderId:(NSString *)orderId payType:(NSInteger)payType itemId:(NSString *)itemId successHandler:(nonnull PayHelplerSuccessBlock)successHandler failureHandler:(nonnull PayHelplerFailureBlock)failureHandler {
  48. // 重置默认信息
  49. [self initValue];
  50. if ([APP_Channel_Id isEqualToString:@"21"]) {
  51. // TimiAppStore
  52. self.itemId = [NSString stringWithFormat:@"TM_%@", itemId];
  53. }else if ([APP_Channel_Id isEqualToString:@"275963954855936"]) {
  54. // TmAppStore
  55. self.itemId = [NSString stringWithFormat:@"Tm_%@", itemId];
  56. }
  57. else {
  58. self.itemId = [itemId copy];
  59. }
  60. self.orderId = [orderId copy];
  61. self.currentPayType = payType;
  62. self.successHandler = successHandler;
  63. self.failureHandler = failureHandler;
  64. [self appStorePay];
  65. }
  66. /// 苹果支付
  67. - (void)appStorePay {
  68. // 判断app是否允许apple支付
  69. [MBProgressHUD showActivityMessage:kLocalizedString(@"请稍等...")];
  70. if ([SKPaymentQueue canMakePayments] && StringIsNotEmpty(self.itemId)) {
  71. // 这里的itemId就对应着苹果后台的商品ID,他们是通过这个ID进行联系的。
  72. NSArray *product = [[NSArray alloc] initWithObjects:self.itemId,nil];
  73. NSSet *nsset = [NSSet setWithArray:product];
  74. //SKProductsRequest参考链接:https://developer.apple.com/documentation/storekit/skproductsrequest
  75. //SKProductsRequest 一个对象,可以从App Store检索有关指定产品列表的本地化信息。
  76. SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];// 初始化请求
  77. request.delegate = self;
  78. [request start];// 开始请求
  79. }else {
  80. dispatch_async(dispatch_get_main_queue(), ^{
  81. [MBProgressHUD hideHUD];
  82. !self.failureHandler?:self.failureHandler(PayErrorCode, @"不支持苹果支付", self.currentPayType);
  83. });
  84. }
  85. }
  86. #pragma mark ------ SKProductsRequestDelegate
  87. // 接收到产品的返回信息,然后用返回的商品信息进行发起购买请求
  88. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
  89. NSArray *product = response.products;
  90. SKProduct *requestProduct = nil;
  91. for (SKProduct *pro in product) {
  92. // NSLog(@"%@", [pro description]);
  93. // NSLog(@"%@", [pro localizedTitle]);
  94. // NSLog(@"%@", [pro localizedDescription]);
  95. // NSLog(@"%@", [pro price]);
  96. // NSLog(@"%@", [pro productIdentifier]);
  97. // 如果后台消费条目的ID与我这里需要请求的一样(用于确保订单的正确性)
  98. if([pro.productIdentifier isEqualToString:self.itemId]){
  99. requestProduct = pro;
  100. break;
  101. }
  102. }
  103. if (requestProduct) {
  104. // 发送购买请求,创建票据 这个时候就会有弹框了
  105. SKPayment *payment = [SKPayment paymentWithProduct:requestProduct];
  106. //将票据加入到交易队列
  107. [[SKPaymentQueue defaultQueue] addPayment:payment];
  108. }else {
  109. dispatch_async(dispatch_get_main_queue(), ^{
  110. [MBProgressHUD hideHUD];
  111. !self.failureHandler?:self.failureHandler(PayErrorCode, @"没有该项目", self.currentPayType);
  112. });
  113. }
  114. }
  115. #pragma mark ------ SKRequestDelegate (@protocol SKProductsRequestDelegate <SKRequestDelegate>)
  116. //请求失败
  117. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
  118. // NSLog(@"error:%@", error);
  119. dispatch_async(dispatch_get_main_queue(), ^{
  120. [MBProgressHUD hideHUD];
  121. !self.failureHandler?:self.failureHandler(error.code, @"支付失败", self.currentPayType);
  122. });
  123. }
  124. //反馈请求的产品信息结束后
  125. - (void)requestDidFinish:(SKRequest *)request{
  126. // NSLog(@"信息反馈结束");
  127. }
  128. #pragma mark ------ SKPaymentTransactionObserver 监听购买结果
  129. // 13.监听购买结果
  130. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction
  131. {
  132. for (SKPaymentTransaction *tran in transaction){
  133. // NSLog(@"%@",tran.payment.applicationUsername);
  134. switch (tran.transactionState) {
  135. case SKPaymentTransactionStatePurchasing:
  136. // NSLog(@"商品添加进列表");
  137. // dispatch_async(dispatch_get_main_queue(), ^{
  138. // [MBProgressHUD showActivityMessage:kLocalizedString(@"请稍等...")];
  139. // });
  140. break;
  141. case SKPaymentTransactionStatePurchased:{
  142. // NSLog(@"交易完成");
  143. // 购买后告诉交易队列,把这个成功的交易移除掉。
  144. //走到这就说明这单交易走完了,无论成功失败,所以要给它移出。finishTransaction
  145. // [MBProgressHUD showActivityMessage:kLocalizedString(@"请稍等...")];
  146. [self completeTransaction:tran];//这儿出了问题抛异常,导致下面一句代码没执行
  147. [[SKPaymentQueue defaultQueue] finishTransaction:tran];
  148. }
  149. break;
  150. case SKPaymentTransactionStateFailed:
  151. // NSLog(@"交易失败");
  152. {
  153. dispatch_async(dispatch_get_main_queue(), ^{
  154. [MBProgressHUD hideHUD];
  155. !self.failureHandler?:self.failureHandler(PayErrorCode, @"交易失败", self.currentPayType);
  156. [[SKPaymentQueue defaultQueue] finishTransaction:tran];
  157. });
  158. }
  159. break;
  160. case SKPaymentTransactionStateRestored:
  161. // NSLog(@"已经购买过商品");
  162. [self completeTransaction:tran];
  163. [[SKPaymentQueue defaultQueue] finishTransaction:tran];
  164. break;
  165. case SKPaymentTransactionStateDeferred:
  166. // NSLog(@"交易延迟");
  167. dispatch_async(dispatch_get_main_queue(), ^{
  168. [MBProgressHUD hideHUD];
  169. [MBProgressHUD showActivityMessage:kLocalizedString(@"请稍等...")];
  170. });
  171. break;
  172. default:
  173. break;
  174. }
  175. }
  176. }
  177. #pragma mark 苹果支付完成
  178. - (void)completeTransaction:(SKPaymentTransaction *)transaction {
  179. //交易验证 本地验证方法
  180. /*NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
  181. NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
  182. if(!receipt){
  183. }
  184. NSError *error;
  185. NSDictionary *requestContents = @{
  186. @"receipt-data": [receipt base64EncodedStringWithOptions:0]
  187. };
  188. NSLog(@"requestContentstr:%@",[receipt base64EncodedStringWithOptions:0]);
  189. NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
  190. options:0
  191. error:&error];
  192. //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
  193. //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
  194. // Create a POST request with the receipt data.
  195. NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
  196. NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
  197. [storeRequest setHTTPMethod:@"POST"];
  198. [storeRequest setHTTPBody:requestData];
  199. // Make a connection to the iTunes Store on a background queue.
  200. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  201. [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
  202. completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
  203. if (connectionError) {
  204. } else {
  205. NSError *error;
  206. NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  207. if (!jsonResponse) { }
  208. //Parse the Response
  209. NSLog(@"成功了:%@",jsonResponse);
  210. }
  211. }];*/
  212. //此时告诉后台交易成功,并把receipt传给后台验证
  213. // 验证凭据,获取到苹果返回的交易凭据 从沙盒中获取到购买凭据
  214. NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
  215. NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
  216. NSString *transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
  217. // 缓存苹果支付成功的数据
  218. NSDictionary *dict = @{@"orderId":self.orderId, @"transactionId":transaction.transactionIdentifier, @"receiptData":transactionReceiptString};
  219. [XYAppStorePayHelpler saveAppStoreCuccessData:dict];
  220. // 查询服务端支付状态
  221. [[XYPayAPIManager new] addPayOrderWithOrderId:self.orderId transactionId:transaction.transactionIdentifier receiptData:transactionReceiptString successHandler:^(ZYLResponseModel *responseModel) {
  222. [MBProgressHUD hideHUD];
  223. // 删除已经验证的支付信息
  224. [XYAppStorePayHelpler removeAppStoreCuccessData:self.orderId];
  225. !self.successHandler?:self.successHandler(self.currentPayType);
  226. } failureHandler:^(ZYLNetworkError *error) {
  227. [MBProgressHUD hideHUD];
  228. CustomActionAlertController *alert = [[CustomActionAlertController alloc] initWithTitle:@"提示" message:@"因为网络问题造成数据没有同步,是否刷新" sure:@"重新加载" cancel:@"取消" selctedBlock:^(NSInteger index, NSString * _Nonnull title) {
  229. if (index == 1) {
  230. // 查询服务端支付状态
  231. [[XYPayAPIManager new] addPayOrderWithOrderId:self.orderId transactionId:transaction.transactionIdentifier receiptData:transactionReceiptString successHandler:^(ZYLResponseModel *responseModel) {
  232. // 删除已经验证的支付信息
  233. [XYAppStorePayHelpler removeAppStoreCuccessData:self.orderId];
  234. !self.successHandler?:self.successHandler(self.currentPayType);
  235. } failureHandler:^(ZYLNetworkError *error) {
  236. dispatch_async(dispatch_get_main_queue(), ^{
  237. !self.failureHandler?:self.failureHandler(error.code, error.domain, self.currentPayType);
  238. });
  239. }];
  240. }else {
  241. dispatch_async(dispatch_get_main_queue(), ^{
  242. !self.failureHandler?:self.failureHandler(error.code, error.domain, self.currentPayType);
  243. });
  244. }
  245. }];
  246. [alert show];
  247. }];
  248. }
  249. - (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
  250. if (jsonString == nil) {
  251. return nil;
  252. }
  253. NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
  254. NSError *err;
  255. NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
  256. options:NSJSONReadingMutableContainers
  257. error:&err];
  258. if(err) {
  259. NSLog(@"json解析失败:%@",err);
  260. return nil;
  261. }
  262. return dic;
  263. }
  264. /**
  265. 根据指定位置索引获取指定的urlSchemes
  266. @param index plist里的索引
  267. @return urlSchemes
  268. */
  269. - (NSString *)getUrlSchemesWithUrltypesIndex:(NSInteger)index {
  270. NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
  271. NSArray *bundleUrltypes = [infoDic objectForKey:@"CFBundleURLTypes"];
  272. NSDictionary *bundleDict = [bundleUrltypes objectAtIndex:index];
  273. NSString *urlSchemes = [[bundleDict objectForKey:@"CFBundleURLSchemes"] firstObject];
  274. return urlSchemes;
  275. }
  276. #pragma mark — 缓存苹果支付成功的订单信息
  277. /// 缓存支付成功的订单信息
  278. + (void)saveAppStoreCuccessData:(NSDictionary *)dic {
  279. NSDictionary *orders = [UserDefaults objectForKey:APPSTORE_PAY_SUCCESS_INFO_USERDEFAULTS];
  280. BOOL isExist = NO;
  281. NSString *key = dic[@"orderId"];
  282. for (NSString *orderId in orders.allKeys) {
  283. if ([orderId isEqualToString:key]) {
  284. isExist = YES;
  285. break;
  286. }
  287. }
  288. if (!isExist) {
  289. NSMutableDictionary *allOrderInfo = [NSMutableDictionary dictionaryWithDictionary:orders];
  290. [allOrderInfo addEntriesFromDictionary:@{key:dic}];
  291. [UserDefaults setObject:allOrderInfo forKey:APPSTORE_PAY_SUCCESS_INFO_USERDEFAULTS];
  292. }
  293. }
  294. /// 删除已经验证过的订单信息
  295. + (void)removeAppStoreCuccessData:(NSString *)orderId {
  296. NSMutableDictionary *orders = [NSMutableDictionary dictionaryWithDictionary:[UserDefaults objectForKey:APPSTORE_PAY_SUCCESS_INFO_USERDEFAULTS]];
  297. [orders removeObjectForKey:orderId];
  298. [UserDefaults setObject:orders forKey:APPSTORE_PAY_SUCCESS_INFO_USERDEFAULTS];
  299. [UserDefaults synchronize];
  300. }
  301. /// 检查苹果支付成功没有校验的订单
  302. + (void)checkAppStorePaySuccessNoValidationOrders {
  303. NSDictionary *orders = [UserDefaults objectForKey:APPSTORE_PAY_SUCCESS_INFO_USERDEFAULTS];
  304. // 没有需要校验的订单
  305. if (orders.allKeys.count == 0) {
  306. return;
  307. }
  308. for (NSDictionary *dict in orders.allValues) {
  309. NSString *orderId = dict[@"orderId"];
  310. NSString *transactionId = dict[@"transactionId"];
  311. NSString *receiptData = dict[@"receiptData"];
  312. [[XYPayAPIManager new] addPayOrderWithOrderId:orderId transactionId:transactionId receiptData:receiptData successHandler:^(ZYLResponseModel *responseModel) {
  313. // 删除已经效验的订单信息
  314. [self removeAppStoreCuccessData:orderId];
  315. } failureHandler:^(ZYLNetworkError *error) {
  316. if ([error.codeStr isEqualToString:@"OPERATION_FAILED"]) {
  317. // 该订单已交易完成,请勿重复提交
  318. [self removeAppStoreCuccessData:orderId];
  319. }
  320. }];
  321. }
  322. }
  323. @end