一.設置協議
1.打開App Store Connect,選擇 協議,稅務和銀行業務
協議,稅務和銀行業務.png
2.因為我已經添加過信息了,所有展示內容是這樣的
協議.png
3.如果想知道要怎么設置相關信息的話 請參考
iOS內購(IAP,In App Purchases-在APP內部支付),設置及使用
4.這是在協議、稅務和銀行業務頁面頁面添加銀行賬戶時遇到的問題
4-1. 國內銀行CNAPS CODE 查詢
4-2. 在提交信息的時候提示 : 賬戶持有人姓名無效. 持卡人姓名,中文名用拼寫,名在前,姓在后 例:Xiaoer Wang
二.添加內購商品
1.打開App Store Connect,- 我的APP,然后再點擊你要添加內購的APP - 功能 - App內購項目
打開頁面如下圖,已填寫好完整數據的商品,狀態為 (準備提交),未準備好的商品 狀態為 (元數據丟失)
我的APP.png
2. 點擊 (+) 開始創建App內購項目
根據需求來創建內購項目,如果不知道該創建什么類型的話,點擊左下角 進一步了解App內購項
創建APP內購項目.png
3.填寫商品信息
填寫結束后點擊右上角的存儲
上.png
下.png
4.在準備提交的版本中添加APP內購項目
選擇完成后點擊右上角的存儲 準備提交審核
添加APP內購項目.png
三.添加測試賬號來進行測試
1.打開App Store Connect, 點擊 用戶和訪問
打開頁面如下,點擊 (+) 進行創建測試用戶
用戶和訪問.png
2.添加測試員
新測試員.png
四.代碼集成
注意:
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(@"http://購買成功");
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(@"http://訂單第二次校驗成功");
// ///購買憑證
// 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.開始測試:
點擊購買按鈕彈出登錄頁面
輸入你添加的測試員賬號和密碼來完成登錄
登錄.PNG
蘋果服務器驗證后彈出提示框
50.PNG
點擊購買后,會在之前的代碼中回調type的類型,判斷類型是否為kIAPPurchSuccess(購買成功),然后再做后續的操作
以下為購買成功的提示
內購測試購買商品不扣錢....
購買成功.PNG
五.遇到過的問題
1.在提交審核的時候提示 : 無法存儲您的 App 信息。請再試一次。如果問題仍然存在,請聯系我們。
方法1:刪除最新添加的內購商品重新添加就可以解決了,應該是蘋果的bug
方法2:換個網絡或者重啟路由
方法3:刪除了再添加還是提示上面那個問題的話,可以試試這個提交審核"無法存儲您的App信息"
注: 我試了,一直加載正在提交APP內購項目....
最后的解決方法是刪除掉舊的商品,然后重新添加..