此篇针对 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;
}
}
}

  

以上便是此次内购支付相关小结,还望多多指点交流!

苹果内购小结 - iOS的更多相关文章

  1. apicloud含有微信支付。支付宝支付和苹果内购的代码

    apicloud含有微信支付.支付宝支付和苹果内购的代码 <!DOCTYPE html> <html> <head> <meta charset=" ...

  2. iOS:苹果内购实践

    iOS 苹果的内购 一.介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝等),当然开发 ...

  3. iOS开发苹果内购的介绍与实现

    1.iOS开发苹果内购的介绍 1.1 介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝 ...

  4. 苹果内购和 Apple Pay

    作者:CC老师_MissCC链接:http://www.jianshu.com/p/e3bc47e81785來源:简书 苹果内购 1.什么是内购? 如果你购买的商品,是在本app中使用和消耗的,就一定 ...

  5. Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解

    这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结 ...

  6. 苹果内购服务器验证之receipt返回多组in_app思考

    最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买 ...

  7. ios 苹果内购订单验证 --- php实现

    验证函数: function appleVerify($receipt_data,$orderId = 0) { /* * 21000 App Store不能读取你提供的JSON对象 * 21002 ...

  8. IOS,苹果内购和添加广告

    内购——应用内购买 通过苹果应用程序商店有三种主要赚钱的方式: 直接收费(与国内大部分用户的消费习惯相悖) 广告(降低用户体验 应用程序名称带Lite可以添加广告) O2O -> Online推 ...

  9. AppStore ipa (苹果内购)笔记

    内购示意图 准备条件 苹果的开发者证书,已经为应用启用App内购,并在Xcode更新配置文件 itunes store设置 itunes中创建App及其它设置 参考:iOS应用程序内购/内付费(一)  ...

随机推荐

  1. Centos 7 系统安装(简单步骤)

    前面步骤忽略.进入安装步骤. 运行安装 到选择语言的时候最好选英文版,这里做模板,用的中文版 接着下一步到安装选项 在日期和时间里,选择上海时区 紧接着进行软件安装选择,如图安装就好 接着进行分区,也 ...

  2. Memcache 学习笔记(一)----Memcache — Linux部署

    Memcache 一.Memcache简介(内容摘自 --百度百科) memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以 ...

  3. Apache服务器运维笔记(5)----容器的处理顺序

    容器在配置文件中是可以多次使用的,同时也可以嵌套使用,但是 Apache 在处理容器时却是有一定顺序的,因此在编写容器配置时需要按照一定的顺序来进行,否则Apache处理的结果很可能不是管理员想要的. ...

  4. HTML+CSS+jQuery 纵向导航 && 横向导航 && 消除IE6 BUG && 感悟怎样学习

    <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...

  5. 浮动属性(float)

    (1.浮动是一种脱离标准文档流的形式. 作用:浮动就是用来制作多个盒子并排显示,也能设置宽高,负责网页排版 1 float:left;  左浮动 2 float:right; 右浮动 3 float: ...

  6. Java Web----Servlet详解

    这篇文章到上一篇,距离的有点遥远呀,隔了大概有两个月把,中间在家过了个年,哈哈~ 现在重新开始拾起,最近在看一本个人觉得很棒的书,<Java Web 整合开发王者归来>,现在写的这一系列基 ...

  7. linux 下apche无法监听端口解决办法(Permission denied: make_sock: could not bind to address)

    想建立一个测试用的虚拟主机,遇到了这个问题:[root@localhost html]# service httpd startStarting httpd: httpd: Could not rel ...

  8. 监控DAG状态

    Add-PSSnapin microsoft.exchange* Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 $server ...

  9. VSTO 开发中 应用ActionPane、CustomTaskPane

    以Excel插件为例: 1. ActionPane 创建 ThisWorkbook 项目 private void ThisWorkbook_Startup(object sender, System ...

  10. 【2D游戏引擎】WIP反思

    WIP(Working In Progress)是我初学游戏引擎开发时候开发的一个2D游戏引擎,当时计划为它实现类似Unity一样的编辑器,具有和Unity相似的工作流,但是由于水平不够,走了很多弯路 ...