说说IOS内购那些事
1.支付类和协议
Store Kit框架下提供用来处理应用内购大致的类可以分为两部分:1.请求商品,2.购买商品。具体如下:
-
SKMutablePayment:该类为App Store定义了用户的付费请求功能,通过封装特定产品名称和购买数量,用来处理应用程序内购。
-
SKPayment:该类为App Store定义了用户的付费请求功能,通过封装特定产品名称和购买数量,用来处理应用程序内购。
-
SKPaymentQueue:该类为App Store交易支付提供了一个队列,用户可以通过它来校验支付。
-
SKPaymentTransaction:该类定义了支付队列对象。每当一个付款被添加到支付队列时就会创建一个该支付交易对象。当App Store已处理完支付交易时,该对象被回传到应用程序。完成的支付交易对象提供了收据,应用程序可以用它作为支付标识符,用来保存处理后支付的交易记录。
-
SKProduct:该类是作为SKProductsResponse的返回对象的一部分。每个产品对象提供有关您先前在iTunes Connect注册的产品信息。
-
SKProductsRequest:该类对象用于检索从App Store的产品列表本地化的信息。应用程序使用此请求提出局部价格等信息给用户,而无需维护该列表本身。
-
SKProductsResponse:该类对象是App Store在响应有关的产品的列表信息的请求时被返回。
-
SKReceiptRefreshRequest:该类允许应用程序刷新其收据。如果收据是无效或丢失,应用程序可以通过该类申请一个新的收据。在沙箱环境,您可以要求收据性质的任意组合进行测试与批量购买计划收益的状态转换。
-
SKRequest:该类是App Store的请求的抽象类。 SKRequest的子类代表不同类型的请求。
-
SKStoreProductViewController:该类对象提供了一个商店,允许用户从App Store购买其他媒体。
-
SKCloudServiceController:主要是针对Apple Music购买.
-
SKDownload:主要是针对Tunes购买.
Store Kit框架下提供以下协议用来处理请求事件
- SKPaymentTransactionObserver:该协议为一个SKPaymentQueue对象的观察者提供方法。
- SKProductsRequestDelegate:该协议声明由一个SKProductsRequest对象的委托实现的方法。委托收到该产品的信息请求是感兴趣的东西。
- SKRequestDelegate:该协议声明了一个由SKRequest抽象类的任何子类实现的常用方法。
- SKStoreProductViewControllerDelegate:当用户退出商店屏幕实现该协议的对象称为。通常情况下,这个协议是由您的应用程序最初显示的屏幕专卖店视图控制器实现的。
2.一般的内购流程
2.1 场景
用户点击进入一个内购页面,页面刷新当前版本支持的内购商品列表,用户点击商品列表中的一个商品,弹出app strore帐号校验,完成校验后,弹出提示框询问用户是否购买,点击购买或是取消。
2.2 流程
-
获取商品列表:这里获取商品列表,分两步,首先是从自己的服务器获取,其次是从app store获取。最终都是以app store获取为准。具体过程有以下几步:
-
每次进入商品列表界面都会直接去自己的服务器拉取。
-
拉取列表成功后,查询本地是否缓存了商品列表中的每一个商品id,
-
如果所有商品都已经缓存了,则直接返回商品列表,
-
如果校验发现有商品没有缓存(商品更新),此时,清空当前缓存,
-
并且直接去app store拉取商品列表,拉取成功后缓存再返回。
-
购买商品:点击某个商品进程购买,具体流程有以下几步:
-
检验当前是否支持内购(网络环境,权限等)
-
根据当前点击的商品,向自己的服务器去申请订单,如果申请订单成功
-
向app store发起支付请求,如果申请订单失败,直接返回提示信息
-
监听并等待app store返回支付交易信息。
-
如果交易完成,向自己的服务器更新订单状态,并结束完成app store交易。
-
如果交易失败,并结束完成app store交易。
-
如果交易取消,向自己的服务器取消订单,并结束完成app store交易。
2.3 状态处理
整个内购流程里面可分为两种类型的状态,一种是支付行为状态,就是从想苹果发起支付请求开始,中间包括支付校验的取消,确定两种行为状态,最后返回的支付交易完成和失败。一种是针对自己服务器的订单校验的状态,主要是开始校验,成功,失败,已校验,无法校验几种。
- 支付行为状态:该状态是通过监听app store回调来做区别处理。
- 交易完成:获取交易凭证,如果苹果返回了交易凭证,需要更具该交易凭证去和本地服务器做校验,校验成功,说明本次交易成功了,如果校验失败了,(验证找不到订单号)需要更具本次交易重新向服务器申请订单,并校验该订单凭证。
- 交易失败:如果是用户取消交易,直接向服务器取消本次订单。如果是交易失败,更新当前订单凭证到缓存中,用于下次校验。
- 已购买过:更新当前订单凭证到缓存中,标致成功。
- 交易加入队列:无处理。
- 本地订单校验凭证状态:该状态是根据支付行为状态用来对服务器的订单凭证进行管理。
- 校验成功:交易完成后,通过app store返回的凭证信息,去向服务器进行校验,校验成功后,更新当前订单凭证到缓存中,标致成功。
- 校验失败:尝试多次校验。
细节处理
1.缓存
主要缓存:商品列表信息,支付凭证信息。
-
商品列表信息:每次进入内购界面时,根据从服务器拉取的商品信息与本地缓存做比较后,如果保持一直,则直接返回,如果存在差异,则还需要从app store拉取商品信息,再更新本地缓存后返回。
-
支付凭证信息:缓存每次交易支付凭证,用来与服务器订单进行校验。针对校验失败的凭证,会采取多次校验。每次进入商品购买时,需要对上次缓存的未成功的支付凭证进行再次校验。以确保服务器订单状态正确。
2.流程顺序
具体流程以伪代码
-
获取商品列表
- (void)fetchProducts:(xxxCompletBlock)complete { if (![netWork isAvalabel]) { if (complete) complete(....); } [[ProductsOrderModel shareInstance] fetchProductsFromServer:xxxx complete:^(BOOL success, NSArry* products,.....) { if (success) { // 检验本地缓存 NSArry* cacheProducts = [[ProductOrderCache shareInstance] cacheProducte]; ...... //缓存与服务器一直 if (complete) complete(....); //如果不一致,再去app store拉取 [ProductOrderCache shareInstance] cleanCacheProducte]; [[ProductsOrderModel shareInstance] fetchProductsFromApple:xxxx complete:^(BOOL success, NSArry* products,.....) { if (complete) complete(....); ... }]; } else { ..... } }]; }
-
支付
- (void)orderProduct:(NSString*)id complete:(xxxCompletBlock)complete {
//先去服务器获取订单
[[ProductsOrderModel shareInstance] fetchOrderFromServer:id complete:^(BOOL success, ...) {
if (success) {
//发起苹果支付
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
....
}
}];
}
//苹果支付回调
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction state:xxx_Succeed];
break;
case SKPaymentTransactionStateFailed:
[self completeTransaction:transaction state:xxx_Failed];
break;
case SKPaymentTransactionStateRestored:
[self completeTransaction:transaction state:xxx_Succeed];
break;
case SKPaymentTransactionStatePurchasing:
break;
default:
break;
}
}
}
- (void)completeTransaction:transaction state:(xxx_State)state {
switch(state) {
case xxx_Succeed:
{
//拿到苹果返回的凭证去和服务进行校验
[ProductsOrderModel shareInstance] verifyTransactionReceipt:xxx orderId:xxx complete:^(BOOL success, xxxx) {
if (success) {
// 更新缓存状态
[[ProductOrderCache shareInstance] updateOrderState:xxxid state:YES];
...
} else {
// 再次校验
...
}
}];
}
break;
case xxx_Failed:
{
// 更新缓存状态
[[ProductOrderCache shareInstance] updateOrderState:xxxid state:NO];
}
break;
default:
break;
}
}
3.其他
- 每次进行购买时,需要对本地缓存的上一次失败凭证进行再次校验。
- 商品列表信息,支付凭证信息需要做文件缓存。