KDIAPManager.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. //
  2. // KDIAPManager.m
  3. // KDChat
  4. //
  5. // Created by JYJ on 2019/6/14.
  6. // Copyright © 2019 dcjf. All rights reserved.
  7. //
  8. #import "KDIAPManager.h"
  9. #import <StoreKit/StoreKit.h>
  10. //#import "KDMainPresenter.h"
  11. #import "XSNetwork.h"
  12. #import <Adjust/Adjust.h>
  13. @interface KDIAPManager() <SKPaymentTransactionObserver,SKProductsRequestDelegate> {
  14. IAPCompletionHandle _handle;
  15. }
  16. @property (nonatomic, strong) NSString* productId;
  17. @property (nonatomic, strong) NSString* orderId;
  18. /** presenter */
  19. //请求网络的.
  20. //@property (nonatomic, strong) KDMainPresenter *presenter;
  21. @end
  22. @implementation KDIAPManager
  23. /**
  24. 单例模式
  25. @return HZIAPManager
  26. */
  27. + (instancetype)shareIAPManager {
  28. static KDIAPManager *IAPManager = nil;
  29. static dispatch_once_t onceToken;
  30. dispatch_once(&onceToken, ^{
  31. IAPManager = [[KDIAPManager alloc] init];
  32. });
  33. return IAPManager;
  34. }
  35. - (instancetype)init {
  36. if (self = [super init]) {
  37. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  38. }
  39. return self;
  40. }
  41. - (void)dealloc {
  42. [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  43. }
  44. #pragma mark -- Method
  45. /** 检测客户端与服务器漏单情况处理*/
  46. + (void)checkOrderStatus {
  47. NSDictionary *orderInfo = [KDIAPManager getReceiptData];
  48. if (orderInfo != nil) {
  49. NSString *orderId = orderInfo[@"orderId"];
  50. id receipt = orderInfo[@"receipt"];
  51. [[KDIAPManager shareIAPManager] verifyPurchaseForServiceWithOrderId:orderId receipt:receipt];
  52. }
  53. }
  54. #pragma mark -- 结束上次未完成的交易
  55. - (void)removeAllUncompleteTransactionsBeforeNewPurchase {
  56. NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
  57. if (transactions.count >= 1) {
  58. for (SKPaymentTransaction* transaction in transactions) {
  59. if (transaction.transactionState == SKPaymentTransactionStatePurchased ||
  60. transaction.transactionState == SKPaymentTransactionStateRestored) {
  61. [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
  62. }
  63. }
  64. } else {
  65. NSLog(@"没有历史未消耗订单");
  66. }
  67. }
  68. - (void)startIAPWithOrderId:(NSString *)orderId productID:(NSString *)productID completeHandle: (IAPCompletionHandle)handle {
  69. _handle = handle;
  70. if(productID && productID.length > 0) {
  71. if ([SKPaymentQueue canMakePayments]) {
  72. [self removeAllUncompleteTransactionsBeforeNewPurchase];
  73. self.orderId = orderId;
  74. // 允许内购
  75. self.productId = productID;
  76. NSSet *set = [NSSet setWithObjects:productID, nil];
  77. SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
  78. request.delegate = self;
  79. // 获取内购项目信息
  80. [request start];
  81. } else {
  82. // 不允许内购
  83. [self handleActionWithType:IAPResultNotArrow data:nil];
  84. }
  85. } else {
  86. NSLog(@"内购项目ID错误");
  87. [XSNetwork errorWithCode:-3 message:@"产品暂时不可用,请检查苹果后台"];
  88. [self handleActionWithType:IAPResultIDError data:nil];
  89. }
  90. }
  91. - (void)handleActionWithType:(IAPResultType)type data:(NSData *)data{
  92. switch (type) {
  93. case IAPResultSuccess:
  94. NSLog(@"购买成功");
  95. break;
  96. case IAPResultFailed:
  97. NSLog(@"购买失败");
  98. break;
  99. case IAPResultCancle:
  100. NSLog(@"用户取消购买");
  101. break;
  102. case IAPResultVerFailed:
  103. NSLog(@"订单校验失败");
  104. break;
  105. case IAPResultVerSuccess:
  106. NSLog(@"订单校验成功");
  107. break;
  108. case IAPResultNotArrow:
  109. NSLog(@"不允许程序内付费");
  110. break;
  111. default:
  112. break;
  113. }
  114. if(_handle){
  115. _handle(type, data);
  116. }
  117. }
  118. #pragma mark -- SKProductsRequestDelegate
  119. /**
  120. 收到产品信息的回调
  121. @param request 请求的信息
  122. @param response 返回的产品信息
  123. */
  124. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
  125. // 商品所在数组
  126. NSArray *productArr = response.products;
  127. if (productArr.count > 0) {
  128. // SKProduct *product = nil;
  129. for (SKProduct *product in productArr) {
  130. if ([product.productIdentifier isEqualToString:self.productId]) {
  131. // product = p;
  132. SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
  133. payment.quantity = 1;
  134. // payment.applicationUsername
  135. // payment.applicationUsername = _orderId;
  136. // 发起内购
  137. [[SKPaymentQueue defaultQueue] addPayment:payment];
  138. break;
  139. }
  140. }
  141. } else {
  142. [self handleActionWithType:IAPResultIDError data:nil];
  143. }
  144. // if ([response.invalidProductIdentifiers containsObject:self.productId]) {
  145. // self.callback(nil, [XSNetwork errorWithCode:-3 message:@"产品暂时不可用,请检查苹果后台"]);
  146. // return;
  147. // }
  148. //
  149. // for (SKProduct* product in response.products) {
  150. //
  151. // SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:product];;
  152. // payment.applicationUsername = self.orderId;
  153. // [SKPaymentQueue.defaultQueue addPayment:payment];
  154. // }
  155. }
  156. #pragma mark -- SKPaymentTransactionObserver
  157. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
  158. // 获取结果
  159. // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
  160. for (SKPaymentTransaction *trans in transactions) {
  161. switch (trans.transactionState) {
  162. case SKPaymentTransactionStatePurchasing:
  163. NSLog(@"商品添加进列表");
  164. break;
  165. case SKPaymentTransactionStatePurchased:
  166. {
  167. NSLog(@"交易完成");
  168. [self completeTransaction:trans];
  169. [[SKPaymentQueue defaultQueue] finishTransaction:trans];
  170. //adjust统计内购支付事件
  171. NSString *kRechargeEventToken = [NSBundle mainBundle].infoDictionary[@"kRechargeEventToken"];
  172. ADJEvent *event = [ADJEvent eventWithEventToken:kRechargeEventToken];
  173. //[event setRevenue:1.0 currency:@"EUR"];
  174. [event setTransactionId:trans.transactionIdentifier]; //避免重复
  175. [Adjust trackEvent:event];
  176. }
  177. break;
  178. case SKPaymentTransactionStateFailed:
  179. NSLog(@"交易失败");
  180. [self failedTransaction:trans];
  181. [[SKPaymentQueue defaultQueue] finishTransaction:trans];
  182. break;
  183. case SKPaymentTransactionStateRestored:
  184. NSLog(@"已经购买过商品");
  185. [[SKPaymentQueue defaultQueue] finishTransaction:trans]; //消耗型商品不用写
  186. break;
  187. case SKPaymentTransactionStateDeferred:
  188. break;
  189. default:
  190. break;
  191. }
  192. }
  193. }
  194. /**
  195. 内购完成
  196. @param transaction 内购项目体
  197. */
  198. - (void)completeTransaction:(SKPaymentTransaction *)transaction {
  199. NSString *productIdentifier = transaction.payment.productIdentifier;
  200. NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
  201. NSData *data = [NSData dataWithContentsOfURL:recepitURL];
  202. id receiptString = [data base64EncodedStringWithOptions:0];
  203. if ([productIdentifier length] > 0 && data) {
  204. if (receiptString && self.orderId.length > 0) {
  205. //1.保存订单号和报文到本地.
  206. [self saveReceiptData:@{@"receipt":receiptString,
  207. @"orderId":self.orderId ?:@""}];
  208. //2.向自己的服务器验证购买凭证.
  209. [self verifyPurchaseForServiceWithOrderId:self.orderId
  210. receipt:receiptString];
  211. }
  212. } else {
  213. [self handleActionWithType:IAPResultVerFailed data:nil];
  214. }
  215. // // 自己向苹果发送验证
  216. // [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
  217. }
  218. ///服务器验证购买凭证.
  219. - (void)verifyPurchaseForServiceWithOrderId:(NSString *)orderId
  220. receipt:(id)receiptString {
  221. if (orderId == nil && !receiptString) {
  222. [self handleActionWithType:IAPResultIDError data:nil];
  223. return;
  224. }
  225. [XSNetwork payWithReceipt:receiptString orderId:orderId complete:^(id object, NSError *error) {
  226. //TTOO:网络异常,服务器校验receiptString失败,就会出现掉单问题.需要保存订单号到本地,下次启动app,继续校验服务器.
  227. NSLog(@"订单校验接口返回:error=%@",error);
  228. if (error != nil) {
  229. NSLog(@"3333. 服务器返回订单校验失败.");
  230. //订单校验失败.
  231. [self handleActionWithType:IAPResultVerFailed data:nil];
  232. }else{
  233. NSLog(@"3333. 服务器返回订单校验成功.");
  234. //订单校验成功,删除本地保存的订单号和苹果返回的data凭证.
  235. [self handleActionWithType:IAPResultSuccess data:nil];
  236. [self removeLocReceiptData];
  237. }
  238. }];
  239. }
  240. /**
  241. 交易失败
  242. @param transaction 内购项目体
  243. */
  244. - (void)failedTransaction:(SKPaymentTransaction *)transaction {
  245. if (transaction.error.code != SKErrorPaymentCancelled) {
  246. [self handleActionWithType:IAPResultFailed data:nil];
  247. } else {
  248. [self handleActionWithType:IAPResultCancle data:nil];
  249. }
  250. }
  251. #pragma mark -- 本地保存一次支付凭证
  252. static NSString *const kSaveReceiptData = @"kSaveReceiptData";
  253. - (void)saveReceiptData:(NSDictionary *)receiptData {
  254. [[NSUserDefaults standardUserDefaults] setValue:receiptData forKey:kSaveReceiptData];
  255. [[NSUserDefaults standardUserDefaults]synchronize];
  256. }
  257. + (NSDictionary *)getReceiptData {
  258. return [[NSUserDefaults standardUserDefaults] valueForKey:kSaveReceiptData];
  259. }
  260. - (void)removeLocReceiptData {
  261. [[NSUserDefaults standardUserDefaults] removeObjectForKey:kSaveReceiptData];
  262. [[NSUserDefaults standardUserDefaults] synchronize];
  263. }
  264. /**
  265. - (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag {
  266. //交易验证
  267. NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
  268. NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
  269. NSError *error;
  270. NSDictionary *requestContents = @{@"receipt-data": [receipt base64EncodedStringWithOptions:0]};
  271. NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
  272. if (!requestData) { // 交易凭证为空验证失败
  273. [self handleActionWithType:IAPResultVerFailed data:nil];
  274. return;
  275. }
  276. //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
  277. //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
  278. NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
  279. if (flag) {
  280. serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
  281. }
  282. NSURL *storeURL = [NSURL URLWithString:serverString];
  283. NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
  284. [storeRequest setHTTPMethod:@"POST"];
  285. [storeRequest setHTTPBody:requestData];
  286. storeRequest.timeoutInterval = 20;
  287. [storeRequest addValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  288. NSURLSession *session = [NSURLSession sharedSession];
  289. NSURLSessionDataTask *task = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  290. if (error) {
  291. // 无法连接服务器,购买校验失败
  292. [self handleActionWithType:IAPResultVerFailed data:nil];
  293. } else {
  294. NSError *error;
  295. NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  296. if (!jsonResponse) {
  297. // 苹果服务器校验数据返回为空校验失败
  298. [self handleActionWithType:IAPResultVerFailed data:nil];
  299. }
  300. // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
  301. NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
  302. if (status && [status isEqualToString:@"21007"]) {
  303. [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
  304. } else if(status && [status isEqualToString:@"0"]){
  305. [self handleActionWithType:IAPResultVerSuccess data:nil];
  306. }
  307. }
  308. }];
  309. [task resume];
  310. }
  311. */
  312. //- (KDMainPresenter *)presenter {
  313. // if (!_presenter) {
  314. // self.presenter = [[KDMainPresenter alloc] init];
  315. // }
  316. // return _presenter;
  317. //}
  318. @end