一.设置协议
1.打开App Store Connect,选择 协议,税务和银行业务
2.因为我已经添加过信息了,所有展示内容是这样的
3.如果想知道要怎么设置相关信息的话 请参考
iOS内购(IAP,In App Purchases-在APP内部支付),设置及使用
4.这是在协议、税务和银行业务页面页面添加银行账户时遇到的问题
4-1. 国内银行CNAPS CODE 查询
4-2. 在提交信息的时候提示 : 账户持有人姓名无效. 持卡人姓名,中文名用拼写,名在前,姓在后 例:Xiaoer Wang
二.添加内购商品
1.打开App Store Connect,- 我的APP,然后再点击你要添加内购的APP - 功能 - App内购项目
打开页面如下图,已填写好完整数据的商品,状态为 (准备提交),未准备好的商品 状态为 (元数据丢失)
2. 点击 (+) 开始创建App内购项目
根据需求来创建内购项目,如果不知道该创建什么类型的话,点击左下角 进一步了解App内购项
3.填写商品信息
填写结束后点击右上角的存储
4.在准备提交的版本中添加APP内购项目
选择完成后点击右上角的存储 准备提交审核
三.添加测试账号来进行测试
1.打开App Store Connect, 点击 用户和访问
打开页面如下,点击 (+) 进行创建测试用户
2.添加测试员
四.代码集成
注意:
1.必须用真机测试。
2.测试的时候必须退出自己的apple ID。弹出页面后登陆沙盒的测试apple id。
代码继承:
1.新建一个名为IAPManager的文件 继承自NSObject , 文件如下:
//
// IAPManager.h
// HistoricalLiterature
//
// Created by sol on 2019/6/18.
// Copyright © 2019 sol. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef enum {
kIAPPurchSuccess = 0, // 购买成功
kIAPPurchFailed = 1, // 购买失败
kIAPPurchCancle = 2, // 取消购买
KIAPPurchVerFailed = 3, // 订单校验失败
KIAPPurchVerSuccess = 4, // 订单校验成功
kIAPPurchNotArrow = 5, // 不允许内购
}IAPPurchType;
typedef void (^IAPCompletionHandle)(IAPPurchType type,NSData *data);
@interface IAPManager : NSObject
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle;
@end
//
// IAPManager.m
// HistoricalLiterature
//
// Created by sol on 2019/6/18.
// Copyright © 2019 sol. All rights reserved.
//
#import "IAPManager.h"
#import <StoreKit/StoreKit.h> //导入支付包
@interface IAPManager () <SKPaymentTransactionObserver,SKProductsRequestDelegate>
@property (nonatomic,strong) NSString *purchID;
@property (nonnull,strong) IAPCompletionHandle handle;
@end
@implementation IAPManager
#pragma mark - system lifecycle
- (instancetype)init{
self = [super init];
if (self) {
// 购买监听写在程序入口,程序挂起时移除监听,这样如果有未完成的订单将会自动执行并回调 paymentQueue:updatedTransactions:方法
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
#pragma mark - Public Method
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle{
if (purchID) {
if ([SKPaymentQueue canMakePayments]) {
// 开始购买服务
self.purchID = purchID;
self.handle = handle;
NSSet *nsset = [NSSet setWithArray:@[purchID]];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}else{
[self handleActionWithType:kIAPPurchNotArrow data:nil];
}
}
}
#pragma mark - Private Method
- (void)handleActionWithType:(IAPPurchType)type data:(NSData *)data{
#if DEBUG
switch (type) {
case kIAPPurchSuccess:
NSLog(@"购买成功");
break;
case kIAPPurchFailed:
NSLog(@"购买失败");
break;
case kIAPPurchCancle:
NSLog(@"用户取消购买");
break;
case KIAPPurchVerFailed:
NSLog(@"订单校验失败");
break;
case KIAPPurchVerSuccess:
NSLog(@"订单校验成功");
break;
case kIAPPurchNotArrow:
NSLog(@"不允许程序内付费");
break;
default:
break;
}
#endif
if(self.handle){
self.handle(type,data);
}
}
// 交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}
// 交易失败
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
[self handleActionWithType:kIAPPurchFailed data:nil];
}else{
[self handleActionWithType:kIAPPurchCancle data:nil];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
//交易验证
NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
if(!receipt){
// 交易凭证为空验证失败
[self handleActionWithType:KIAPPurchVerFailed data:nil];
return;
}
// 购买成功将交易凭证发送给服务端进行再次校验
[self handleActionWithType:kIAPPurchSuccess data:receipt];
NSError *error;
NSDictionary *requestContents = @{
@"receipt-data": [receipt base64EncodedStringWithOptions:0]
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { // 交易凭证为空验证失败
[self handleActionWithType:KIAPPurchVerFailed data:nil];
return;
}
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In the real environment, use https://buy.itunes.apple.com/verifyReceipt
NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
if (flag) {
serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
}
NSURL *storeURL = [NSURL URLWithString:serverString];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
// 无法连接服务器,购买校验失败
[self handleActionWithType:KIAPPurchVerFailed data:nil];
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!jsonResponse) {
// 苹果服务器校验数据返回为空校验失败
[self handleActionWithType:KIAPPurchVerFailed data:nil];
}
// 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
if (status && [status isEqualToString:@"21007"]) {
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
}else if(status && [status isEqualToString:@"0"]){
[self handleActionWithType:KIAPPurchVerSuccess data:nil];
}
#if DEBUG
NSLog(@"----验证结果 %@",jsonResponse);
#endif
}
}];
// 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *product = response.products;
if([product count] <= 0){
#if DEBUG
NSLog(@"--------------没有商品------------------");
#endif
return;
}
SKProduct *p = nil;
for(SKProduct *pro in product){
if([pro.productIdentifier isEqualToString:self.purchID]){
p = pro;
break;
}
}
#if DEBUG
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
NSLog(@"%@",[p description]);
NSLog(@"%@",[p localizedTitle]);
NSLog(@"%@",[p localizedDescription]);
NSLog(@"%@",[p price]);
NSLog(@"%@",[p productIdentifier]);
NSLog(@"发送购买请求");
#endif
SKPayment *payment = [SKPayment paymentWithProduct:p];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
#if DEBUG
NSLog(@"------------------错误-----------------:%@", error);
#endif
}
- (void)requestDidFinish:(SKRequest *)request{
#if DEBUG
NSLog(@"------------反馈信息结束-----------------");
#endif
}
#pragma mark - SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
for (SKPaymentTransaction *tran in transactions) {
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:tran];
break;
case SKPaymentTransactionStatePurchasing:
#if DEBUG
NSLog(@"商品添加进列表");
#endif
break;
case SKPaymentTransactionStateRestored:
#if DEBUG
NSLog(@"已经购买过商品");
#endif
// 消耗型不支持恢复购买
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:tran];
break;
default:
break;
}
}
}
@end
2.使用方法:
注:初始化方法 宏定义
// 懒加载
#define QH_LAZY(object, assignment) (object = object ?: assignment)
///内购
@property (nonatomic,strong) IAPManager *iapManager;
///初始化
-(IAPManager *)iapManager{
return QH_LAZY(_iapManager, ({
IAPManager *iap = [[IAPManager alloc]init];
iap;
}));
}
///使用
//点击购买按钮后,先从后台获取订单号(order_id),然后再发起内购.
//plyID 为 产品ID,之前在创建的时候自定义的
[self.iapManager startPurchWithID:plyID completeHandle:^(IAPPurchType type,NSData *data) {
NSLog(@"data --- %@",data);
if (type == kIAPPurchSuccess) { // 购买成功
if (data) { //返回数据
[FSHUD showWithStatus:@"购买成功,请稍等"];
NSLog(@"//购买成功");
NSString *base64String = [data base64EncodedStringWithOptions:0];
//将请求到的数据与传给服务器 order_id 为订单ID,从后台获取
//[self apple_pay:base64String order_id:order_id];
}
}else if(type == kIAPPurchCancle){
[FSHUD showErrorWithStatus:@"已取消购买"];
}else if(type == kIAPPurchNotArrow){
[FSHUD showErrorWithStatus:@"不允许内购"];
}else if(type == kIAPPurchFailed){
[FSHUD showErrorWithStatus:@"购买失败"];
}
// if (type == KIAPPurchVerSuccess) { //订单第二次校验成功
// NSLog(@"//订单第二次校验成功");
// ///购买凭证
// NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
// NSString *receiptStr = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//
// NSDictionary *dict =[data mj_JSONObject];
// NSLog(@"%@",dict);
// [FSHUD dismiss];
// }
}];
3.开始测试:
点击购买按钮弹出登录页面
输入你添加的测试员账号和密码来完成登录
苹果服务器验证后弹出提示框
点击购买后,会在之前的代码中回调type的类型,判断类型是否为kIAPPurchSuccess(购买成功),然后再做后续的操作
以下为购买成功的提示
内购测试购买商品不扣钱....
五.遇到过的问题
1.在提交审核的时候提示 : 无法存储您的 App 信息。请再试一次。如果问题仍然存在,请联系我们。
方法1:删除最新添加的内购商品重新添加就可以解决了,应该是苹果的bug
方法2:换个网络或者重启路由
方法3:删除了再添加还是提示上面那个问题的话,可以试试这个提交审核"无法存储您的App信息"
注: 我试了,一直加载正在提交APP内购项目....
最后的解决方法是删除掉旧的商品,然后重新添加..