應用內支付:
大致的業務邏輯是這樣的。
1.向服務器請求商品訂單號碼以及ituns配置的商品ID
2.發起IAP購買請求
3.購買流程結束后, 向服務器發起驗證憑證以及支付結果的請求
4.驗證流程結束完成購買流程。
流程
- 首先打開itunesconnect看一下有沒有配置用戶賬戶等信息, 點擊 『協議, 稅務與銀行卡業務』進去配置就可以。
- 創建App或者選擇已經有的App, 點擊進入詳情之后, 點擊App內購項目這一選項
屏幕快照 2015-09-24 上午11.53.06.png
進入, 點擊右上角的CreateNew按鈕進行創建。 如果已經有的話可以點擊進詳情編輯內容。
消耗型項目
對於消耗型 App 內購買項目,用戶每次下載時都必須進行購買。一次性服務通常屬於消耗型項目,例如釣魚 App 中的魚餌。
非消耗型項目
對於非消耗型 App 內購買項目,用戶僅需要購買一次。不會過期或隨使用而減少的服務通常為非消耗型項目,例如游戲 App 的新跑道。
自動續訂訂閱
通過自動續訂訂閱,用戶可以購買指定時間期限內的更新和動態內容。除非用戶取消選擇,否則訂閱(例如雜志訂閱等)會自動續訂。
免費訂閱
免費訂閱是開發人員在“報刊雜志”中推廣其內容的絕佳方式。用戶注冊免費訂閱后,此訂閱內容在與該用戶 Apple ID 相關聯的所有設備上可用。免費訂閱不會過期,並且僅能在位於“報刊雜志”類別中的 App 中提供。
非續訂訂閱
非續訂訂閱允許有時限性的營銷服務。對於 App 內購買項目中的限時訪問內容,就需使用非續訂訂閱。例如,導航 App 中語音導航功能的一周訂閱,或者年度訂閱已存檔的視頻或音頻的在線目錄。
通常我們都選擇消耗形項目, 如果你要按月付費之類的就要選擇非續訂訂閱之類的嘍。
選擇消耗形項目, 然后繼續,輸入商品的名稱, 產品的ID(自定義), 在下邊添加語言的地方添加一下商品的描述信息, 然后上傳一張商品界面的截圖(這里可以隨便, 影響不是很大)保存就可以了。

