iOS 内购,也叫内支付,是在iOS应用内部,向苹果服务器发起购买请求的过程。我们在这边来讲一讲代码的实现过程。还有,在做内购的时候,常常会有丢单现象发生,每到这时候,我的内心几乎是崩溃的。所以后面我们来讲讲如何有效地防止丢单。
先简述一下内购过程。比如说游戏里面,你要买一个装备,这时候你点击了购买按钮;游戏向苹果服务器发送购买请求,弹出loading框;手机弹出苹果账户和密码输入框;输入完账户密码后,钱打到苹果那边,然后提示你购买成功或者失败;游戏loading框消失,成功给你发放商品,失败提示你购买失败。整个购买过程结束。
当然,游戏方必须事先在苹果iTunes后台,配置好相应的商品和价格。具体的配置过程,网络上有很多教程,这边我们就不再赘述。
配置好以后,我们会获得一个产品ID。这个东西,我们待会代码中会用到。
然后工程中创建一个类,我们把它称为SDKAppStore。 import StoreKit头文件。
#import
然后在头部写上StoreKit的协议
@interface SDKAppStore : NSObject<SKProductsRequestDelegate,SKPaymentTransactionObserver>
SKProductsRequestDelegate是商品请求回调,用来告诉你有没有这个商品。
SKPaymentTransactionObserver是交易观察者,用来告诉你交易进行到哪个步骤了。
接下来使用单例的形式
#pragma mark - 获取单例
+ (instancetype)sharedInstance {
static SDKAppStore* instance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
instance = [[SDKAppStore alloc] init];
});
return instance;
}
交易开始之前,我们先要用productId去请求一下商品的信息。
#pragma mark - 开始支付
- (void)startPay:(SDKPayInfo*)payInfo productId:(NSString*)productId {
self.payInfo = payInfo;
self.productId = productId;
if ([SKPaymentQueue canMakePayments]) {
[self requestProducts:productId];
} else {
//不允许程序内付费购买;
[self.appStoreDelegate sdkAppStoreNotAllowPay];
}
}
#pragma mark - 请求商品信息
- (void)requestProducts:(NSString*)productId {
NSSet *productIds = [NSSet setWithObject:productId];
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIds];
productsRequest.delegate = self;
[productsRequest start];
}
当程序调用startPay这个方法,整个购买交易就开始了。payInfo是记录了一些用户信息,到时候要传到游戏服务器去的。productId就是上面说的商品ID。
首先用[SKPaymentQueue canMakePayments]判断一下程序是否可以交易。
然后将productId放到SKProductsRequest里面,调用一下start,这时候商品ID就被发送到苹果服务器了。
在此之前,我们要先设置好回调,不然接受不到返回来的商品信息
productsRequest.delegate = self;
然后重写回调方法
pragma mark - appstore回调 请求商品信息回调
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
SKProduct *product = [products count] > 0 ? [products objectAtIndex:0] : nil;
if (product) {
//添加付款请求到队列
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
//无法获取商品信息
[self.appStoreDelegate sdkAppStoreNoProductInfo];
SDKLog(@"无法获取商品信息");
}
}
这边我们先把返回来的商品信息包装一下,然后放到交易队列里面去
[[SKPaymentQueue defaultQueue] addPayment:payment];
这时候就开始交易啦!
商品信息放到交易队列里面以后,会回调paymentQueue: updatedTransactions:这个方法这个方法。这个方法的作用是告诉你交易进行到哪个步骤了。
#pragma mark - appstore回调 付款请求回调
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction {
for(SKPaymentTransaction *tran in transaction){
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchasing:
//购买中
[self.appStoreDelegate sdkAppStorePaying];
break;
case SKPaymentTransactionStateDeferred:
//购买中 交易被推迟
[self.appStoreDelegate sdkAppStorePaying];
break;
case SKPaymentTransactionStateFailed:
//购买监听 交易失败
[self failedTransaction:tran];
break;
case SKPaymentTransactionStatePurchased:
//购买监听 交易完成
[self completeTransaction:tran];
break;
case SKPaymentTransactionStateRestored:
//购买监听 恢复成功
[self restoreTransaction:tran];
break;
default:
break;
}
}
}
这边是一个switch,分别对应调用交易的各个步骤:
#pragma mark - 交易事务处理
// 交易成功
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
[self checkReceipt:transaction];
[self finishTransaction:transaction wasSuccessful:YES];
}
// 交易失败
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
[self.appStoreDelegate sdkAppStorePayComplete:NO];
[self finishTransaction:transaction wasSuccessful:NO];
}
// 交易恢复
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[self.appStoreDelegate sdkAppStorePayComplete:YES];
[self finishTransaction:transaction wasSuccessful:YES];
}
//结束交易事务
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
所有的事务,到最后都要调用一下finishTransaction这个方法来结束事务。这个方法里面,我们把刚刚记录下来的订单删除。并且调用一下SKPaymentQueue的finishTransaction。这样一个完整的交易才算结束。
如果没有调用SKPaymentQueue的finishTransaction,交易不会关闭。
在这边我们把交易的大概流程走了一遍。下一篇我们来讲讲如何验证票据和防止丢单。