此篇針對 iOS 支付進行一次小結,好久沒碰這塊了,有些方法 Apple 官方也進行了優化,故也將隨之進行更新.服務器
首先,code 部分將分爲兩部分,一部分在 appdelegate 中,另外一部分單獨封裝在了一個類中執行,須要使用的地方調用的接口方法.網絡
其次,大致支付流程爲獲取到充值價格訂單列表後,選擇對應的價格後向 Apple 發起支付請求,接收到 Apple 支付回調後,根據結果處理相關邏輯,最後將處理完成的結果反饋至用戶.併發
其過程當中會分爲幾個環節來處理:app
若支付失敗則執行異常處理並將最後處理結果信息反饋至用戶;異步
若支付成功則對支付憑證校驗,此篇文章中的校驗過程分爲兩部分,先是由客戶端自行校驗,若校驗成功則將相關用戶信息和支付憑證發送至服務端進行二次校驗;ide
其中,客戶端優先進行交易憑證校驗,校驗失敗則將校驗的異常處理信息反饋至用戶;反之,校驗成功則再次將相關用戶信息和本次支付憑證數據一併發送至服務端進行二次校驗,最終將雙重驗證後的結果信息反饋至用戶,從而爲了不刷單的狀況.post
最後,文章中具體處理邏輯中可能會由於需求的不一樣與實際有些小的出入,但大致流程應該是一致的,也會對應添加相應的註釋,若存在不清楚的地方能夠帖子下方留言溝通交流.測試
大體支付流程:fetch
1.蘋果APP(商家)優化
2.告訴蘋果Store服務器要賣的商品
3.蘋果審覈完(告訴你是否能夠賣)
4.用戶(買商品)
5.蘋果APP(商家)
6.開發票給(用戶)
7.用戶(拿着發票去蘋果Store服務器付款)
8.付款成功(用戶在APP裏得到服務商品)
注:若是要模擬測試內購,須要用真機才能夠測試
憑證校驗地址:
開發環境: https://sandbox.itunes.apple.com/verifyReceipt
生產環境: https://buy.itunes.apple.com/verifyReceipt
憑證校驗異常 code 參照碼:
內購驗證憑據返回結果狀態碼說明(status 狀態)
21000 App Store沒法讀取你提供的JSON數據
21002 收據數據不符合格式
21003 收據沒法被驗證
21004 你提供的共享密鑰和帳戶的共享密鑰不一致
21005 收據服務器當前不可用
21006 收據是有效的,但訂閱服務已通過期。當收到這個信息時,解碼後的收據信息也包含在返回內容中
21007 收據信息是測試用(sandbox),但卻被髮送到產品環境中驗證
21008 收據信息是產品環境中使用,但卻被髮送到測試環境中驗證
Code 以下:
/* 支付_管理類(Apple Pay) */ #import <Foundation/Foundation.h> #import <StoreKit/StoreKit.h> //充值金額類型 typedef NS_ENUM(NSInteger, buyCoinsTag) { IAP0p20=20, IAP1p100, IAP4p600, IAP9p1000, IAP24p6000, }; @import WebKit; @interface PaymentManager : NSObject <SKPaymentTransactionObserver, SKProductsRequestDelegate> { /** 購買類型*/ int buyType; } /** 產品 ID*/ @property (nonatomic, strong) NSString *productID; /** Init*/ + (PaymentManager *)manager; #pragma mark - 方法相關 /** 判斷當前環境是否支持支付購買*/ - (void)buyInfo:(NSDictionary *)buyDic AndViewController:(UIViewController *)vc AndSn:(NSString *)sn; /** 校驗交易憑證 - App Store (Plan A) 該方法以客戶端爲基準: 若客戶端校驗結果失敗,則服務器再也不進行二次校驗; 若客戶端校驗結果成功,則服務器再次進行二次校驗. @param transaction 交易事物 */ - (void)verifyTransaction:(SKPaymentTransaction *)transaction; /** 交易失敗*/ - (void)failedTransaction:(SKPaymentTransaction *)transaction; @end
/* 1.蘋果APP(商家) 2.告訴蘋果Store服務器要賣的商品 3.蘋果審覈完(告訴你是否能夠賣) 4.用戶(買商品) 5.蘋果APP(商家) 6.開發票給(用戶) 7.用戶(拿着發票去蘋果Store服務器付款) 8.付款成功(用戶在APP裏得到服務商品) 注:若是要模擬測試內購,須要用真機才能夠測試 */ #import "PaymentManager.h" #import <objc/runtime.h> #import "GTMBase64.h" #import "RequestToolManager.h" //在內購項目中創的商品單號 #define ProductID_IAP0p20 @"***此處與實際內購價格配置表爲準***" //20 #define ProductID_IAP1p100 @"***此處與實際內購價格配置表爲準***" //100 #define ProductID_IAP4p600 @"***此處與實際內購價格配置表爲準***" //600 #define ProductID_IAP9p1000 @"***此處與實際內購價格配置表爲準***" //1000 #define ProductID_IAP24p6000 @"***此處與實際內購價格配置表爲準***" //6000 #define PaySucceed @"充值成功" #define PayFailed @"充值失敗" #define PayException @"訂單發生異常,請聯繫客服" #define RequestError @"支付成功,等待驗證" /* AppStore增長了驗證內購(In App Purchasement)的方法, 就是蘋果提供一個url地址: 當購買成功時, 會獲得蘋果返回的一個收據(receipt), 蘋果推薦的方法是將收據發給開發者的 server 服務端, 由 server 服務端向上述地址發起請求(post http)消息, 進行驗證, 蘋果將校驗結果返回.這次交易憑證是真購買憑證仍是僞購買憑證. 開發環境地址: https://sandbox.itunes.apple.com/verifyReceipt 生成環境地址: https://buy.itunes.apple.com/verifyReceipt */ #define SANDBOX_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"] #define APP_STORE_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"] #ifdef DEBUG #define VERIFY_RECEIPT_URL SANDBOX_VERIFY_RECEIPT_URL #else #define VERIFY_RECEIPT_URL APP_STORE_VERIFY_RECEIPT_URL #endif @interface PaymentManager () <SKPaymentTransactionObserver,SKProductsRequestDelegate, SKRequestDelegate> /** 訂單編號*/ @property (nonatomic, strong) NSString *tradeNo; @end @implementation PaymentManager #pragma mark - Init + (PaymentManager *)manager { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } #pragma mark - ************************************ 支付初始化 /** Pay Method 支付初始化 具體方法介紹詳見上面註釋中鏈接地址 注:切記綁定 <SKPaymentTransactionObserver> 事件,設置購買隊列的監聽器,實時監聽跟蹤訂單狀態,避免發送丟單的意外,即 [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; @param buyDic 支付所對應的產品信息 @param vc 當前 VC 控件 @param sn 用戶信息 */ - (void)buyInfo:(NSDictionary *)buyDic AndViewController:(UIViewController *)vc AndSn:(NSString *)sn { //判斷網絡是否可用 if (k_NetWorkIsReachable) {// 可用 self.productID = [NSString stringWithFormat:@"%@", [buyDic objectForKey:@"productId"]]; // 獲取訂單編號 NSMutableDictionary *dicParameter = [NSMutableDictionary dictionary]; [dicParameter setValue:@"Apple Pay" forKey:@"payType"]; [dicParameter setValue:sn forKey:@"user"]; [dicParameter setValue:@"iPhone" forKey:@"deviceType"]; [dicParameter setValue:self.productID forKey:@"productId"]; kWeakSelf(self); [[HttpRequestManager shareInstance] PayPOST:URL_ApplePay parameters:dicParameter isEncipherment:YES success:^(id responseObject) { NSDictionary *result = [NSDictionary dictionaryWithDictionary:responseObject]; // 訂單編號 NSString *tradeNo = [NSString stringWithFormat:@"%@", [result objectForKey:@"tradeNo"]]; //設置購買隊列的監聽器 [[SKPaymentQueue defaultQueue] addTransactionObserver:weakself]; //判斷當前是否可支付 if ([SKPaymentQueue canMakePayments]) { NSLog(@"容許程序內付費購買"); //請求產品數據 [weakself fetchProductInformationForIds:weakself.productID AndTradeNo:tradeNo]; } else { NSLog(@"不容許程序內付費購買"); // Callback //[MBProgressHUD showError:@"請開啓手機內付費購買功能" toView:vc.view]; [self hudAlertMessage:@"請開啓手機內付費購買功能"]; } } failure:^(NSError *error) { NSLog(@"buyInfo: %@",error); // Callback //[MBProgressHUD showError:@"服務異常,請從新嘗試" toView:vc.view]; [self hudAlertMessage:@"服務異常,請從新嘗試"]; }]; } else { // Callback //[MBProgressHUD showError:@"暫無網絡" toView:vc.view]; [self hudAlertMessage:@"暫無網絡"]; } } /** 獲取對應的產品數據信息 @param productIds 產品 id @param tradeNo 訂單編號 */ - (void)fetchProductInformationForIds:(NSString *)productIds AndTradeNo:(NSString *)tradeNo { NSLog(@"------------請求對應的產品信息------------"); self.tradeNo = tradeNo; NSArray *product = [[NSArray alloc] initWithObjects:productIds, nil]; //爲該產品標識符建立一個集合 NSSet *nsSet = [NSSet setWithArray:product]; //建立該產品請求對象,並將上面的集合進行初始化它 SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsSet]; request.delegate = self; //向 App Store 發起請求 [request start]; } #pragma mark - ************************************ 交易處理中 /** 獲取 App Store 產品反饋信息 注:設置請求協議代理: <SKProductsRequestDelegate> @param request 請求 @param response 應用結果 */ - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSLog(@"------------收到產品反饋消息------------"); /** 當前產品信息*/ NSArray *myProduct = response.products; if (0 == myProduct.count) { NSLog(@"------------暫無商品------------"); // Callback //[MBProgressHUD showError:@"App Store支付異常,請從新嘗試" toView:vc.view]; [self hudAlertMessage:@"App Store支付異常,請從新嘗試"]; return; } else { NSLog(@"------------預購商品------------"); SKProduct *p = nil; for (SKProduct *product in myProduct) { NSLog(@"*** 產品信息相關[product info] ***\n1.描述信息(SKProduct): %@\n2.產品標題(Title): %@\n3.產品描述信息(Description): %@\n4.產品價格(Price): %@\n5.產品 id(Product id): %@", product, product.localizedTitle, product.localizedDescription, product.price, product.productIdentifier); if([product.productIdentifier isEqualToString:self.productID]){ p = product; } } if (p != nil) { // 將要購買的產品 SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p];// The product is available, let's submit a payment request to the queue NSLog(@"------------發送購買請求------------"); // 發起準備購買流程(異步) [[SKPaymentQueue defaultQueue] addPayment:payment]; } } } #pragma mark - ************************************ 交易校驗中 /** 校驗交易憑證 - App Store 該方法以客戶端爲基準: 若客戶端校驗結果失敗,則服務器再也不進行二次校驗; 若客戶端校驗結果成功,則服務器再次進行二次校驗. @param transaction 交易事物 */ - (void)verifyTransaction:(SKPaymentTransaction *)transaction { /* 使用以下方法獲取購買憑證也 ok,則須要對 data 進行判空操做, NSData *transactionReceipt = [PaymentManager receiptDataFromTransaction:transaction]; // 若 data 爲空則執行以下方法 SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; receiptRefreshRequest.delegate = self; [receiptRefreshRequest start]; return; */ // 從沙盒中獲取到購買憑據 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) { NSData *transactionReceipt = [NSData dataWithContentsOfURL:receiptURL]; NSString *encodeStr = [transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//轉化爲base64字符串 /* 驗證自動訂閱的有效 receipt 示例 { "receipt-data" : "...", "password" : "..." } */ // 拼接請求數據 NSString *bodyStr = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr]; NSData *bodyData = [bodyStr dataUsingEncoding:NSUTF8StringEncoding]; // 建立請求,驗證憑證,蘋果服務器比較坑,建議超時時長設置稍稍長一些 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:VERIFY_RECEIPT_URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f]; request.HTTPBody = bodyData; request.HTTPMethod = @"POST"; NSError *error = nil; // 建立鏈接併發送同步請求,得到官方的驗證JSON結果 NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; if (error) { NSLog(@"App Store 驗證購買過程當中發生錯誤,錯誤信息: %@",error.localizedDescription); // Callback //[MBProgressHUD showError:@"網絡請求超時,請重試" toView:vc.view]; [self hudAlertMessage:@"網絡請求超時,請重試"]; } else { NSDictionary *result = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&error]; /* 內購驗證憑據返回結果狀態碼說明(status 狀態) 21000 App Store沒法讀取你提供的JSON數據 21002 收據數據不符合格式 21003 收據沒法被驗證 21004 你提供的共享密鑰和帳戶的共享密鑰不一致 21005 收據服務器當前不可用 21006 收據是有效的,但訂閱服務已通過期。當收到這個信息時,解碼後的收據信息也包含在返回內容中 21007 收據信息是測試用(sandbox),但卻被髮送到產品環境中驗證 21008 收據信息是產品環境中使用,但卻被髮送到測試環境中驗證 */ NSString *status = [NSString stringWithFormat:@"%@", [result objectForKey:@"status"]]; if (0 == [status intValue]) { NSLog(@"App Store 驗證購買 --- 成功"); NSDictionary *dicReceipt = [NSDictionary dictionaryWithDictionary:[result objectForKey:@"receipt"]]; NSArray *arrInApp = [dicReceipt objectForKey:@"in_app"]; // 注:此處 in_app 字段中數據可能爲多個,需進行循環 NSMutableDictionary *dicInAppReult = [NSMutableDictionary dictionary]; for (NSDictionary *dict in arrInApp) { /** 產品標識*/ NSString *product_id = [NSString stringWithFormat:@"%@", [dict objectForKey:@"product_id"]]; /** 事物標識*/ NSString *transaction_id = [NSString stringWithFormat:@"%@", [dict objectForKey:@"transaction_id"]]; [dicInAppReult setValue:product_id forKey:transaction_id]; } NSString *bundle_id = [NSString stringWithFormat:@"%@", [dicReceipt objectForKey:@"bundle_id"]]; NSString *application_version = [NSString stringWithFormat:@"%@", [dicReceipt objectForKey:@"application_version"]]; if ([bundle_id isEqualToString:kGetBundleId] && [application_version isEqualToString:kAppBundle] && [dicInAppReult.allKeys containsObject:transaction.transactionIdentifier] && [[dicInAppReult objectForKey:transaction.transactionIdentifier] isEqualToString:transaction.payment.productIdentifier] ) { NSLog(@"App Store 憑證驗證 --- 成功"); // 交易成功且憑證驗證成功向服務端提交憑證進行處理 [self commitSeversSucceeWithTransaction:transaction]; } else { NSLog(@"App Store 憑證驗證 --- 失敗"); // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; // Callback //[MBProgressHUD showError:PayFailed toView:vc.view]; [self hudAlertMessage:PayFailed]; } } else { NSLog(@"App Store 憑證驗證 --- 失敗: %@", error.localizedDescription); // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; // Callback //[MBProgressHUD showError:PayFailed toView:vc.view]; [self hudAlertMessage:PayFailed]; } } } else { SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; receiptRefreshRequest.delegate = self; [receiptRefreshRequest start]; return; } } /** 獲取交易憑證 @param transaction 交易事物 @return 結果集 */ + (NSData *)receiptDataFromTransaction:(SKPaymentTransaction *)transaction { NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) { // 從沙盒中獲取到購買憑據 NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; if (!receiptData) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" if ([transaction respondsToSelector:@selector(transactionReceipt)]) { //Works in iOS3 - iOS8, deprected since iOS7, actual deprecated (returns nil) since receiptData = transaction.transactionReceipt; } #pragma clang diagnostic pop } return receiptData; } else { return nil; } } #pragma mark - ************************************ 交易成功 - 向公司服務器驗證購買憑證 /** 交易成功 - 向公司服務器驗證購買憑證 status:0:訂單開始,1:充值成功,2:充值失敗 @param transaction 交易事務 */ - (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction { NSLog(@"------------交易成功向公司服務器驗證購買憑證------------"); #pragma mark - 交易憑證相關 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) { // 取 receipt 的時候要判空,若是文件不存在,就要從蘋果服務器從新刷新下載 receipt 了 // SKReceiptRefreshRequest 刷新的時候,須要用戶輸入 Apple ID,同時須要網絡狀態良好 SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; receiptRefreshRequest.delegate = self; [receiptRefreshRequest start]; return; } NSData *data = [NSData dataWithContentsOfURL:receiptURL]; /** 交易憑證*/ NSString *receipt_data = [data base64EncodedStringWithOptions:0]; /** 事務標識符(交易編號) 交易編號(必傳:防止越獄下內購被破解,校驗 in_app 參數)*/ NSString *transaction_id = transaction.transactionIdentifier; // 此處忽略,純好奇心所驅,一個神奇的 data,拆不出來 ... 你贏了 // NSString * test1 = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // NSString * test2 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // NSError *error; // NSDictionary * test3 = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; // 判空相關 if (receipt_data == nil) { receipt_data = @""; } if (transaction_id == nil) { transaction_id = @""; } NSLog(@"交易憑證:\n%@", receipt_data); NSLog(@"事務標識符(交易編號):\n%@", transaction_id); NSLog(@"產品標識符(內購產品編號) --- productIdentifier:\n%@", transaction.payment.productIdentifier); // NSLog(@"交易日期\nDate: %@,Date(String): %@", transaction_date, strTransaction_date); // NSLog(@"事物狀態:\n%@", transaction_state); NSMutableDictionary *dicParameter = [NSMutableDictionary dictionary]; [dicParameter setValue:kAppBundle forKey:@"appBundle"]; [dicParameter setValue:transaction_id forKey:@"transactionId"];// 查明交易標識符(防止越獄下內購被破解,校驗 in_app 參數) [dicParameter setValue:receipt_data forKey:@"receiptData"];// 收到的收據,即收據證實 transactionReceipt kWeakSelf(self); // Request [[HttpRequestManager shareInstance] PayPOST:URL_ApplePay parameters:dicParameter isEncipherment:NO success:^(id responseObject) { NSDictionary *result = [NSDictionary dictionaryWithDictionary:responseObject]; NSString *status = [NSString stringWithFormat:@"%@", [result objectForKey:@"payStatus"]]; NSLog(@"Pay Callback Result: %@", result); // Callback if ([status isEqualToString:@"success"]) {// 成功 // Callback //[MBProgressHUD showError:PaySucceed toView:vc.view]; [self hudAlertMessage:PaySucceed]; } else { // Callback //[MBProgressHUD showError:PayException toView:vc.view]; [self hudAlertMessage:PayException]; } // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } failure:^(NSError *error) { NSLog(@"commitSeversSucceeWithTransaction: %@", error.localizedDescription); // Callback //[MBProgressHUD showError:RequestError toView:vc.view]; [self hudAlertMessage:RequestError]; }]; } #pragma mark - ************************************ 交易失敗相關 /* 內購驗證憑據返回結果狀態碼說明 21000 App Store沒法讀取你提供的JSON數據 21002 收據數據不符合格式 21003 收據沒法被驗證 21004 你提供的共享密鑰和帳戶的共享密鑰不一致 21005 收據服務器當前不可用 21006 收據是有效的,但訂閱服務已通過期。當收到這個信息時,解碼後的收據信息也包含在返回內容中 21007 收據信息是測試用(sandbox),但卻被髮送到產品環境中驗證 21008 收據信息是產品環境中使用,但卻被髮送到測試環境中驗證 */ #pragma mark - 交易失敗 -> 彈出錯誤信息 Error - (void)failedTransaction:(SKPaymentTransaction *)transaction { NSLog(@"*** 交易失敗 Error code: (%d)",transaction.error.code); if (transaction.error != nil) { switch (transaction.error.code) { case SKErrorUnknown: NSLog(@"SKErrorUnknown --- 未知的錯誤,您可能正在使用越獄手機"); break; case SKErrorClientInvalid: NSLog(@"SKErrorClientInvalid --- 當前蘋果帳戶沒法購買商品(若有疑問,能夠詢問蘋果客服)"); break; case SKErrorPaymentCancelled: NSLog(@"SKErrorPaymentCancelled --- 訂單已取消"); break; case SKErrorPaymentInvalid: NSLog(@"SKErrorPaymentInvalid --- 訂單無效(若有疑問,能夠詢問蘋果客服)"); break; case SKErrorPaymentNotAllowed: NSLog(@"SKErrorPaymentNotAllowed --- 當前蘋果設備沒法購買商品(若有疑問,能夠詢問蘋果客服)"); break; case SKErrorStoreProductNotAvailable: NSLog(@"SKErrorStoreProductNotAvailable --- 當前商品不可用"); break; default: NSLog(@"No Match Found for error -- 未知錯誤"); break; } } // Callback //[MBProgressHUD showError:PayFailed toView:vc.view]; [self hudAlertMessage:PayFailed]; // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } -(void)requestDidFinish:(SKRequest *)request { NSLog(@"------------反饋信息結束------------"); } #pragma mark - 交易失敗 - 請求失敗 -> 彈出錯誤信息 Error - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { NSLog(@"------------彈出錯誤信息------------"); // Callback //[MBProgressHUD showError:error.localizedDescription toView:vc.view]; [self hudAlertMessage:error.localizedDescription]; } #pragma mark - 交易恢復處理 - (void)restoreTransaction:(SKPaymentTransaction *)transaction { // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } #pragma mark - 完成付款隊列恢復完成交易 - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentTransaction *)transaction { NSLog(@"****** 完成付款隊列恢復完成交易: %@", transaction.transactionIdentifier); /* NSMutableArray *productIDsToRestore = From the user ; SKPaymentTransaction *transaction = Current transaction ; if ([productIDsToRestore containsObject:transaction.transactionIdentifier]) { // Re-download the Apple-hosted content, then finish the transaction // and remove the product identifier from the array of product IDs. } else { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } */ } #pragma mark - 付款隊列 - (void)paymentQueue:(SKPaymentQueue *)paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error { NSLog(@"------------付款隊列(當從用戶的購買歷史記錄向隊列添加事務時遇到錯誤時發送)------------\n%@", error.localizedDescription); } #pragma mark - 購買交易 - (void)purchasedTransaction:(SKPaymentTransaction *)transaction { NSLog(@"------------購買交易------------"); NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil]; [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions]; } /** 提示框 @param msg 提示語 */ - (void)hudAlertMessage:(NSString *)msg { UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"" message:msg delegate:nil cancelButtonTitle:NSLocalizedString(@"肯定",nil) otherButtonTitles:nil]; [alerView show]; } #pragma mark - ************************************ Connection delegate - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"connection delegate --- %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection{ } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ switch([(NSHTTPURLResponse *)response statusCode]) { case 200: case 206: break; case 304: break; case 400: break; case 404: break; case 416: break; case 403: break; case 401: case 500: break; default: break; } } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"connection:(NSURLConnection *)connection didFailWithError:(NSError *)error: %@", error.localizedDescription); } - (void)dealloc { [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];// 解除監聽 }
AppDelegate 中 Code:
#import "AppDelegate.h" // Apple pay #import <StoreKit/StoreKit.h> #import "PaymentManager.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // 將觀察者添加到支付隊列中 [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; return YES; } #pragma mark - ****************************** Apple Pay /** 監聽購買交易結果 transactions @param queue 交易隊列 @param transactions 交易事物 */ - (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing:// 商品添加進列表 // Transaction is being added to the server queue. NSLog(@"------------商品添加進列表------------"); /* 不用finish,繼續觀察支付隊列,等待transaction狀態改變 */ break; case SKPaymentTransactionStateDeferred: // The transaction is in the queue, but its final status is pending external action. NSLog(@"------------事務在隊列中,但其最終狀態是等待外部操做"); /* 不用finish,繼續觀察支付隊列 */ break; case SKPaymentTransactionStateFailed:// 交易失敗 // Transaction was cancelled or failed before being added to the server queue. NSLog(@"------------交易失敗: %@", transaction.error.localizedDescription); /* 檢查錯誤並根據須要處理,而後調用 finishTransaction */ [[PaymentManager manager] failedTransaction:transaction]; break; case SKPaymentTransactionStatePurchased:// 交易完成 // Transaction is in queue, user has been charged. Client should complete the transaction. NSLog(@"------------交易完成: %@", transaction.payment.productIdentifier); /* 分發內容給用戶,而後調用 finishTransaction */ [[PaymentManager manager] verifyTransaction:transaction]; break; case SKPaymentTransactionStateRestored:// 已經購買過該商品 // Transaction was restored from user's purchase history. Client should complete the transaction. NSLog(@"------------已經購買過該商品"); /* 分發內容給用戶,而後調用 finishTransaction */ [[PaymentManager manager] verifyTransaction:transaction]; break; default: break; } } }
以上即是這次內購支付相關小結,還望多多指點交流!