/*注意事项:
1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
2.请务必使用真机来测试,一切以真机为准。
3.项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
4.如果是你自己的设备上已经绑定了自己的AppleID账号请先注销掉,否则你哭爹喊娘都不知道是怎么回事。
5.订单校验 苹果审核app时,仍然在沙盒环境下测试,所以需要先进行正式环境验证,如果发现是沙盒环境则转到沙盒验证。
识别沙盒环境订单方法:
1.根据字段 environment = sandbox。
2.根据验证接口返回的状态码,如果status=21007,则表示当前为沙盒环境。
苹果反馈的状态码:
21000App Store无法读取你提供的JSON数据
21002 订单数据不符合格式
21003 订单无法被验证
21004 你提供的共享密钥和账户的共享密钥不一致
21005 订单服务器当前不可用
21006 订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
21007 订单信息是测试用(sandbox),但却被发送到产品环境中验证
21008 订单信息是产品环境中使用,但却被发送到测试环境中验证
*/
开发内购功能,首先需要一个开发者账号,在 App store connect -> 我的App -> 功能 中申请 如图:
有四种类型,主要看项目的需求而决定(自动续期订阅这个最为麻烦,要签署协议,而已在App 启动是请求苹果服务器,是否存在自动续期订单)
这个图是新增一项内购产品
参考名称:介绍这个内购产品
产品ID(主要用到的):支付的时候用到
其他的都是在app store 中看到的介绍
首先引用 #import <StoreKit/StoreKit.h>
封装一个内购功能,这个在app 中可能多个地方用到
+ (instancetype)shareIAPManager;
//添加内购产品
- (void)addPurchWithProductID:(NSString *)product_id completeHandle:(IAPCompletionHandleBlock)handle;
复制代码
SKProductsRequestDelegate代理方法
交易结束后用到
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSString * productIdentifier = transaction.payment.productIdentifier;
NSData *data = [productIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSString *receipt = [data base64EncodedStringWithOptions:0];
YMLog(@"%@",receipt);
if ([productIdentifier length] > 0) {
// 向自己的服务器验证购买凭证
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;
NSString *goodID = transaction.payment.productIdentifier;
//这里缓存receipt_data,transaction_id 因为后端做校验的时候需要用到这两个字段
YMLog(@"%@",receipt_data);
YMLog(@"%@",transaction_id);
[self retquestApplePay:receipt_data transaction_id:transaction_id goodsID:goodID];
}
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}
复制代码
- (void)completeTransaction:(SKPaymentTransaction *)transaction ;
中 获取
NSString * productIdentifier = transaction.payment.productIdentifier;
NSData *data = [productIdentifier
dataUsingEncoding:NSUTF8StringEncoding];NSString *receipt = [data base64EncodedStringWithOptions:0];
NSString *receipt_data = [data base64EncodedStringWithOptions:0];
/** 事务标识符(交易编号) 交易编号(必传:防止越狱下内购被破解,校验 in_app 参数)*/
NSString *transaction_id = transaction.transactionIdentifier;
NSString *goodID = transaction.payment.productIdentifier;
得到的 transaction_id receipt_data goodID需要上传到app 服务器校验(具体看后端的需求)
//private 提交订单数据到app 服务器校验
- (void)retquestApplePay:(NSString *)receipt_data transaction_id:(NSString *)transaction_id goodsID:(NSString *)goodsId;
复制代码
// 交易失败
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
[self handleActionWithType:IAPPurchFailed data:nil];
}else{
[self handleActionWithType:IAPPurchCancel data:nil];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
#pragma mark -- 结束上次未完成的交易 防止串单
-(void)removeAllUncompleteTransactionBeforeStartNewTransaction{
NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
if (transactions.count > 0) {
//检测是否有未完成的交易
SKPaymentTransaction* transaction = [transactions firstObject];
if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
return;
}
}
}复制代码
IAPDemo链接:Demo