iOS內付費的功能對於一個app來說是非常重要的,如果在這一環節出了一些致命的問題,那就很可能會影響app的推廣和公司的利益了。
我在很早之前寫過一篇關於iOS內付費的文章(文章地址),在那篇博客中講述了如何在蘋果後台生成iOS內付費商品,以及在我們的app工程中如何去添加相應的內付費代碼。但是,在後來的日子裡面我發現網友在關於內購功能上遇到的問題比我想象的還要多,其中很大一部分都是一些很簡單的問題,比如說簽名使用的不正確,內購商品ID不正確,bundleID沒有和簽名一致,蘋果賬號沒有補充完整銀行稅務信息等導致的,只要簡單的修改一下就能解決以上問題。
另外,很多app項目創建的內購商品都是以“消耗品“ ,這種類型的商品為主,不過有的網友也遇到了其他的問題,他在app中創建了幾個非消耗品的內購商品,然後提交蘋果審核的時候,卻被蘋果打回了,原因是蘋果要求開發者對這種非消耗品的商品增加一個“恢復“的按鈕。
Apple 打回的原文如下:
Business - 3.1.1 We found that your app offers In-App Purchase(s) that can be restored but does not include a "Restore Purchases" feature to allow users to restore the previously purchased In-App Purchase(s), as specified in the "Restoring Purchase Products" section of the In-App Purchase Programming Guide "Users restore transactions to maintain access to content they've already purchased. For example, when they upgrade to a new phone, they don't lose all of the items they purchased on the old phone. Include some mechanism in your app to let the user restore their purchases, such as a Restore Purchases button." To restore previously purchased In-App Purchase products, it would be appropriate to provide a "Restore" button and initiate the restore process when the "Restore" button is tapped by the user. Before You Submit
今天,我在這篇文章中就來針對上述的蘋果打回的問題做出解決方案。
“非消耗品的購買和恢復” 該如何操作
創建非消耗品(non-consumable)
在 iTunesconnect 後台中創建一個非消耗品,根據內購商品所需要的內容編輯完整。創建的選項如下圖所示:
內購流程梳理
非消耗品第一次購買的邏輯和消耗品是一樣,我們先來理一遍內購的流程,首先在代碼中我們一定要在內購初始化的地方加上這句代碼:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
不然購買成功和失敗的任何回調你都收不到。在初始化之後,要去蘋果服務器那獲取我們所要提供給用戶的詳細商品信息,代碼邏輯如下:
- (void) requestProductData{ if(!self.mRequestProductDataObjects || self.mRequestProductDataObjects.count == 0){ return; } if ([SKPaymentQueue canMakePayments]) { NSArray *product = nil; product = [NSArray arrayWithArray:_mRequestProductDataObjects]; NSSet *nsset = [NSSet setWithArray:product]; SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset]; request.delegate = self; [request start]; } ... }
在保證商品id正確的情況下,我們的程序會把獲取到的商品信息回調到此函數中:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response NS_AVAILABLE_IOS(3_0);
解析商品信息的邏輯,我這邊就不細說了,要看詳細的代碼,就去我這篇文章中 iOS應用程序內購/內付費看吧!
在獲取到商品信息之後,我們就可以開始進行購買了,購買的邏輯函數如下:
- (void) purchase:(NSString*) productId { if(productId.length == 0 || !productId){ return; } SKProduct * _payProduct =nil; if ([SKPaymentQueue canMakePayments]) { for (SKProduct * payment in self.purchasableObjects) { if ([productId isEqualToString:payment.productIdentifier] ) { _payProduct = payment; break; } } } if(!_payProduct){ return; } SKMutablePayment * payment = [SKMutablePayment paymentWithProduct:_payProduct]; [[SKPaymentQueue defaultQueue] addPayment:payment]; }
上述函數的內部邏輯走完以後,就會回調此方法了:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions){ switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; NSLog(@"支付成功"); break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; NSLog(@"支付失敗"); break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; NSLog(@"交易恢復處理"); default: break; } } }
在這個回調函數的內部邏輯裡面,分別有對購買成功,購買失敗,恢復交易的處理。好了,經過一遍簡單的梳理以後,想必你已經在心中有了一個大概,接下來就是我們的重頭戲了。
恢復按鈕怎麼添加?
正常情況下,非消耗品的內購商品在購買成功後,應該把 “購買” 字樣變成 “恢復” 字樣,當用戶完成一次購買以後,就可以永久使用啦!那在什麼時機去修改這個按鈕呢。
當非消耗品完成一次購買以後,再次去點擊購買的話,系統會提示你已經購買了此項目,如圖:
為了確保我們的app在每次運行起來後,內購商品列表能正確顯示,哪些非消耗商品是已經購買過的,我們得在內購功能初始化的地方(就是添加監聽函數下面)調用該函數,來獲取哪些商品是已經買過的:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
該函數調用後會恢復之前的交易,程序會直接進入到函數updatedTransactions的回調:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { case SKPaymentTransactionStateRestored: ..... }
然後再走到此回調函數:
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
我們可以給上面函數中添加如下邏輯,用一個NSMutableArray來存儲蘋果回調過來給我們已經購買過的非消耗品的商品信息:
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { purchasedItemIDs = [[NSMutableArray alloc] init]; NSLog(@"received restored transactions: %i", queue.transactions.count); for (SKPaymentTransaction *transaction in queue.transactions) { NSString *productID = transaction.payment.productIdentifier; [purchasedItemIDs addObject:productID]; NSLog(@"%@",purchasedItemIDs); } }
在這裡我們已經獲取到了所有已經購買過的非消耗品的 transactions,那最後一步就是回調app界面去修改按鈕名稱咯。那有些人會問,如果我換了設備怎麼辦,其實也很簡單,iOS內購商品是跟著你的Apple ID走的,你換到新設備後登錄你的Apple ID帳號,啟動app就會走到restoreCompletedTransactions函數,蘋果還是會給你推送過來你購買過的非消耗品信息,然後再去修改app按鈕是顯示 購買 還是 恢復 。
最後關於在app內購買非消耗品 ,道具應該怎麼下發,我做一下自己的理解說明吧!非消耗品不管是第一次購買,還是恢復購買,蘋果驗證票據返回過來的信息都是一致的,如圖所示:
所以,我們在第一次購買非消耗品的時候,待蘋果服務器票據驗證成功後,服務器要將玩家的賬號信息(非Apple ID賬號,再說你app也獲取不到)和 該商品ID保存在數據庫中;玩家若換設備或者卸載了app以後,再次想要獲取該道具,我們就可以直接根據玩家的賬號信息和商品ID去數據庫中查找,若已經購買過了,就可以直接下發道具。
總結
關於iOS內購的功能其實代碼就那麼些,但是如果你真的要做到內購功能很健壯,例如購買東西時不丟單,游戲充值到賬等,確實要花很多精力去研究,這不單單是客戶端的任務,還包括服務器如何保存票據,丟單的時候如何去補單這些操作。好了,寫了這麼些,不早了也該歇歇了,若有什麼問題的話,請加以下公眾號聯系我。