iOS内购(IAP)流程记录(代码篇)
2019-12-19 本文已影响0人
顾宇丶
iOS内购(IAP)流程记录(业务篇)
之前已经写过内购前期准备资料的文章,这篇文章梳理下业务实现逻辑。
前期需求:公司是有自己的订单系统,所以我们需要在发起支付的时候需要先去后台获取订单号,拿到订单号后再调用苹果内购流程,最后把订单号和支付凭证返回给后台,由后台去和苹果再次校验交易结果,最后返回订单支付结果给我们。(这是正常流程,内购存在漏单,异常订单稍后讲)。
支付流程图:

了解流程之后,我们就开始编写代码了,我这边是把内购的代码封装了一个单例
准备:
需导入库:
StoreKit
头文件需要引用:
#import <StoreKit/StoreKit.h>
代理添加:
<SKPaymentTransactionObserver,SKProductsRequestDelegate>
代码实现:
1.先去拿到后台订单号,有了后台订单号之后再判断是否有购买权限
[SKPaymentQueue canMakePayments]
2.如果有购买权限,则通过产品id去获取内购项目信息
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];
3.通过苹果内购回调函数去处理
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
// 商品数组
NSArray *productArr = response.products;
if (productArr.count > 0) {
SKProduct *product = nil;
for (SKProduct *p in productArr) {
if ([p.productIdentifier isEqualToString:_productID]) {
product = p;
break;
}
}
// 发起内购
SKPayment *payMent = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payMent];
} else {
//项目id错误
}
}
4.判断交易状态:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
// 获取结果
// 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
for (SKPaymentTransaction *trans in transactions) {
switch (trans.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加进列表");
break;
case SKPaymentTransactionStatePurchased:
NSLog(@"交易完成");
//自己可添加验证
[self completeTransaction:trans];
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateFailed:
NSLog(@"交易失败");
[self failedTransaction:trans];//处理失败逻辑
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateRestored:
NSLog(@"已经购买过商品");
[[SKPaymentQueue defaultQueue] finishTransaction:trans]; //消耗型商品不用写
break;
case SKPaymentTransactionStateDeferred:
break;
default:
break;
}
}
}
5.收到支付成功后把订单号和交易凭证抛给后台:
- (void) completeTransaction:(SKPaymentTransaction *)transaction{
//这里要把SKPaymentTransaction整个对象给后台,记得携带订单号,先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
//正式环境:https://buy.itunes.apple.com/verifyReceipt
//沙箱环境:https://sandbox.itunes.apple.com/verifyReceipt
}
整合好之后,拿到后台返回的订单号每次发起内购调用下面这个函数就可以了
- (void)startIAPWithProductID:(NSString *)productID andOrderNo:(NSString *)orderNo completeHandle: (IAPCompletionHandle)handle{
_handle = handle;
_orderNo = orderNo;
if(productID && productID.length > 0) {
if ([SKPaymentQueue canMakePayments]) {
// 允许内购
_productID = productID;
NSSet *set = [NSSet setWithObjects:productID, nil];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
// 获取内购项目信息
[request start];
} else {
// 不允许内购
}
} else {
NSLog(@"内购项目ID错误");
}
}
不出意外,正常流程就是这样了,但是为了防止漏单的情况,所以增加了漏单机制。
异常订单处理
我的处理逻辑是把苹果返回成功但是后台返回失败的订单存到异常队列,每次启动APP的时候把异常队列轮询一次,就是把订单数据再发送给后台,让后台再去校验,如果校验成功,则在队列中移除异常订单,后台添加购买数据,更新订单状态。
//array是异常数组,包含订单号、交易凭据
-(void)anomalyOrderVerify:(NSMutableArray * )array{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = array.count; i > 0 ; i-- ) {
//循环判断,通过信号量控制
dispatch_semaphore_signal(semaphore);
}
}
这只是一种异常订单处理方法,还有其他的暂时没有添加,网上大神这么多,我也在借鉴他们漏单处理的方式,后面也还会再完善这方面内容,毕竟涉及到支付。