内购是啥
App 内购买项目允许顾客通过访问 App Store 购买您 App 中的内容、功能或服务,并安全处理来自用户的付款。
详情传送门https://help.apple.com/itunes-connect/developer/#/devb57be10e7
下面来说内购集成流程
1.协议
登录苹果开发者中心,进入iTunes Connect,再进入“协议、税务和银行业务”页面,如图
点击进入可以看到,目前共有两个分组,三种合同。(此处有坑,比如我们当前账号不能申请合同!如下图)
Request Contracts 可以申请的合同;
Contracts In Effect 已经生效的合同。
三种合同分别是
Free Applications 免费应用(默认已经生效);
Paid Applications 付费应用,需要申请;
iAd App Network 广告应用,需要申请。
内购对应的是Paid Applications 付费应用,需要申请,如图2.(如果Request按钮不显示,则说明当前账号权限有问题)
点击Request完善信息,提交就行.
2.内购集成
内购实现流程:
1.客户端向Appstore请求购买产品(假设产品信息已经取得),Appstore验证产品成功后,从用户的Apple账户余额中扣费。
2.Appstore向客户端返回一段receipt-data,里面记录了本次交易的证书和签名信息。
3.客户端向我们可以信任的服务器提供receipt-data
4.服务器对receipt-data进行一次base64编码
5.把编码后的receipt-data发往itunes.appstore进行验证
6.itunes.appstore返回验证结果给服务器
7.服务器对商品购买状态以及商品类型,向客户端发放相应的道具与推送数据更新通知
注,下图3步骤和上面流程不是一一对应
我项目里面的购买流程,加入了一点业务逻辑和后台验证流程,有什么问题欢迎大家指出.
3.去苹果开发者中心创建内购商品
如下图5,点击+号去创建内购商品,产品id最好是当前应用+数字,价格区间苹果提供了一张表,商品价格只能是表上的价格,苹果会抽取30%,商家能收到的钱是用户充值的70%.这就造成了部分平台区分安卓和苹果.两端账号不互通,也造就了代充行业,再次就不展开说了.
商品价格大于100$,提交审核的时候要说明这个金额是确认过的,不然可能会被拒
4.代码集成
建议单独建一个类来处理内购业务
导入头文件#import <StoreKit/StoreKit.h>
遵循协议<SKPaymentTransactionObserver,SKProductsRequestDelegate>
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];// 4.设置支付服务
-(void)starBuyToAppStore:(NSString *)goodsID{ if ([SKPaymentQueue canMakePayments]) {//5.判断app是否允许apple支付 [self getRequestAppleProduct:goodsID];// 6.请求苹果后台商品 } else {// NSLog(@"not"); }}
#pragma mark ------ 请求苹果商品- (void)getRequestAppleProduct:(NSString *)goodsID{ self.goodsId = goodsID;//把前面传过来的商品id记录一下,下面要用 // 7.这里的com.czchat.CZChat01就对应着苹果后台的商品ID,他们是通过这个ID进行联系的。 NSArray *product = [[NSArray alloc] initWithObjects:goodsID,nil]; NSSet *nsset = [NSSet setWithArray:product]; //SKProductsRequest参考链接:https://developer.apple.com/documentation/storekit/skproductsrequest //SKProductsRequest 一个对象,可以从App Store检索有关指定产品列表的本地化信息。 SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];// 8.初始化请求 request.delegate = self; [request start];// 9.开始请求}
// 10.接收到产品的返回信息,然后用返回的商品信息进行发起购买请求- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ NSArray *product = response.products; if([product count] == 0){//如果服务器没有产品 return; } SKProduct *requestProduct = nil; for (SKProduct *pro in product) {// NSLog(@"%@", [pro description]);// NSLog(@"%@", [pro localizedTitle]);// NSLog(@"%@", [pro localizedDescription]);// NSLog(@"%@", [pro price]);// NSLog(@"%@", [pro productIdentifier]); // 11.如果后台消费条目的ID与我这里需要请求的一样(用于确保订单的正确性) if([pro.productIdentifier isEqualToString:self.goodsId]){ requestProduct = pro; } } // 12.发送购买请求,创建票据 这个时候就会有弹框了 SKPayment *payment = [SKPayment paymentWithProduct:requestProduct]; [[SKPaymentQueue defaultQueue] addPayment:payment];//将票据加入到交易队列 }
// 13.监听购买结果- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{ if (self.delegate && [self.delegate respondsToSelector:@selector(EMAppStorePay:responseAppStorePayStatusshow:error:)]) { [self.delegate EMAppStorePay:self responseAppStorePayStatusshow:@{@"value":transaction} error:nil]; } for(SKPaymentTransaction *tran in transaction){// NSLog(@"%@",tran.payment.applicationUsername); switch (tran.transactionState) { case SKPaymentTransactionStatePurchased:{// NSLog(@"交易完成"); // 购买后告诉交易队列,把这个成功的交易移除掉。 //走到这就说明这单交易走完了,无论成功失败,所以要给它移出。finishTransaction [self completeTransaction:tran];//这儿出了问题抛异常,导致下面一句代码没执行 [[SKPaymentQueue defaultQueue] finishTransaction:tran]; } break; case SKPaymentTransactionStatePurchasing:// NSLog(@"商品添加进列表"); break; case SKPaymentTransactionStateRestored:// NSLog(@"已经购买过商品"); [[SKPaymentQueue defaultQueue] finishTransaction:tran]; break; case SKPaymentTransactionStateFailed:// NSLog(@"交易失败"); [[SKPaymentQueue defaultQueue] finishTransaction:tran]; break; case SKPaymentTransactionStateDeferred:// NSLog(@"交易还在队列里面,但最终状态还没有决定"); break; default: break; } } }
#pragma mark ------ 支付完成,得到交易凭证- (void)completeTransaction:(SKPaymentTransaction *)transaction{ //此时告诉后台交易成功,并把receipt传给后台验证 NSString *transactionReceiptString= nil; //系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。 // 验证凭据,获取到苹果返回的交易凭据 // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址 NSURLRequest *appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] appStoreReceiptURL]]; NSError *error = nil; // 从沙盒中获取到购买凭据 NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error]; // 20 BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性 21 BASE64是可以编码和解码的 22 transactionReceiptString = [receiptData base64EncodedStringWithOptions:0];//[receiptData base64EncodedStringWithOptions:0]; // NSLog(@"requestContentstr:%@",[receiptData base64EncodedStringWithOptions:0]); // NSDictionary *dic = @{@"orderCode":self.dataOrder.orderCode, // @"receipt":transactionReceiptString, // @"category":@"1" // }; // NSLog(@"diczhi:%@",dic); // // self.tran = transaction; // [self.bizEBeanBuy requestAppStorePaySuccessCallBack:dic];//苹果支付成功,传receipt-data给后台验证 [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; }
5.沙盒测试
如下图6,点添加创建沙盒测试账号,账号未未注册成AppleID的账号,测试前先到设置里退出当前AppleID,登录沙盒测试账号,沙盒测试账号只能用来测试沙盒支付,不具备正常AppleID的功能.
需要了解双重验证,交易凭证判重机制,漏单处理,提交审核坑,集成坑,交易流水对账的朋友下方留言我抽时间补充.
补充内容和代码请看我的另一篇文章:iOS内购全面实战