這是我們創建的結果, 右邊的准備好去提交的提示是用於你提交App審核的時候同時提交一下內購項目的審核, 在應用程序的附加信息里邊。必須要提交- - 。 這里的產品ID就是后期用於請求商品的商品ID。這時候就可以開始測試了。
代碼
-
導入 StoreKit.Framework 這個框架
並在VC中#import <StoreKit/StoreKit.h>
-
實現
SKPaymentTransactionObserver, SKProductsRequestDelegate
這兩個代理
3.在ViewDidLoad中添加購買監聽
// 添加購買監聽 [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
- 開始模擬購買, 要先檢測是否允許內購。
// 檢測是否允許內購 if([SKPaymentQueue canMakePayments]){ [self requestProductData:productID]; }else{ NSLog(@"不允許程序內付費"); }
//請求商品 - (void)requestProductData:(NSString *)type{ NSLog(@"請求商品"); [SVProgressHUD showWithStatus:@"正在請求商品信息" maskType:SVProgressHUDMaskTypeGradient]; NSArray *product = [[NSArray alloc] initWithObjects:type, nil]; NSSet *nsset = [NSSet setWithArray:product]; // 請求動作 SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset]; request.delegate = self; [request start]; }
//收到產品返回信息 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ NSLog(@"收到了請求反饋"); NSArray *product = response.products; if([product count] == 0){ NSLog(@"沒有這個商品"); return; } NSLog(@"productID:%@", response.invalidProductIdentifiers); NSLog(@"產品付費數量:%ld",[product count]); SKProduct *p = nil; // 所有的商品, 遍歷招到我們的商品 for (SKProduct *pro in product) { NSLog(@"%@", [pro description]); NSLog(@"%@", [pro localizedTitle]); NSLog(@"%@", [pro localizedDescription]); NSLog(@"%@", [pro price]); NSLog(@"%@", [pro productIdentifier]); if([pro.productIdentifier isEqualToString:productID]) { p = pro; } } SKPayment * payment = [SKPayment paymentWithProduct:p]; NSLog(@"發送購買請求"); [SVProgressHUD showWithStatus:@"正在發送購買請求" maskType:SVProgressHUDMaskTypeGradient]; [[SKPaymentQueue defaultQueue] addPayment:payment]; }
//請求失敗 - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { NSLog(@"商品信息請求錯誤:%@", error); [SVProgressHUD showErrorWithStatus:[error localizedDescription]]; } - (void)requestDidFinish:(SKRequest *)request { NSLog(@"請求結束"); [SVProgressHUD dismiss]; }
//監聽購買結果 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction { for(SKPaymentTransaction *tran in transaction){ switch (tran.transactionState) { case SKPaymentTransactionStatePurchased: NSLog(@"交易完成"); [SVProgressHUD showSuccessWithStatus:@"交易完成"]; break; case SKPaymentTransactionStatePurchasing: NSLog(@"商品添加進列表"); [SVProgressHUD showWithStatus:@"正在請求付費信息" maskType:SVProgressHUDMaskTypeGradient]; break; case SKPaymentTransactionStateRestored: NSLog(@"已經購買過商品"); [SVProgressHUD showErrorWithStatus:@"已經購買過商品"]; break; case SKPaymentTransactionStateFailed: NSLog(@"交易失敗"); [SVProgressHUD showErrorWithStatus:@"交易失敗, 請重試"]; break; default: [SVProgressHUD dismiss]; break; } } }
在交易結束的時候, 要向服務器做憑證的驗證, 因為要鏈接蘋果的服務器, 所以這里的網絡請求可能稍慢一點。 所以建議做一下本地化。
//交易結束 - (void)completeTransaction:(SKPaymentTransaction *)transaction{ NSLog(@"交易結束"); [SVProgressHUD dismiss]; NSString * productIdentifier = [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding]; NSString * receipt = [[productIdentifier dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString]; if ([productIdentifier length] > 0) { // 向自己的服務器驗證購買憑證 // https://sandbox.itunes.apple.com/verifyReceipt // receipt-data } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; }
最后在dealloc中移除監聽
- (void)dealloc{ // 移除監聽 [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; }
自己了解一下流程很重要, 順便附上服務器端的驗證代碼
<?php //服務器二次驗證代碼 function getReceiptData($receipt, $isSandbox = false) { if ($isSandbox) { $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt'; } else { $endpoint = 'https://buy.itunes.apple.com/verifyReceipt'; } $postData = json_encode( array('receipt-data' => $receipt) ); $ch = curl_init($endpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0); //這兩行一定要加,不加會報SSL 錯誤 curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); $response = curl_exec($ch); $errno = curl_errno($ch); $errmsg = curl_error($ch); curl_close($ch); //判斷時候出錯,拋出異常 if ($errno != 0) { throw new Exception($errmsg, $errno); } $data = json_decode($response); //判斷返回的數據是否是對象 if (!is_object($data)) { throw new Exception('Invalid response data'); } //判斷購買時候成功 if (!isset($data->status) || $data->status != 0) { throw new Exception('Invalid receipt'); } //返回產品的信息 return array( 'quantity' => $data->receipt->quantity, 'product_id' => $data->receipt->product_id, 'transaction_id' => $data->receipt->transaction_id, 'purchase_date' => $data->receipt->purchase_date, 'app_item_id' => $data->receipt->app_item_id, 'bid' => $data->receipt->bid, 'bvrs' => $data->receipt->bvrs ); } //獲取 App 發送過來的數據,設置時候是沙盒狀態 $receipt = $_GET['data']; $isSandbox = true; //開始執行驗證 try { $info = getReceiptData($receipt, $isSandbox); // 通過product_id 來判斷是下載哪個資源 switch($info['product_id']){ case 'com.application.xxxxx.xxxx': Header("Location:xxxx.zip"); break; } } //捕獲異常 catch(Exception $e) { echo 'Message: ' .$e->getMessage(); } ?>
在我們公司的測試服務器中,我們會連接蘋果的測試服務器https://sandbox.itunes.apple.com/verifyReceipt
驗證。
在我們部署在線上的正式服務器中,我們會連接蘋果的正式服務器https://buy.itunes.apple.com/verifyReceipt
驗證。
我們提交給蘋果審核的是正式版,我們以為蘋果審核時,我們應該連接蘋果的線上驗證服務器來驗證購買憑證。結果我理解錯了,蘋果在審核App時,只會在sandbox環境購買,其產生的購買憑證,也只能連接蘋果的測試驗證服務器。但是審核的app又是連接的我們的線上服務器。所以我們這邊的服務器無法驗證通過IAP購買,造成我們app的又一次審核被拒。
解決方法是判斷蘋果正式驗證服務器的返回code,如果是21007,則再一次連接測試服務器進行驗證即可。蘋果的這一篇文檔上有對返回的code的詳細說明 (引自 唐巧, 上邊有文章地址).
原文鏈接:http://www.jianshu.com/p/e9ae4cece800
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。