此篇針對 iOS 支付進行一次小結,很久沒碰這塊了,有些方法 Apple 官方也進行了優化,故也將隨之進行更新.

首先,code 部分將分為兩部分,一部分在 appdelegate 中,另一部分單獨封裝在了一個類中執行,需要使用的地方調用的接口方法.
其次,大體支付流程為獲取到充值價格訂單列表后,選擇對應的價格后向 Apple 發起支付請求,接收到 Apple 支付回調后,根據結果處理相關邏輯,最后將處理完成的結果反饋至用戶.
其過程中會分為幾個環節來處理:
若支付失敗則執行異常處理並將最后處理結果信息反饋至用戶;
若支付成功則對支付憑證校驗,此篇文章中的校驗過程分為兩部分,先是由客戶端自行校驗,若校驗成功則將相關用戶信息和支付憑證發送至服務端進行二次校驗;
其中,客戶端優先進行交易憑證校驗,校驗失敗則將校驗的異常處理信息反饋至用戶;反之,校驗成功則再次將相關用戶信息和本次支付憑證數據一並發送至服務端進行二次校驗,最終將雙重驗證后的結果信息反饋至用戶,從而為了避免刷單的情況.
最后,文章中具體處理邏輯中可能會因為需求的不同與實際有些小的出入,但大體流程應該是一致的,也會對應添加相應的注釋,若存在不清楚的地方可以帖子下方留言溝通交流.
大致支付流程:
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;
}
}
}
以上便是此次內購支付相關小結,還望多多指點交流!
