iOS應用如果涉及到支付功能,分為兩類:第三方支付和蘋果內購。那么什么情況下選擇使用第三方支付,又在什么情況下選擇蘋果內購呢?讓我們先來簡單了解一下:
Understanding What You Can Sell Using In-App Purchase
You can use In-App Purchase to sell content, app functionality, and services.
App functionality.Unlock behavior and expand features you’ve already delivered. Examples include a free game that offers multiplayer mode as an in-app purchase and a free weather app that lets users make a one-time purchase to remove ads.
Services.Have users pay for one-time services such as voice transcription and for ongoing services such as access to a collection of data.
You can’t use In-App Purchase to sell real-world goods and services or to sell unsuitable content.
Real-world goods and services.You must deliver a digital good or service within your app when using In-App Purchase. Use a different payment mechanism to let your users buy real-world goods and services in your app, such as a credit card or payment service.
Unsuitable content.Don’t use In-App Purchase to sell content that the isn’t allowed by the App Review Guidelines—for example, pornography, hate speech, or defamation.
開發文檔的這里:Understanding What You Can Sell Using In-App Purchase
開發者中心的這里:https://developer.apple.com/app-store/review/guidelines/#payments
```
購買的內容如果要在 app 內部使用,必須使用 IAP ,但是你要買的東西與 app 本身無關,就不能使用 IAP 。For Example:你用淘寶、京東的 app 買個鼠標,真實物品就不能使用IAP。但是要想在斗魚app內買虛擬物品來送主播禮物,則必須使用 IAP ,不走內購就不行。
```
一、先到iTunes Connect上填寫協議、稅務和銀行業務
如果你是外包公司,那么你可以讓你的客戶填寫這一堆信息;如果你只是是產品公司的技術開發人員,那么你可以讓項目負責人填寫這一堆信息;如果沒有如果,兄弟辛苦了,自己動手來吧。
先點擊Contact Info 的Set Up
進行十二步的時候可能有些銀行通過下面的Look up CNAPS Code方法查不到,就需要借助百度了,一定要準確查詢,否則會有問題。推薦一個地址
https://e.czbank.com/CORPORBANK/query_unionBank_index.jsp
這一步需要注意的是,貨幣類型可能有歧義,看你是想收美元還是人民幣了,都說美元合適。不過,我做的時候為了避免事情,還是選擇了CNY,支持國產。還有一點,銀行賬號如果是對公的賬號,需要填寫公司的英文名稱,如果沒有的話,上拼音!然后點擊保存銀行信息就算ok了,然后退回到最開始的頁面
二、為app添加內購產品
在iTunes Connect在你要添加內購的app中,進入到功能頁面
在你點擊添加內購產品按鈕后會有彈框,提示你選擇類型,這個就要看你app的需求了
填寫完審核信息后,點擊右上角的“存儲”按鈕,就添加了一個內購產品~
三、添加沙盒技術測試員
在iTunes Connect的用戶和智能中選擇“沙盒技術測試員”,填寫信息保存以后就有一個測試員了
四、代碼部分
首先是.h文件
#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
#import "MBProgressHUD.h"
@interface GiftChartViewController :UIViewController<SKPaymentTransactionObserver,SKProductsRequestDelegate>
@end
然后是.m文件
//下面的字符串就是你在iTunes Connect上的內購項目的產品ID,
static const NSString *productCoin60 =@"隨便寫的60";
static const NSString *productCoin300 =@"隨便寫的300";
static const NSString *productCoin600 =@"隨便寫的600";
static const NSString *productCoin980 =@"隨便寫的980";
static const NSString *productCoin1280 =@"隨便寫的1280";
static const NSString *productCoin2580 =@"隨便寫的2580";
typedefNS_ENUM(NSInteger,coinType) {
coinType60,/** 60金幣*/
coinType300,/** 300金幣*/
coinType600,/** 600金幣*/
coinType980,/** 980金幣*/
coinType1280,/** 1280金幣*/
coinType2580/** 2580金幣*/
};
#pragma mark - lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
//支付的點擊事件
- (void)payAction:(id)sender{
NSLog(@"pay pay pay");
NSLog(@"-------------請求對應的產品信息----------------");
if([SKPaymentQueue canMakePayments]) {
[self requestProductData];
}else{
//做一些提示
}
}
- (void)requestProductData{
NSArray *product;
switch(_buyCoinType) {
case:coinType60:
product =@[productCoin60];
break;
case:coinType300:
product =@[productCoin300];
break;
case:coinType600:
product =@[productCoin600];
break;
case:coinType980:
product =@[productCoin980];
break;
case:coinType1280:
product =@[productCoin1280];
break;
case:coinType2580:
product =@[productCoin2580];
break;
default:
break;
}
NSLog(@"請求的產品%@",product);
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsset];
request.delegate =self;
[request start];
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"-----------收到產品反饋信息--------------");
NSArray *myProduct = response.products;
NSLog(@"產品Product ID:%@",response.invalidProductIdentifiers);
NSLog(@"產品付費數量: %d", (int)[myProduct count]);
// populate UI
for(SKProduct *productinmyProduct){
NSLog(@"product info");
NSLog(@"SKProduct描述信息%@", [product description]);
NSLog(@"產品標題%@", product.localizedTitle);
NSLog(@"產品描述信息: %@", product.localizedDescription);
NSLog(@"價格: %@", product.price);
NSLog(@"Product id: %@", product.productIdentifier);
[MBProgressHUD hideHUDForView:self.view animated:YES];
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
//addPayment 將支付信息添加進蘋果的支付隊列后,蘋果會自動完成后續的購買請求,在用戶購買成功或者點擊取消購買的選項后回調
}
//payment = [SKPayment paymentWithProductIdentifier:coin60];這個方法不要使用了
//+ (id)paymentWithProductIdentifier:(NSString*)identifier NS_DEPRECATED_IOS(3_0,5_0,"Use +paymentWithProduct: after fetching the available products using SKProductsRequest");
};
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"------------------錯誤-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request{
NSLog(@"------------反饋信息結束-----------------");
}
//監聽購買結果的回調
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tranintransaction){
switch(tran.transactionState) {
case:SKPaymentTransactionStatePurchased:{
NSLog(@"交易完成");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self verifyTransactionResult];
break;
?}
case:SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加進列表");
break;
case:SKPaymentTransactionStateRestored:
NSLog(@"已經購買過商品");
break;
case:SKPaymentTransactionStateFailed:{
NSLog(@"交易失敗");
[MyTaShowMessageView showMessage:@"交易失敗!"];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
break;
}
default:
break;
}
}
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
購買成功后我們iOS前端可以單獨在客戶端完成訂單正確性的驗證。但是因為有的項目后臺要Android和iOS兩端生成賬單便于對賬。所以我們請求后臺接口,服務器處驗證是否支付成功,依據后臺返回結果做相應邏輯處理。
(PS:訂單正確性的驗證本來可以是:iOS客戶端(購買成功)→ 前端到蘋果服務器驗證→處理蘋果返回結果做相應邏輯處理; 現在:iOS客戶端(購買成功)→ 后臺→后臺到蘋果服務器驗證→處理后臺返回結果做相應邏輯處理)
服務器要做的是:
1.接收iOS前端發過來的購買憑證。
2.判斷憑證是否已經存在或驗證過,然后存儲該憑證。
3.將該憑證發送到對應環境下的蘋果服務器驗證,并將驗證結果返回給客戶端。
4.根據需求,是否修改用戶相應信息。
官方文檔應該也是支持的這么做的→In-App Purchase Programming Guide
- (void)verifyTransactionResult{
//驗證憑據,獲取到蘋果返回的交易憑據
// appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據存放在該地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
//從沙盒中獲取到購買憑據
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
//傳輸的是BASE64編碼的字符串
/**
BASE64常用的編碼方案,通常用于數據傳輸,以及加密算法的基礎算法,傳輸過程中能夠保證數據傳輸的穩定性,BASE64是可以編碼和解碼的。
*/
NSDictionary *requestDict =@{@"receipt-data": [receipt base64EncodedStringWithOptions:0],@"sandbox":@"1"};
/**
請求后臺接口,服務器處驗證是否支付成功,依據返回結果做相應邏輯處理
與后臺協調好,讓后臺根據你的“sandbox”字段的1,0來區分請求是正式環境還是測試環境
(當然“sandbox”這個字段也可以替換為你想要的,但是“receipt-data”不能替換,要注意!)
詳情點這里→蘋果官方文檔
*/
//請求成功的response自己輸出看一下吧,status是0就成功了,這里就不貼出來了,因為有一些敏感數據,比如你的bundleID,product_id之類的
}
下面是兩種環境下的蘋果服務器驗證地址
// Create a POST request with the receipt data.
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In?the?real?environment,?use?https://buy.itunes.apple.com/verifyReceipt
到這里最基本的內購流程就可以跑通了~
五、要注意的事項!
1.bundleID要與iTunes Connect上你App的相同,不然是請求不到產品信息的
2.在沙盒環境進行測試內購的時候,要使用沒有越獄的蘋果手機。
3.在沙盒環境下真機測試內購時,請去app store中注銷你的apple ID,不然發起支付購買請求后會直接case:SKPaymentTransactionStateFailed。使用沙盒測試員的賬號時不需要真正花錢的。
4.如果只添加了一個沙盒測試員賬號,當一個真機已經使用了這個賬號,另一個真機再使用這個賬號支付也是會發生錯誤的。那就去多建幾個沙盒測試員賬號使用不同的,反正也是免費的,填寫也很快。
5.監聽購買結果,當失敗和成功時代碼中要調用:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
該方法通知蘋果支付隊列該交易已完成,不然就會已發起相同 ID 的商品購買就會有此項目將免費恢復的提示。
六、請在本地做一下憑證存儲!
現在訂單正確性的驗證是:iOS客戶端(購買成功)→ 后臺→后臺到蘋果服務器驗證→處理后臺返回結果做相應邏輯處理。
針對圖上的情況:當我們前端購買成功后,憑證本地保留一份,當與后臺驗證成功后,再將本地保留的憑證刪除。否者一直使用本地已經保留的憑證與后臺交互。
七、最后
在第一節 :先到iTunes Connect上填寫協議、稅務和銀行業務中,因為我自己的開發者賬號已經填寫過一遍這個信息了,步驟無法復現。所以在征得簡書作者:睡不著的葉-《iOS開發 內購流程 手把手教你還不學?》的同意后,轉用了部分圖片,在此表示感謝。