成熟的小朋友要學(xué)會(huì)自己看文檔
置于為什么示例代碼是swift,因?yàn)楣俜轿臋n沒(méi)有給OC版本的,我也懶得改。
更新-后續(xù)的內(nèi)容有OC的代碼示例了。
StoreKit
支持應(yīng)用程序內(nèi)購(gòu)買以及與App Store的交互。
總覽
在應(yīng)用程序中使用StoreKit,可以提供以下功能和服務(wù):
- In-App Purchase。 提供和推廣應(yīng)用內(nèi)購(gòu)買的內(nèi)容和服務(wù)。
- Apple Music。 檢查用戶的Apple Music功能并提供訂閱。
- Recommendations and reviews。 提供有關(guān)第三方內(nèi)容的建議,并使用戶能夠?qū)δ膽?yīng)用進(jìn)行評(píng)分和審查。
這里主要看內(nèi)購(gòu)。
In-App Purchase
通過(guò)在您的應(yīng)用內(nèi)購(gòu)買商品,為用戶提供其他內(nèi)容和服務(wù)。
應(yīng)用內(nèi)購(gòu)買可讓您為用戶提供購(gòu)買應(yīng)用內(nèi)內(nèi)容和功能的機(jī)會(huì)。 客戶可以在您的應(yīng)用程序中進(jìn)行購(gòu)買,也可以直接從App Store中進(jìn)行購(gòu)買。 有關(guān)在App Store中推廣產(chǎn)品的信息,請(qǐng)參閱Promoting Your In-App Purchases。
StoreKit框架代表您的應(yīng)用程序連接到App Store,以提示并安全地處理付款。 然后,框架通知您的應(yīng)用程序,該應(yīng)用程序?qū)⒔桓顿?gòu)買的產(chǎn)品。 要驗(yàn)證購(gòu)買,您可以通過(guò)App Store或設(shè)備在服務(wù)器上驗(yàn)證收據(jù)。 對(duì)于自動(dòng)續(xù)訂訂閱,App Store還可將關(guān)鍵訂閱事件通知您的服務(wù)器。

在App Store Connect中配置應(yīng)用內(nèi)購(gòu)買
要使用應(yīng)用內(nèi)購(gòu)買,必須首先在App Store Connect中配置產(chǎn)品。 在開(kāi)發(fā)應(yīng)用程序時(shí),您可以添加或刪除產(chǎn)品以及優(yōu)化或重新配置現(xiàn)有產(chǎn)品。 有關(guān)更多信息,請(qǐng)參閱Workflow for configuring in-app purchases。
您還可以提供在多個(gè)平臺(tái)上運(yùn)行的應(yīng)用程序和應(yīng)用程序內(nèi)購(gòu)買作為一次購(gòu)買。 有關(guān)通用購(gòu)買的更多信息,請(qǐng)參閱App Store Connect Help。
了解產(chǎn)品類型
您可提供四種類型的 App 內(nèi)購(gòu)買項(xiàng)目:
- 消耗型項(xiàng)目是一種使用一次之后即失效的項(xiàng)目。用戶可以多次購(gòu)買這類項(xiàng)目。
- 非消耗型項(xiàng)目是一種用戶只需購(gòu)買一次的項(xiàng)目。這類項(xiàng)目不會(huì)過(guò)期。
- 服務(wù)或內(nèi)容的自動(dòng)續(xù)期訂閱是一種用戶購(gòu)買一次之后,只要用戶不選擇取消,就會(huì)一直自動(dòng)續(xù)期的項(xiàng)目。
- 服務(wù)或內(nèi)容的非續(xù)期訂閱有特定訪問(wèn)時(shí)限,不會(huì)自動(dòng)續(xù)期。用戶可以再次購(gòu)買這類項(xiàng)目。
您可以使用 StoreKit 跨設(shè)備同步和恢復(fù)非消耗型項(xiàng)目和自動(dòng)續(xù)期訂閱。當(dāng)用戶購(gòu)買自動(dòng)續(xù)期訂閱或非續(xù)期訂閱時(shí),您的 app 應(yīng)當(dāng)讓用戶能夠在所有設(shè)備上訪問(wèn)這一訂閱,并讓用戶能夠恢復(fù)以前購(gòu)買的項(xiàng)目。
官方API實(shí)例
為付款隊(duì)列設(shè)置交易觀察對(duì)象
通過(guò)添加觀察者,使您的應(yīng)用能夠接收和處理交易。
總覽
要在您的應(yīng)用中處理交易,您必須創(chuàng)建一個(gè)觀察者并將其添加到付款隊(duì)列中。 觀察者對(duì)象響應(yīng)新交易并將待處理交易隊(duì)列與App Store同步,并且支付隊(duì)列提示用戶授權(quán)支付。 您的應(yīng)用應(yīng)在應(yīng)用啟動(dòng)時(shí)添加交易觀察者,以確保您的應(yīng)用將盡快收到付款隊(duì)列通知。
創(chuàng)建觀察者
創(chuàng)建并構(gòu)建自定義觀察者類以處理對(duì)支付隊(duì)列的更改:
class StoreObserver: NSObject, SKPaymentTransactionObserver {
....
//Initialize the store observer.
override init() {
super.init()
//Other initialization here.
}
//Observe transaction updates.
func paymentQueue(_ queue: SKPaymentQueue,updatedTransactions transactions: [SKPaymentTransaction]) {
//Handle transaction states here.
}
....
}
創(chuàng)建此觀察者類的實(shí)例以充當(dāng)付款隊(duì)列更改的觀察者:
let iapObserver = StoreObserver()
提示
考慮將您的觀察者創(chuàng)建為該類的共享實(shí)例,以便在任何其他類中進(jìn)行全局引用。 共享實(shí)例還可以保證對(duì)象的生存期,從而確保通過(guò)SKPaymentTransactionObserver協(xié)議進(jìn)行的回調(diào)始終由同一實(shí)例處理。
創(chuàng)建交易觀察者后,您可以將其添加到付款隊(duì)列中。
總結(jié)一下就是:自定義的實(shí)現(xiàn)了SKPaymentTransactionObserver協(xié)議的觀察對(duì)象
添加觀察者
當(dāng)您的應(yīng)用調(diào)用時(shí),StoreKit會(huì)將您的觀察者附加到隊(duì)列中:
SKPaymentQueue.default().add(iapObserver)
當(dāng)付款隊(duì)列的內(nèi)容在恢復(fù)或運(yùn)行應(yīng)用程序時(shí)發(fā)生更改時(shí),StoreKit可以自動(dòng)通知您的SKPaymentTransactionObserver實(shí)例。
實(shí)現(xiàn)交易觀察器:
import UIKit
import StoreKit
class AppDelegate: UIResponder, UIApplicationDelegate {
....
// Attach an observer to the payment queue.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
SKPaymentQueue.default().add(iapObserver)
return true
}
// Called when the application is about to terminate.
func applicationWillTerminate(_ application: UIApplication) {
// Remove the observer.
SKPaymentQueue.default().remove(iapObserver)
}
....
}
重要的是在啟動(dòng)時(shí)在application:didFinishLaunchingWithOptions:中添加觀察者,以確保觀察者在您的應(yīng)用程序所有啟動(dòng)期間持續(xù)存在,接收所有付款隊(duì)列通知并繼續(xù)進(jìn)行可能在應(yīng)用程序外部處理的交易,例如:
- 推薦的應(yīng)用內(nèi)購(gòu)買
- 后臺(tái)訂閱續(xù)訂
- 采購(gòu)中斷
觀察者必須是持久的,因此當(dāng)應(yīng)用程序發(fā)送到后臺(tái)時(shí),它不會(huì)被釋放。 只有持久的觀察者才能接收到您的應(yīng)用程序在后臺(tái)運(yùn)行時(shí)可能發(fā)生的交易,例如自動(dòng)續(xù)訂的續(xù)訂交易。
總結(jié)一下:在App啟動(dòng)的時(shí)候,把自定義的觀察對(duì)象放進(jìn)SKPaymentQueue隊(duì)列中,然后后續(xù)一系列的任務(wù)等待監(jiān)聽(tīng)回調(diào)交給觀察對(duì)象的協(xié)議方法處理。
提供和完成 App 內(nèi)購(gòu)買以及恢復(fù) App 內(nèi)購(gòu)買項(xiàng)目
在您的應(yīng)用中獲取,完成和還原交易。
總覽
應(yīng)用程序內(nèi)購(gòu)買允許用戶在您的應(yīng)用程序內(nèi)或使用StoreKit框架直接從App Store購(gòu)買虛擬商品。此示例代碼演示了如何檢索,顯示和還原應(yīng)用內(nèi)購(gòu)買。首先,您設(shè)置您的應(yīng)用程序以在啟動(dòng)時(shí)注冊(cè)并使用單事務(wù)隊(duì)列觀察器。交易隊(duì)列觀察者管理所有支付交易并處理所有交易狀態(tài)。確認(rèn)這是符合SKPaymentTransactionObserver協(xié)議的自定義類的共享實(shí)例。然后,在系統(tǒng)即將終止應(yīng)用程序時(shí)刪除事務(wù)觀察器。有關(guān)更多信息,請(qǐng)參見(jiàn)Setting Up the Transaction Observer for the Payment Queue。
此示例構(gòu)建了InAppPurchases應(yīng)用程序,支持iOS,macOS和tvOS平臺(tái)。啟動(dòng)后,InAppPurchases會(huì)在App Store中查詢Products.plist文件中保存的產(chǎn)品標(biāo)識(shí)符。 InAppPurchases會(huì)使用App Store的響應(yīng)來(lái)更新其UI,其中可能包括待售產(chǎn)品,無(wú)法識(shí)別的產(chǎn)品標(biāo)識(shí)符或兩者。該應(yīng)用程序還在其UI中顯示所有可用的已購(gòu)買和已恢復(fù)的付款交易。
配置示例代碼項(xiàng)目
在運(yùn)行和測(cè)試此示例之前,您需要:
- 從一個(gè)完整的應(yīng)用程序開(kāi)始,該應(yīng)用程序支持應(yīng)用程序內(nèi)購(gòu)買并在App Store Connect中配置了一些應(yīng)用程序內(nèi)購(gòu)買。有關(guān)更多信息,請(qǐng)參見(jiàn)第1步和第2步。
- 在App Store Connect中創(chuàng)建沙盒測(cè)試用戶帳戶。
- 在
Xcode中打開(kāi)此示例,選擇要構(gòu)建的目標(biāo),然后將其捆綁ID更改為支持應(yīng)用內(nèi)購(gòu)買的捆綁ID。接下來(lái),選擇合適的團(tuán)隊(duì),讓Xcode自動(dòng)管理您的配置文件。 - 打開(kāi)示例中的ProductIds.plist文件,并使用您現(xiàn)有的應(yīng)用內(nèi)購(gòu)買商品ID更新其內(nèi)容。
- 對(duì)于
iOS和tvOS設(shè)備,分別構(gòu)建和運(yùn)行InAppPurchases和InAppPurchasestvOS目標(biāo),該示例用于構(gòu)建InAppPurchases。 - 對(duì)于
macOS,在構(gòu)建InAppPurchasesmacOS目標(biāo)之前,請(qǐng)退出Mac App Store。構(gòu)建目標(biāo),然后首次從Finder啟動(dòng)生成的應(yīng)用以獲取收據(jù)。有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn)Testing In-App Purchases with Sandbox。 -
InAppPurchases應(yīng)用程序在啟動(dòng)時(shí)向App Store查詢ProductIds.plist中包含的產(chǎn)品標(biāo)識(shí)符。成功后,它將顯示可在App Store中出售的產(chǎn)品列表。點(diǎn)按列表中的任何產(chǎn)品以進(jìn)行購(gòu)買。當(dāng)系統(tǒng)提示您進(jìn)行購(gòu)買驗(yàn)證時(shí),請(qǐng)使用在步驟2中創(chuàng)建的測(cè)試用戶帳戶。當(dāng)產(chǎn)品請(qǐng)求失敗時(shí),出于各種原因,請(qǐng)參閱 invalidProductIdentifiers的討論,原因是App Store可能會(huì)返回?zé)o效的產(chǎn)品標(biāo)識(shí)符。
顯示可用本地化定價(jià)出售的產(chǎn)品
該示例配置了應(yīng)用程序,以便在展示要出售的產(chǎn)品之前確認(rèn)用戶已被授權(quán)在設(shè)備上付款。
var isAuthorizedForPayments: Bool {
return SKPaymentQueue.canMakePayments()
}
應(yīng)用確認(rèn)授權(quán)后,就會(huì)向App Store發(fā)送產(chǎn)品請(qǐng)求,以從App Store獲取本地化的產(chǎn)品信息。 查詢App Store可確保該App僅向用戶顯示可供購(gòu)買的產(chǎn)品。 該應(yīng)用程序使用與其希望在其UI中出售的產(chǎn)品相關(guān)聯(lián)的產(chǎn)品標(biāo)識(shí)符列表來(lái)初始化產(chǎn)品請(qǐng)求。 確保對(duì)產(chǎn)品請(qǐng)求對(duì)象使用強(qiáng)引用持有,系統(tǒng)可能會(huì)在請(qǐng)求完成之前將其釋放。
fileprivate func fetchProducts(matchingIdentifiers identifiers: [String]) {
// Create a set for the product identifiers.
let productIdentifiers = Set(identifiers)
// Initialize the product request with the above identifiers.
productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productRequest.delegate = self
// Send the request to the App Store.
productRequest.start()
}
App Store使用 SKProductsResponse對(duì)象響應(yīng)產(chǎn)品請(qǐng)求。 其產(chǎn)品屬性包含有關(guān)可在App Store中實(shí)際購(gòu)買的所有產(chǎn)品的信息。 該應(yīng)用程序使用此屬性來(lái)更新其UI。 響應(yīng)的invalidProductIdentifiers屬性包含App Store無(wú)法識(shí)別的所有產(chǎn)品標(biāo)識(shí)符。 出于各種原因,請(qǐng)參閱invalidProductIdentifiers的討論,為什么App Store可能會(huì)返回?zé)o效的產(chǎn)品標(biāo)識(shí)符。
// products contains products whose identifiers have been recognized by the App Store. As such, they can be purchased.
if !response.products.isEmpty {
availableProducts = response.products
}
// invalidProductIdentifiers contains all product identifiers not recognized by the App Store.
if !response.invalidProductIdentifiers.isEmpty {
invalidProductIdentifiers = response.invalidProductIdentifiers
}
要在用戶界面中顯示產(chǎn)品的價(jià)格,請(qǐng)使用App Store返回的語(yǔ)言環(huán)境和貨幣。 例如,考慮一個(gè)登錄法國(guó)應(yīng)用程序商店并且其設(shè)備使用美國(guó)語(yǔ)言環(huán)境的用戶。 嘗試購(gòu)買產(chǎn)品時(shí),App Store會(huì)以歐元顯示產(chǎn)品價(jià)格。 因此,轉(zhuǎn)換和顯示以美元表示的產(chǎn)品價(jià)格以使其與用戶界面中的設(shè)備區(qū)域設(shè)置相匹配是不正確的。
extension SKProduct {
/// - returns: The cost of the product formatted in the local currency.
var regularPrice: String? {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = self.priceLocale
return formatter.string(from: self.price)
}
}
與應(yīng)用互動(dòng)以購(gòu)買產(chǎn)品
在用戶界面中點(diǎn)擊任何可出售的產(chǎn)品進(jìn)行購(gòu)買。 該應(yīng)用程序允許用戶還原非消耗性產(chǎn)品和自動(dòng)續(xù)訂的訂閱。 使用還原按鈕和設(shè)置>還原所有可還原的購(gòu)買分別在iOS和tvOS版本的應(yīng)用程序中實(shí)現(xiàn)此功能。 選擇商店>恢復(fù)菜單項(xiàng)以在應(yīng)用的macOS版本中恢復(fù)購(gòu)買。 點(diǎn)擊任何購(gòu)買的商品都會(huì)顯示購(gòu)買信息,例如產(chǎn)品標(biāo)識(shí)符,交易標(biāo)識(shí)符和交易日期。 當(dāng)購(gòu)買的商品是托管產(chǎn)品時(shí),購(gòu)買信息還包括其內(nèi)容標(biāo)識(shí)符,內(nèi)容版本和內(nèi)容長(zhǎng)度。 當(dāng)購(gòu)買恢復(fù)后,購(gòu)買信息還將包含其原始交易的標(biāo)識(shí)符和日期。
處理付款交易狀態(tài)
當(dāng)付款隊(duì)列中有待處理的交易時(shí),StoreKit會(huì)通過(guò)調(diào)用應(yīng)用的 paymentQueue:updatedTransactions:方法來(lái)通知應(yīng)用的交易觀察者。 每個(gè)交易都有五個(gè)可能的狀態(tài),包括SKPaymentTransactionStatePurchasing,SKPaymentTransactionStatePurchased,SKPaymentTransactionStateFailed,SKPaymentTransactionStateRestored和SKPaymentTransactionStateDeferred; 有關(guān)更多信息,請(qǐng)參見(jiàn)SKPaymentTransactionState 應(yīng)用程序應(yīng)確保其觀察者的 paymentQueue:updatedTransactions:可以隨時(shí)響應(yīng)任何一種狀態(tài)。 提供Apple托管的產(chǎn)品時(shí),他們應(yīng)該在其觀察者上實(shí)現(xiàn) paymentQueue:updatedDownloads:`方法。
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchasing: break
// Do not block the UI. Allow the user to continue using the app.
case .deferred: print(Messages.deferred)
// The purchase was successful.
case .purchased: handlePurchased(transaction)
// The transaction failed.
case .failed: handleFailed(transaction)
// There're restored products.
case .restored: handleRestored(transaction)
@unknown default: fatalError(Messages.unknownPaymentTransaction)
}
}
}
當(dāng)事務(wù)失敗時(shí),檢查其錯(cuò)誤屬性以確定發(fā)生了什么。 僅顯示代碼與paymentCancelled不同的錯(cuò)誤。
// Do not send any notifications when the user cancels the purchase.
if (transaction.error as? SKError)?.code != .paymentCancelled {
DispatchQueue.main.async {
self.delegate?.storeObserverDidReceiveMessage(message)
}
}
當(dāng)用戶推遲交易時(shí),應(yīng)用應(yīng)允許他們?cè)诘却齋toreKit更新交易時(shí)繼續(xù)使用UI。
恢復(fù)已完成的購(gòu)買
當(dāng)用戶購(gòu)買非消耗品,自動(dòng)更新訂閱或非更新訂閱時(shí),他們希望它們可以在所有設(shè)備上無(wú)限期可用。 有關(guān)更多信息,請(qǐng)參見(jiàn)In-App Purchase。 該應(yīng)用程序提供了一個(gè)UI,允許用戶恢復(fù)其過(guò)去的購(gòu)買記錄。
@IBAction func restore(_ sender: UIBarButtonItem) {
// Calls StoreObserver to restore all restorable purchases.
StoreObserver.shared.restore()
}
使用SKPaymentQueue的restoreCompletedTransactions還原非消耗性和自動(dòng)續(xù)訂的訂閱。 StoreKit通過(guò)調(diào)用每個(gè)已恢復(fù)的交易的SKPaymentTransactionStateRestored交易狀態(tài)的paymentQueue:updatedTransactions:通知應(yīng)用程序的交易觀察者。 如果還原失敗,請(qǐng)參閱restoreCompletedTransactions的討論以獲取有關(guān)如何解決該問(wèn)題的詳細(xì)信息。 恢復(fù)非續(xù)訂的訂閱不在此示例的范圍內(nèi)。
提供內(nèi)容并完成交易
在收到狀態(tài)為SKPaymentTransactionStatePurchased或SKPaymentTransactionStateRestored的交易后,應(yīng)用必須交付內(nèi)容或解鎖購(gòu)買的功能。這些狀態(tài)表明App Store已從用戶處收到產(chǎn)品付款。當(dāng)購(gòu)買的產(chǎn)品包含來(lái)自App Store的托管內(nèi)容時(shí),請(qǐng)致電SKPaymentQueue的startDownloads:下載內(nèi)容。
未完成的交易留在付款隊(duì)列中。在每次從后臺(tái)啟動(dòng)或從后臺(tái)恢復(fù)運(yùn)行之前,StoreKit都會(huì)調(diào)用該應(yīng)用程序的永久觀察者的paymentQueue:updatedTransactions:直到應(yīng)用程序完成這些交易。結(jié)果,App Store可能會(huì)反復(fù)提示用戶對(duì)他們的購(gòu)買進(jìn)行身份驗(yàn)證,或阻止他們從該應(yīng)用程序購(gòu)買產(chǎn)品。
對(duì)狀態(tài)為SKPaymentTransactionStateFailed,SKPaymentTransactionStatePurchased或SKPaymentTransactionStateRestored的事務(wù)調(diào)用finishTransaction:以將其從隊(duì)列中刪除。完成的交易無(wú)法恢復(fù)。因此,應(yīng)用程序必須提供購(gòu)買的內(nèi)容,下載產(chǎn)品的所有Apple托管內(nèi)容或完成購(gòu)買過(guò)程,然后才能完成交易。
// Finish the successful transaction.
SKPaymentQueue.default().finishTransaction(transaction)
SKPaymentQueue 付款隊(duì)列
由App Store處理的付款交易隊(duì)列。
@interface SKPaymentQueue : NSObject
總覽
付款隊(duì)列與App Store通信并顯示用戶界面,以便用戶可以授權(quán)付款。隊(duì)列的內(nèi)容在應(yīng)用程序啟動(dòng)之間保持不變。
要處理付款,請(qǐng)首先將至少一個(gè)觀察者對(duì)象(SKPaymentTransactionObserver)添加到隊(duì)列中(請(qǐng)參閱addTransactionObserver:`)。然后,為用戶要購(gòu)買的商品添加支付對(duì)象(SKPayment)。每次添加付款對(duì)象時(shí),隊(duì)列都會(huì)創(chuàng)建一個(gè)交易對(duì)象(SKPaymentTransaction)以處理該付款并將其排隊(duì)。付款完成后,隊(duì)列更新交易對(duì)象,然后調(diào)用任何觀察者對(duì)象向他們提供更新的交易。您的觀察者應(yīng)處理該事務(wù),然后將其從隊(duì)列中刪除。
您用于處理已處理交易的確切機(jī)制取決于應(yīng)用程序的設(shè)計(jì)和所購(gòu)買的產(chǎn)品。以下是一些常見(jiàn)示例:
- 如果產(chǎn)品是您的應(yīng)用程序中已內(nèi)置的功能,則您的應(yīng)用程序?qū)⒂迷摴δ軄?lái)處理交易。
- 如果產(chǎn)品包含
App Store提供的可下載內(nèi)容,則您的應(yīng)用將從交易中檢索SKDownload對(duì)象,并要求付款隊(duì)列下載它們。您在創(chuàng)建產(chǎn)品信息時(shí)將實(shí)際的內(nèi)容文件提供給App Store并提供給App Store Connect。 - 如果產(chǎn)品代表您自己的服務(wù)器提供的可下載內(nèi)容,則您的應(yīng)用程序可能會(huì)打開(kāi)與服務(wù)器的網(wǎng)絡(luò)連接并從那里下載內(nèi)容。
有關(guān)設(shè)計(jì)應(yīng)用程序的付款處理部分的更多信息,請(qǐng)參閱應(yīng)用程序內(nèi)購(gòu)買編程指南。
Product Information 產(chǎn)品信息
加載應(yīng)用內(nèi)商品的唯一標(biāo)識(shí)符,以便從App Store檢索商品信息。
應(yīng)用內(nèi)購(gòu)買流程可以分為三個(gè)階段。 在購(gòu)買過(guò)程的第一階段,您的應(yīng)用程序從App Store檢索有關(guān)其產(chǎn)品的信息,向用戶顯示其商店UI,并讓用戶選擇產(chǎn)品。 當(dāng)用戶在您的應(yīng)用商店中選擇產(chǎn)品時(shí),您的應(yīng)用會(huì)請(qǐng)求付款,最后,您的應(yīng)用會(huì)交付該產(chǎn)品。 圖1中突出顯示了應(yīng)用程序和App Store在第一階段執(zhí)行的步驟。

要開(kāi)始購(gòu)買過(guò)程,您的應(yīng)用必須知道其產(chǎn)品標(biāo)識(shí)符,以便它可以從App Store檢索有關(guān)產(chǎn)品的信息,并將其商店UI呈現(xiàn)給用戶。 應(yīng)用中出售的每個(gè)產(chǎn)品都有唯一的產(chǎn)品標(biāo)識(shí)符。 當(dāng)您創(chuàng)建新的應(yīng)用內(nèi)購(gòu)買產(chǎn)品時(shí),您可以在App Store Connect中提供此值(有關(guān)更多信息,請(qǐng)參閱 Create an In-App Purchase)。 您的應(yīng)用使用這些產(chǎn)品標(biāo)識(shí)符來(lái)獲取有關(guān)在App Store中可出售的產(chǎn)品的信息(例如定價(jià)),并在用戶購(gòu)買這些產(chǎn)品時(shí)提交付款請(qǐng)求。
有幾種策略可用于在應(yīng)用程序中存儲(chǔ)產(chǎn)品標(biāo)識(shí)符列表,例如嵌入到應(yīng)用程序捆綁包中或存儲(chǔ)在服務(wù)器上。 然后,您可以通過(guò)在應(yīng)用程序包中本地讀取產(chǎn)品標(biāo)識(shí)符或從服務(wù)器中獲取它們來(lái)檢索產(chǎn)品標(biāo)識(shí)符。 選擇最能滿足您應(yīng)用程序需求的方法。
注意
沒(méi)有運(yùn)行時(shí)機(jī)制可以獲取在App Store Connect中為特定應(yīng)用配置的所有產(chǎn)品的列表。 您負(fù)責(zé)管理應(yīng)用程序的產(chǎn)品列表,并將該信息提供給您的應(yīng)用程序。 如果您需要管理大量產(chǎn)品,請(qǐng)考慮使用App Store Connect中的批量XML上傳/下載功能。
從應(yīng)用程序包中檢索產(chǎn)品ID
在以下情況下,將產(chǎn)品標(biāo)識(shí)符嵌入到您的應(yīng)用程序包中:
- 您的應(yīng)用有固定的應(yīng)用內(nèi)購(gòu)買產(chǎn)品列表。 例如,通過(guò)應(yīng)用內(nèi)購(gòu)買來(lái)刪除廣告或解鎖功能的應(yīng)用可以將產(chǎn)品標(biāo)識(shí)符列表嵌入應(yīng)用包中。
- 您希望用戶更新應(yīng)用程序以查看新的應(yīng)用程序內(nèi)購(gòu)買產(chǎn)品。
- 該應(yīng)用程序或產(chǎn)品不需要服務(wù)器。
在您的應(yīng)用程序捆綁包中包含一個(gè)屬性列表文件,其中包含一系列產(chǎn)品標(biāo)識(shí)符,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>com.example.level1</string>
<string>com.example.level2</string>
<string>com.example.rocket_car</string>
</array>
</plist>
要從屬性列表中獲取產(chǎn)品標(biāo)識(shí)符,請(qǐng)?jiān)趹?yīng)用程序捆綁包中找到文件并閱讀。
NSURL *url = [[NSBundle mainBundle] URLForResource:@"product_ids"
withExtension:@"plist"];
NSArray *productIdentifiers = [NSArray arrayWithContentsOfURL:url];
從服務(wù)器中檢索產(chǎn)品ID
在以下情況下,存儲(chǔ)來(lái)自服務(wù)器的產(chǎn)品標(biāo)識(shí)符:
- 您可以經(jīng)常更新應(yīng)用程序內(nèi)產(chǎn)品列表,而無(wú)需更新應(yīng)用程序。 例如,支持其他級(jí)別或角色的游戲應(yīng)從您的服務(wù)器獲取產(chǎn)品標(biāo)識(shí)符列表。
- 產(chǎn)品包括交付的內(nèi)容。
- 您的應(yīng)用或產(chǎn)品需要服務(wù)器。
使用產(chǎn)品標(biāo)識(shí)符在您的服務(wù)器上托管JSON文件。 例如,以下JSON文件包含三個(gè)產(chǎn)品ID:
[
"com.example.level1",
"com.example.level2",
"com.example.rocket_car"
]
要從服務(wù)器獲取產(chǎn)品標(biāo)識(shí)符,請(qǐng)獲取并讀取JSON文件。
- (void)fetchProductIdentifiersFromURL:(NSURL *)url delegate:(id)delegate
{
dispatch_queue_t global_queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(global_queue, ^{
NSError *err;
NSData *jsonData = [NSData dataWithContentsOfURL:url
options:NULL
error:&err];
if (!jsonData) { /* Handle the error. */ }
NSArray *productIdentifiers = [NSJSONSerialization
JSONObjectWithData:jsonData options:NULL error:&err];
if (!productIdentifiers) { /* Handle the error. */ }
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^{
[delegate displayProducts:productIdentifiers]; // Custom method.
});
});
}
考慮對(duì)JSON文件進(jìn)行版本控制,以便將來(lái)的應(yīng)用程序版本可以更改其結(jié)構(gòu),而不會(huì)破壞應(yīng)用程序的舊版本。 例如,您可以命名使用舊結(jié)構(gòu)products_v1.json的文件和使用新結(jié)構(gòu)products_v2.json的文件。 如果您的JSON文件比示例中的簡(jiǎn)單數(shù)組復(fù)雜,則此功能特別有用。
為了確保您的應(yīng)用程序保持響應(yīng)狀態(tài),請(qǐng)使用后臺(tái)線程下載JSON文件并提取產(chǎn)品標(biāo)識(shí)符列表。 為了使傳輸?shù)臄?shù)據(jù)最少,請(qǐng)使用標(biāo)準(zhǔn)的HTTP緩存機(jī)制,例如Last-Modified和If-Modified-Since`標(biāo)頭。
加載所有應(yīng)用內(nèi)商品標(biāo)識(shí)后,將其傳遞到商品信息請(qǐng)求中,并發(fā)送到App Store。 有
從App Store獲取產(chǎn)品信息
在您的應(yīng)用中檢索有關(guān)待售產(chǎn)品的最新信息,以顯示給用戶。
總覽
為了確保您的用戶只能看到實(shí)際可購(gòu)買的產(chǎn)品,請(qǐng)?jiān)陲@示應(yīng)用商店的用戶界面之前查詢App Store。 您將需要所有應(yīng)用程序產(chǎn)品標(biāo)識(shí)符的列表; 有關(guān)獲取此列表的更多信息,請(qǐng)參閱加載應(yīng)用內(nèi)商品標(biāo)識(shí)。
請(qǐng)求產(chǎn)品信息
要查詢App Store,請(qǐng)創(chuàng)建一個(gè)products request(SKProductsRequest),并使用您的產(chǎn)品標(biāo)識(shí)符列表對(duì)其進(jìn)行初始化。 確保對(duì)請(qǐng)求對(duì)象有嚴(yán)格的引用; 否則,系統(tǒng)可能會(huì)在請(qǐng)求完成之前取消分配該請(qǐng)求。
products request檢索有關(guān)有效產(chǎn)品的信息以及無(wú)效產(chǎn)品標(biāo)識(shí)符的列表,然后調(diào)用其委托以處理結(jié)果。 委托必須實(shí)現(xiàn)SKProductsRequestDelegate協(xié)議以處理來(lái)自App Store的響應(yīng)。 這是這兩段代碼的簡(jiǎn)單實(shí)現(xiàn):
// Custom method.
- (void)validateProductIdentifiers:(NSArray *)productIdentifiers
{
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
// Keep a strong reference to the request.
self.request = productsRequest;
productsRequest.delegate = self;
[productsRequest start];
}
// SKProductsRequestDelegate protocol method.
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
// Handle any invalid product identifiers.
}
[self displayStoreUI]; // Custom method.
}
保留對(duì)返回給委托的產(chǎn)品對(duì)象數(shù)組(SKProduct)的引用,因?yàn)楫?dāng)用戶購(gòu)買產(chǎn)品時(shí),您將需要相應(yīng)的產(chǎn)品對(duì)象來(lái)創(chuàng)建付款請(qǐng)求。 如果在您的應(yīng)用程序上出售的產(chǎn)品列表可能會(huì)發(fā)生變化,例如當(dāng)您添加或從銷售中刪除產(chǎn)品時(shí),請(qǐng)考慮創(chuàng)建一個(gè)自定義類,該類封裝對(duì)產(chǎn)品對(duì)象的引用以及其他信息,例如圖片或描述文字 從服務(wù)器上獲取的信息。 有關(guān)付款請(qǐng)求的更多信息,請(qǐng)參閱Requesting a Payment from the App Store。
解決無(wú)效的產(chǎn)品ID
在App Store對(duì)您的產(chǎn)品請(qǐng)求的回復(fù)中,無(wú)效的產(chǎn)品ID通常表示您的應(yīng)用的產(chǎn)品ID列表中存在錯(cuò)誤。 無(wú)效的產(chǎn)品ID也可能意味著您在App Store Connect中配置了不正確的產(chǎn)品。 可行的用戶界面和深入的日志記錄可以幫助您更輕松地解決此類問(wèn)題:
- 在生產(chǎn)版本中,通過(guò)顯示應(yīng)用程序其余的商店用戶界面并忽略無(wú)效的產(chǎn)品,以使其正常運(yùn)行。
- 在開(kāi)發(fā)版本中,顯示錯(cuò)誤以引起注意該問(wèn)題。
- 在生產(chǎn)和開(kāi)發(fā)版本中,都使用
NSLog將消息寫(xiě)入控制臺(tái),以便您記錄無(wú)效標(biāo)識(shí)符。 - 如果您的應(yīng)用程序是從服務(wù)器上獲取列表的,則還可以定義一種日志記錄機(jī)制,以使您的應(yīng)用程序?qū)o(wú)效標(biāo)識(shí)符的列表發(fā)送回服務(wù)器。
購(gòu)買- 從App Store請(qǐng)求付款
用戶選擇要購(gòu)買的產(chǎn)品時(shí),向App Store提交付款請(qǐng)求。
總覽
在顯示應(yīng)用程序的商店用戶界面后,用戶可以在應(yīng)用程序內(nèi)進(jìn)行購(gòu)買。 當(dāng)用戶選擇產(chǎn)品時(shí),您的應(yīng)用會(huì)創(chuàng)建一個(gè)付款請(qǐng)求并將其提交到App Store。
實(shí)施應(yīng)用內(nèi)購(gòu)買流程可以分為三個(gè)階段。 在第一階段,您的應(yīng)用將檢索產(chǎn)品信息。 然后,當(dāng)用戶在您的應(yīng)用商店中選擇產(chǎn)品時(shí),您的應(yīng)用就會(huì)請(qǐng)求付款。 最后,您的應(yīng)用可以交付產(chǎn)品。 本節(jié)詳細(xì)介紹了應(yīng)用程序和App Store在第二階段執(zhí)行的步驟,如圖1所示。

創(chuàng)建付款請(qǐng)求
當(dāng)用戶選擇要購(gòu)買的產(chǎn)品時(shí),請(qǐng)使用相應(yīng)的SKProduct對(duì)象創(chuàng)建付款請(qǐng)求,并根據(jù)需要設(shè)置數(shù)量,如下所示。 產(chǎn)品對(duì)象來(lái)自您應(yīng)用的產(chǎn)品請(qǐng)求所返回的一系列產(chǎn)品,如從Fetching Product Information from the App Store。
SKProduct *product = <# Product returned by a products request. #>;
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.quantity = 2;
提交付款請(qǐng)求
通過(guò)將您的付款請(qǐng)求添加到付款隊(duì)列,將其提交到App Store。 如果您將付款對(duì)象多次添加到隊(duì)列中,則會(huì)多次提交到App Store,向用戶收費(fèi)并要求您的應(yīng)用每次交付產(chǎn)品。
[[SKPaymentQueue defaultQueue] addPayment:payment];
對(duì)于您的應(yīng)用程序提交的每個(gè)付款請(qǐng)求,它都會(huì)收到相應(yīng)的交易要處理。 有關(guān)交易和付款隊(duì)列的更多信息,請(qǐng)參閱Processing a Transaction。
對(duì)于自動(dòng)續(xù)訂訂閱,您可以針對(duì)確定為有資格接收?qǐng)?bào)價(jià)的用戶提交帶有訂閱報(bào)價(jià)的付款請(qǐng)求。 有關(guān)更多信息,請(qǐng)參閱Implementing Promotional Offers in Your App。
交易處理
注冊(cè)事交易列觀察器以從App Store獲取和處理交易更新。
總覽
實(shí)施應(yīng)用內(nèi)購(gòu)買流程可以分為三個(gè)階段。 您首先要檢索產(chǎn)品信息,然后通過(guò)將其添加到付款隊(duì)列中來(lái)發(fā)送付款請(qǐng)求。 最后,您的應(yīng)用可以交付成功交易的產(chǎn)品。 本節(jié)詳細(xì)介紹了您的應(yīng)用程序和App Store在最后階段執(zhí)行的步驟,如圖1所示。
在處理付款請(qǐng)求后,App Store會(huì)調(diào)用交易隊(duì)列觀察器。 然后,您的應(yīng)用會(huì)記錄有關(guān)購(gòu)買的信息以供將來(lái)啟動(dòng),下載購(gòu)買的內(nèi)容并將交易標(biāo)記為完成。
圖1通過(guò)監(jiān)視交易隊(duì)列以交付產(chǎn)品來(lái)完成購(gòu)買過(guò)程

監(jiān)視隊(duì)列中的交易
交易隊(duì)列在讓您的應(yīng)用通過(guò)StoreKit框架與App Store進(jìn)行通信中起著核心作用。您將工作添加到需要應(yīng)用商店執(zhí)行的隊(duì)列中,例如要處理的付款請(qǐng)求。當(dāng)交易狀態(tài)發(fā)生變化時(shí)(例如,付款請(qǐng)求成功時(shí)),StoreKit會(huì)調(diào)用應(yīng)用的交易隊(duì)列觀察器。您決定哪個(gè)類充當(dāng)觀察員。在非常小的應(yīng)用程序中,您可以處理應(yīng)用程序委托中的所有StoreKit邏輯,包括觀察交易隊(duì)列。但是,在大多數(shù)應(yīng)用中,您創(chuàng)建一個(gè)單獨(dú)的類來(lái)處理此觀察者邏輯以及應(yīng)用的其余存儲(chǔ)邏輯。觀察者必須符合SKPaymentTransactionObserver協(xié)議。
通過(guò)添加觀察者,您的應(yīng)用無(wú)需持續(xù)輪詢其活動(dòng)交易的狀態(tài)。您的應(yīng)用將交易隊(duì)列用于支付請(qǐng)求,下載Apple托管的內(nèi)容以及確定訂閱已續(xù)訂。
始終在啟動(dòng)應(yīng)用程序后立即注冊(cè)事務(wù)隊(duì)列觀察器,如下所示。有關(guān)更多指導(dǎo),請(qǐng)參閱Setting Up the Transaction Observer for the Payment Queue.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/* ... */
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
}
確保觀察者隨時(shí)準(zhǔn)備處理交易,不僅是在將交易添加到隊(duì)列中之后。 例如,如果用戶在進(jìn)入隧道之前立即在您的應(yīng)用程序中購(gòu)買了商品,則在沒(méi)有網(wǎng)絡(luò)連接的情況下,您的應(yīng)用程序可能無(wú)法傳遞購(gòu)買的內(nèi)容。 下次您的應(yīng)用程序啟動(dòng)時(shí),StoreKit再次調(diào)用您的交易隊(duì)列觀察器,并且您的應(yīng)用程序應(yīng)處理交易并交付購(gòu)買的內(nèi)容。 同樣,如果您的應(yīng)用程序未能將交易標(biāo)記為已完成,則StoreKit會(huì)在您的應(yīng)用程序每次啟動(dòng)時(shí)調(diào)用觀察者,直到事務(wù)完成為止。
在您的交易隊(duì)列觀察器上實(shí)現(xiàn)paymentQueue:updatedTransactions:方法。 當(dāng)交易狀態(tài)改變時(shí),例如在處理付款請(qǐng)求時(shí),StoreKit調(diào)用此方法。 交易狀態(tài)會(huì)告訴您您的應(yīng)用需要執(zhí)行什么操作,如表1所示。
| 狀態(tài) | 對(duì)應(yīng)的處理方式 |
|---|---|
| SKPaymentTransactionStatePurchasing | 更新您的用戶界面以反映進(jìn)行中的狀態(tài),然后等待再次調(diào)用。 |
| SKPaymentTransactionStateDeferred | 更新您的用戶界面以反映延遲狀態(tài),然后等待再次調(diào)用。 |
| SKPaymentTransactionStateFailed | 使用error屬性的值向用戶顯示消息。 有關(guān)錯(cuò)誤常量的列表,請(qǐng)參見(jiàn)SKErrorDomain。 |
| SKPaymentTransactionStatePurchased | 通常通過(guò)解鎖功能或提供內(nèi)容來(lái)提供購(gòu)買的功能。 |
| SKPaymentTransactionStateRestored | 恢復(fù)以前購(gòu)買的功能。 |
隊(duì)列中的事務(wù)可以按任何順序更改狀態(tài)。 您的應(yīng)用需要隨時(shí)準(zhǔn)備就緒,可以處理任何活動(dòng)交易。 根據(jù)每個(gè)交易的交易狀態(tài)對(duì)其進(jìn)行操作,如本例所示:
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
// Call the appropriate custom method for the transaction state.
case SKPaymentTransactionStatePurchasing:
[self showTransactionAsInProgress:transaction deferred:NO];
break;
case SKPaymentTransactionStateDeferred:
[self showTransactionAsInProgress:transaction deferred:YES];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
// For debugging
NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));
break;
}
}
}
更新應(yīng)用程序的用戶界面以反映交易的變化
為了使您的用戶界面在等待時(shí)保持最新?tīng)顟B(tài),事務(wù)隊(duì)列觀察器可以從SKPaymentTransactionObserver協(xié)議中實(shí)現(xiàn)可選方法,如下所示:
- 當(dāng)
StoreKit從隊(duì)列中刪除事務(wù)時(shí),StoreKit會(huì)調(diào)用paymentQueue:removedTransactions:方法。 在實(shí)施此方法時(shí),請(qǐng)從應(yīng)用的用戶界面中刪除相應(yīng)的項(xiàng)。 - 當(dāng)
StoreKit完成還原交易時(shí),將根據(jù)其是否存在錯(cuò)誤來(lái)調(diào)用paymentQueueRestoreCompletedTransactionsFinished:或paymentQueue:restoreCompletedTransactionsFailedWithError:方法。 在實(shí)施這些方法時(shí),請(qǐng)更新應(yīng)用的用戶界面以反映成功或失敗。
對(duì)于成功處理的交易,您的應(yīng)用應(yīng)驗(yàn)證與交易關(guān)聯(lián)的收據(jù),以驗(yàn)證用戶購(gòu)買的商品并相應(yīng)地解鎖內(nèi)容。 有關(guān)在服務(wù)器端驗(yàn)證收據(jù)的更多信息,請(qǐng)參閱Validating Receipts with the App Store。
選擇收據(jù)驗(yàn)證技術(shù)
選擇適用于您的應(yīng)用的收據(jù)驗(yàn)證類型。
總覽
App Store收據(jù)提供了應(yīng)用程序銷售或在應(yīng)用程序內(nèi)進(jìn)行的任何購(gòu)買的記錄,您可以通過(guò)向應(yīng)用程序或服務(wù)器添加收據(jù)驗(yàn)證碼來(lái)驗(yàn)證購(gòu)買的內(nèi)容。 收據(jù)驗(yàn)證需要了解安全編碼技術(shù),才能采用對(duì)您的應(yīng)用程序安全且獨(dú)特的解決方案。
選擇驗(yàn)證技術(shù)
有兩種驗(yàn)證收據(jù)真實(shí)性的方法:
- 建議在設(shè)備上進(jìn)行本地設(shè)備上的收據(jù)驗(yàn)證,以驗(yàn)證帶有應(yīng)用內(nèi)購(gòu)買的應(yīng)用的收據(jù)簽名。
- 建議通過(guò)App Store進(jìn)行服務(wù)器端收據(jù)驗(yàn)證,以持久保存應(yīng)用內(nèi)購(gòu)買,以維護(hù)和管理購(gòu)買記錄。
比較方法并確定最適合您的應(yīng)用程序和基礎(chǔ)架構(gòu)的方法。 您也可以選擇實(shí)施兩種方法。
消耗性的應(yīng)用內(nèi)購(gòu)買將保留在收據(jù)中,直到您調(diào)用finishTransaction:為止。 如果需要,維護(hù)和管理服務(wù)器上消耗品的記錄。 非消耗品,自動(dòng)續(xù)訂的訂閱項(xiàng)目和非續(xù)訂的訂閱項(xiàng)目會(huì)無(wú)限期保留在收據(jù)中。 對(duì)于自動(dòng)續(xù)訂訂閱管理,服務(wù)器端收據(jù)驗(yàn)證比設(shè)備上的收據(jù)驗(yàn)證具有關(guān)鍵優(yōu)勢(shì)。
| -- | 本地設(shè)備上驗(yàn)證 | 服務(wù)器端驗(yàn)證 |
|---|---|---|
| 驗(yàn)證收據(jù)的真實(shí)性 | Yes | Yes |
| 包括續(xù)訂交易 | Yes | Yes |
| 包括其他用戶訂閱信息 | No | Yes |
| 處理續(xù)簽而無(wú)需客戶依賴性 | No | Yes |
| Resistant to device clock change | No | Yes |
注意
為了使設(shè)備上的收據(jù)驗(yàn)證包括續(xù)訂交易,必須有互聯(lián)網(wǎng)連接才能刷新收據(jù)。
有關(guān)為包含自動(dòng)可更新訂閱產(chǎn)品的應(yīng)用程序?qū)嵤┦論?jù)驗(yàn)證的更多信息,請(qǐng)參見(jiàn) WWDC 2018 > Engineering Subscriptions。
驗(yàn)證收據(jù)
本地驗(yàn)證需要代碼讀取和驗(yàn)證PKCS#7簽名,還需要代碼解析和驗(yàn)證簽名的有效負(fù)載。 通過(guò)App Store進(jìn)行驗(yàn)證需要在您的應(yīng)用程序與服務(wù)器之間建立安全連接,并在服務(wù)器上進(jìn)行編碼以通過(guò)App Store驗(yàn)證收據(jù)。 有關(guān)服務(wù)器端驗(yàn)證的更多信息,請(qǐng)參閱Validating Receipts with the App Store。
盡管收據(jù)通常會(huì)在完成購(gòu)買或恢復(fù)購(gòu)買后立即更新,但是當(dāng)應(yīng)用程序未運(yùn)行時(shí),更改可能會(huì)在其他時(shí)間發(fā)生。 如有必要,請(qǐng)調(diào)用SKReceiptRefreshRequest以確保您正在使用的收據(jù)是最新的,例如在后臺(tái)更新訂閱時(shí)。 此刷新需要網(wǎng)絡(luò)連接。
通過(guò)App Store驗(yàn)證收據(jù)
在安全服務(wù)器上通過(guò)App Store驗(yàn)證交易。
總覽
App Store收據(jù)是使用Apple證書(shū)簽名的二進(jìn)制加密文件。 為了讀取加密文件的內(nèi)容,您需要將其通過(guò)verifyReceipt端點(diǎn)。 端點(diǎn)的響應(yīng)包括可讀的JSON正文。 與App Store的通信按照RFC 4627的定義構(gòu)造為JSON字典。二進(jìn)制數(shù)據(jù)按RFC 4648的定義進(jìn)行Base64編碼。通過(guò)安全服務(wù)器驗(yàn)證與App Store的收據(jù)。 有關(guān)與App Store建立安全網(wǎng)絡(luò)連接的信息,請(qǐng)參閱Preventing Insecure Network Connections.
警告
不要從您的應(yīng)用程序調(diào)用App Store服務(wù)器的verifyReceipt端點(diǎn)。 您無(wú)法直接在用戶的設(shè)備與App Store之間建立信任的連接,因?yàn)槟鸁o(wú)法控制該連接的任何一端,這會(huì)使它容易受到中間人攻擊。
獲取收據(jù)數(shù)據(jù)
要從設(shè)備上的應(yīng)用中檢索收據(jù)數(shù)據(jù),請(qǐng)使用NSBundle的appStoreReceiptURL方法找到該應(yīng)用的收據(jù),然后在Base64中對(duì)數(shù)據(jù)進(jìn)行編碼。 將此Base64編碼的數(shù)據(jù)發(fā)送到您的服務(wù)器。
/* Load the receipt from the app bundle. */
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) {
NSLog(@"no receipt");
/* No local receipt -- handle the error. */
} else {
/* Get the receipt in encoded format */
NSString *encodedReceipt = [receipt base64EncodedStringWithOptions:0];
}
/* ... Send the receipt data to your server ... */
將收據(jù)數(shù)據(jù)發(fā)送到App Store
在您的服務(wù)器上,創(chuàng)建一個(gè)帶有收據(jù)數(shù)據(jù),密碼(如果收據(jù)包含自動(dòng)續(xù)訂)的JSON對(duì)象,并在requestBody中詳細(xì)說(shuō)明exclude-old-transactions鍵。
提交此JSON對(duì)象作為HTTP POST請(qǐng)求的有效負(fù)載。 在沙盒中測(cè)試您的應(yīng)用程序以及在審查應(yīng)用程序時(shí),請(qǐng)使用測(cè)試環(huán)境URL https://sandbox.itunes.apple.com/verifyReceipt。 當(dāng)您的應(yīng)用程序在App Store中上線時(shí),請(qǐng)使用生產(chǎn)URL https://buy.itunes.apple.com/verifyReceipt。 有關(guān)這些端點(diǎn)的更多信息,請(qǐng)參見(jiàn)verifyReceipt。
重要信息
首先使用生產(chǎn)網(wǎng)址驗(yàn)證收據(jù); 然后使用沙盒網(wǎng)址進(jìn)行驗(yàn)證,如果您收到21007狀態(tài)代碼。 這種方法可確保您在測(cè)試應(yīng)用程序,通過(guò)App Review進(jìn)行審查或在App Store中運(yùn)行應(yīng)用程序時(shí),不必在URL之間切換。
解析響應(yīng)
App Store的響應(yīng)有效負(fù)載是一個(gè)JSON對(duì)象,其中包含responseBody中詳細(xì)介紹的鍵和值。
in_app數(shù)組包含用戶先前購(gòu)買的不可消耗,不可更新的訂閱以及可自動(dòng)更新的訂閱項(xiàng)。 檢查響應(yīng)中這些應(yīng)用內(nèi)購(gòu)買類型的值,以根據(jù)需要驗(yàn)證交易。
對(duì)于自動(dòng)續(xù)訂的訂閱項(xiàng)目,解析響應(yīng)以獲取有關(guān)當(dāng)前有效訂閱期的信息。 當(dāng)您驗(yàn)證訂閱的收據(jù)時(shí),latest_receipt包含最新的編碼收據(jù),與請(qǐng)求中收據(jù)數(shù)據(jù)的值相同,并且latest_receipt_info包含訂閱的所有交易,包括首次購(gòu)買和后續(xù)續(xù)訂,但不包含 包括所有還原。
您可以使用這些值來(lái)檢查自動(dòng)更新訂閱是否已過(guò)期。 將這些值與expiration_intent訂閱字段一起使用以獲取到期原因。
解鎖購(gòu)買內(nèi)容
驗(yàn)證購(gòu)買后,將內(nèi)容交付給用戶。
總覽
購(gòu)買完成后,您有責(zé)任確保以編程方式將內(nèi)容提供給用戶。
識(shí)別購(gòu)買的內(nèi)容
對(duì)于啟用了應(yīng)用程序功能(例如高級(jí)訂閱)的應(yīng)用程序內(nèi)購(gòu)買產(chǎn)品,請(qǐng)?jiān)O(shè)置布爾值以啟用代碼路徑并根據(jù)需要更新用戶界面。 查看應(yīng)用程序中發(fā)生的交易的永久記錄,以確定要解鎖的功能。 每當(dāng)用戶完成購(gòu)買并在應(yīng)用啟動(dòng)時(shí),您的應(yīng)用都必須更新此布爾值。 有關(guān)進(jìn)行永久記錄的信息,請(qǐng)參閱Persisting a Purchase。
例如,使用應(yīng)用程序收據(jù),您的代碼可能如下所示:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// Custom method to work with receipts
BOOL rocketCarEnabled = [self receipt:receiptData
includesProductID:@"com.example.rocketCar"];
或者,使用user defaults system:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];
定義布爾變量后,使用購(gòu)買信息在應(yīng)用程序中啟用適當(dāng)?shù)拇a路徑:
if (rocketCarEnabled) {
// Use the rocket car.
} else {
// Use the regular car.
}
傳送相關(guān)內(nèi)容
您的應(yīng)用必須將與購(gòu)買的產(chǎn)品相關(guān)的任何內(nèi)容交付給用戶。例如,在音樂(lè)應(yīng)用程序中購(gòu)買樂(lè)器需要傳送讓用戶演奏這些樂(lè)器所需的聲音文件。您可以將該內(nèi)容嵌入到應(yīng)用程序的捆綁軟件中,或根據(jù)需要下載。每種方法都有其優(yōu)點(diǎn)和缺點(diǎn)。
在您的應(yīng)用程序中嵌入較小的文件(最大幾兆字節(jié)),尤其是在您希望大多數(shù)用戶購(gòu)買該產(chǎn)品的情況下。您可以在用戶購(gòu)買應(yīng)用捆綁包后立即使其可用。但是,要添加或更新應(yīng)用程序捆綁包中的內(nèi)容,必須提交應(yīng)用程序的更新版本。
僅在需要時(shí)下載較大的文件。將內(nèi)容與應(yīng)用捆綁包分開(kāi)可以使應(yīng)用的初始下載量小。例如,游戲可以在其應(yīng)用包中包含第一級(jí),并允許用戶在購(gòu)買時(shí)下載其他級(jí)。假設(shè)您的應(yīng)用從服務(wù)器中獲取了產(chǎn)品標(biāo)識(shí)符列表,并且該信息未在應(yīng)用捆綁中進(jìn)行硬編碼,則無(wú)需重新提交應(yīng)用即可添加或更新應(yīng)用下載的內(nèi)容。
注意
您無(wú)法修補(bǔ)應(yīng)用二進(jìn)制文件或下載可執(zhí)行代碼。 您的應(yīng)用在提交時(shí)必須包含支持其所有功能所需的所有可執(zhí)行代碼。 如果新產(chǎn)品需要更改代碼,請(qǐng)?zhí)峤粦?yīng)用程序的更新版本。
加載本地內(nèi)容
從應(yīng)用程序包加載其他資源時(shí),請(qǐng)使用NSBundle類加載本地內(nèi)容。
NSURL *url = [[NSBundle mainBundle] URLForResource:@"rocketCar"
withExtension:@"plist"];
[self loadVehicleAtURL:url];
從Apple的服務(wù)器下載托管內(nèi)容
大多數(shù)應(yīng)用程序應(yīng)使用Apple托管的內(nèi)容來(lái)下載文件。您使用Xcode中的“應(yīng)用內(nèi)購(gòu)買內(nèi)容”目標(biāo)創(chuàng)建了Apple托管的內(nèi)容包,并將其提交到App Store Connect。 Apple的服務(wù)器使用支持其他大規(guī)模操作(例如App Store)的相同基礎(chǔ)結(jié)構(gòu)來(lái)存儲(chǔ)您的應(yīng)用程序內(nèi)容。即使您的應(yīng)用未運(yùn)行,Apple托管的內(nèi)容也會(huì)在后臺(tái)自動(dòng)下載。
如果您需要支持舊版本的iOS或在多個(gè)平臺(tái)上共享服務(wù)器基礎(chǔ)結(jié)構(gòu),則可以選擇使用自己的服務(wù)器基礎(chǔ)結(jié)構(gòu)來(lái)托管自己的內(nèi)容。
當(dāng)用戶購(gòu)買具有關(guān)聯(lián)的Apple托管內(nèi)容的產(chǎn)品時(shí),傳遞給您的事務(wù)隊(duì)列觀察器的事務(wù)還包括一個(gè) SKDownload實(shí)例,可讓您下載關(guān)聯(lián)的內(nèi)容。
要下載內(nèi)容,請(qǐng)通過(guò)調(diào)用SKPaymentQueue的 startDownloads:方法,將交易對(duì)象的downloads屬性中的下載對(duì)象添加到交易隊(duì)列中。如果downloads屬性的值為nil,則說(shuō)明該交易沒(méi)有Apple托管的內(nèi)容。與下載應(yīng)用程序不同,下載內(nèi)容不會(huì)自動(dòng)要求Wi-Fi連接以獲取大于特定大小的內(nèi)容。避免在沒(méi)有用戶明確行動(dòng)的情況下使用蜂窩網(wǎng)絡(luò)下載大文件。
注意
另外,您可以使用按需資源(ODR)獲得更大的靈活性來(lái)下載應(yīng)用程序中的數(shù)據(jù)。ODR是Apple托管的服務(wù),一旦您使用應(yīng)用收據(jù)確認(rèn)用戶的購(gòu)買后,便可以使用該服務(wù)存儲(chǔ)應(yīng)用內(nèi)購(gòu)買數(shù)據(jù),以供用戶下載內(nèi)容。 與SKDownload相比,此替代方法的優(yōu)勢(shì)在于ODR不需要您調(diào)用即可恢復(fù)事務(wù)并驗(yàn)證用戶以下載Apple服務(wù)器上托管的內(nèi)容。
在交易隊(duì)列觀察器上實(shí)現(xiàn) paymentQueue:updatedDownloads:方法,以響應(yīng)下載狀態(tài)的更改,例如通過(guò)更新UI中的進(jìn)度。如果下載失敗,請(qǐng)使用其error屬性中的信息將錯(cuò)誤呈現(xiàn)給用戶。
確保您的應(yīng)用正常處理錯(cuò)誤。例如,如果設(shè)備在下載期間磁盤(pán)空間不足,請(qǐng)給用戶選擇放棄部分下載或在空間可用時(shí)稍后繼續(xù)下載的選項(xiàng)。
在下載內(nèi)容時(shí),使用 progress和timeRemaining屬性的值更新用戶界面。您可以從用戶界面中使用SKPaymentQueue的 pauseDownloads:, resumeDownloads:,和 cancelDownloads:方法來(lái)讓用戶控制正在進(jìn)行的下載。使用downloadState屬性確定下載是否已完成。不要使用下載對(duì)象的progress或timeRemaining屬性來(lái)檢查其狀態(tài);這些屬性用于更新您的UI。
重點(diǎn)
完成交易之前,請(qǐng)下載所有Apple托管的內(nèi)容。 事務(wù)完成后,其下載對(duì)象將無(wú)法使用。
在iOS中,您的應(yīng)用可以管理下載的文件。 StoreKit框架會(huì)在未設(shè)置備份標(biāo)記的情況下將這些文件保存在Caches目錄中。 下載完成后,您的應(yīng)用程序負(fù)責(zé)將這些文件移動(dòng)到適當(dāng)?shù)奈恢谩?對(duì)于如果設(shè)備磁盤(pán)空間不足(然后由您的應(yīng)用再次下載)可以刪除的內(nèi)容,請(qǐng)將文件保留在Caches目錄中。 否則,將文件移動(dòng)到Documents文件夾并設(shè)置標(biāo)志以將其從用戶備份中排除。
NSError *error;
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
if (!success) { /* Handle error... */ }
在macOS中,系統(tǒng)管理下載的文件; 您的應(yīng)用無(wú)法直接移動(dòng)或刪除它們。 要在下載后找到內(nèi)容,請(qǐng)使用下載對(duì)象的contentUR屬性。 要在隨后的啟動(dòng)中找到文件,請(qǐng)使用SKDownload的contentURLForProductID:類方法。 要?jiǎng)h除文件,請(qǐng)使用deleteContentForProductID:類方法。 有關(guān)閱讀應(yīng)用收據(jù)的信息,請(qǐng)參閱 Validating Receipts with the App Store。
從自己的服務(wù)器上下載內(nèi)容
與您的應(yīng)用程序和服務(wù)器之間的所有其他交互一樣,從您自己的服務(wù)器下載內(nèi)容的過(guò)程的細(xì)節(jié)和機(jī)制也是您的責(zé)任。 通信至少包括以下步驟:
- 您的應(yīng)用會(huì)將收據(jù)發(fā)送到您的服務(wù)器并請(qǐng)求內(nèi)容。
- 您的服務(wù)器將驗(yàn)證收據(jù)以確認(rèn)已購(gòu)買了內(nèi)容,如Validating Receipts with the App Store中所述。
- 假設(shè)收據(jù)有效,您的服務(wù)器將使用內(nèi)容響應(yīng)您的應(yīng)用。
確保您的應(yīng)用正常處理錯(cuò)誤。 例如,如果設(shè)備在下載期間磁盤(pán)空間不足,請(qǐng)給用戶選擇放棄部分下載或在空間可用時(shí)稍后繼續(xù)下載的選項(xiàng)。
考慮如何托管內(nèi)容以及應(yīng)用程序與服務(wù)器通信的安全性。 有關(guān)更多信息,請(qǐng)參見(jiàn)Security Overview。
保存購(gòu)買記錄
保持購(gòu)買記錄,以繼續(xù)根據(jù)需要提供該產(chǎn)品。
總覽
在提供產(chǎn)品之后,您的應(yīng)用應(yīng)記錄購(gòu)買的永久記錄。您的應(yīng)用使用該永久記錄來(lái)繼續(xù)使產(chǎn)品在發(fā)布時(shí)可用并恢復(fù)購(gòu)買。您的應(yīng)用的持久性策略取決于您銷售的產(chǎn)品類型:
- 對(duì)于非消耗性產(chǎn)品和自動(dòng)續(xù)訂的訂閱,請(qǐng)使用應(yīng)用收據(jù)作為您的永久記錄。如果應(yīng)用收據(jù)不可用,請(qǐng)使用用戶默認(rèn)系統(tǒng)或
iCloud保留永久記錄。 - 對(duì)于非續(xù)訂訂閱,請(qǐng)使用
iCloud或您自己的服務(wù)器來(lái)保留永久記錄。 - 對(duì)于消耗品,您的應(yīng)用會(huì)更新其內(nèi)部狀態(tài)以反映購(gòu)買情況。確保更新后的狀態(tài)是支持狀態(tài)保留的對(duì)象的一部分(在
iOS中),或者在應(yīng)用啟動(dòng)時(shí)手動(dòng)保留狀態(tài)(在iOS或macOS中)。
使用用戶默認(rèn)系統(tǒng)或iCloud時(shí),您的應(yīng)用程序可以存儲(chǔ)一個(gè)值(例如數(shù)字或布爾值)或交易收據(jù)的副本。在macOS中,用戶可以使用defaults命令編輯用戶默認(rèn)系統(tǒng)。存儲(chǔ)收據(jù)需要更多的應(yīng)用程序邏輯,但可以防止永久記錄被篡改。
注意
通過(guò)iCloud持久保存時(shí),您的應(yīng)用程序的持久記錄會(huì)在設(shè)備之間同步,但是您的應(yīng)用程序負(fù)責(zé)在其他設(shè)備上下載任何關(guān)聯(lián)的內(nèi)容。
使用應(yīng)用收據(jù)保存購(gòu)買
應(yīng)用收據(jù)包含由Apple加密簽名的用戶購(gòu)買記錄。 有關(guān)更多信息,請(qǐng)參見(jiàn)Choosing a Receipt Validation Technique。
付款后,有關(guān)消費(fèi)品的信息就會(huì)添加到收據(jù)中,并保留在收據(jù)中,直到您完成交易為止。 完成交易后,下次更新收據(jù)時(shí)(例如用戶下次購(gòu)物),將刪除此信息。
購(gòu)買產(chǎn)品時(shí),有關(guān)所有其他購(gòu)買信息的信息會(huì)添加到收據(jù)中,并無(wú)限期保留在收據(jù)中。
在UserDefault或者iCloud中保存一個(gè)值
要將信息存儲(chǔ)在UserDefault值或iCloud中,請(qǐng)?jiān)O(shè)置密鑰的值。
#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
[storage setBool:YES forKey:@"enable_rocket_car"];
[storage setObject:@15 forKey:@"highest_unlocked_level"];
使用自己的服務(wù)器保存購(gòu)買記錄
將收據(jù)的副本以及憑據(jù)或標(biāo)識(shí)符發(fā)送到您的服務(wù)器,以便您可以跟蹤哪些收據(jù)屬于特定用戶。 例如,讓用戶使用用戶名和密碼向您的服務(wù)器標(biāo)識(shí)自己。 不要使用UIDevice的identifierForVendor屬性。 不同的設(shè)備對(duì)此屬性具有不同的值,因此您不能使用它來(lái)識(shí)別和恢復(fù)同一用戶在不同設(shè)備上進(jìn)行的購(gòu)買。
完成交易
完成transaction以完成購(gòu)買過(guò)程。
總覽
完成交易將告訴StoreKit購(gòu)買所需的一切均已完成。 未完成的交易會(huì)一直保留在隊(duì)列中,直到完成為止。因此,您應(yīng)在每次啟動(dòng)應(yīng)用程序時(shí)添加事務(wù)隊(duì)列觀察器,以使應(yīng)用程序能夠完成交易。 您的應(yīng)用程序必須完成每筆交易,無(wú)論成功還是失敗。
完成交易之前,請(qǐng)執(zhí)行以下所有操作:
- 保存購(gòu)買記錄。
- 下載相關(guān)內(nèi)容。
- 更新應(yīng)用的用戶界面,以便用戶可以訪問(wèn)產(chǎn)品。
要完成交易,請(qǐng)?jiān)谥Ц蛾?duì)列上調(diào)用finishTransaction:方法。
SKPaymentTransaction *transaction = <# The current transaction #>;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
完成交易后,請(qǐng)勿對(duì)其執(zhí)行任何操作或進(jìn)行任何交付產(chǎn)品的工作。 如果還有任何工作要做,則說(shuō)明您的應(yīng)用尚未準(zhǔn)備好完成交易。
重要信息
在事務(wù)實(shí)際完成之前,請(qǐng)勿嘗試調(diào)用finishTransaction:方法,并嘗試使用應(yīng)用程序中的其他機(jī)制來(lái)跟蹤未完成的事務(wù)。StoreKit并非設(shè)計(jì)用于這種方式,這樣做會(huì)阻止您的應(yīng)用下載Apple托管的內(nèi)容,并可能導(dǎo)致其他問(wèn)題。
處理退款通知
響應(yīng)在客戶退款期間為消耗性,非消耗性和非更新性訂閱產(chǎn)品創(chuàng)建的通知。
總覽
當(dāng)客戶收到應(yīng)用內(nèi)購(gòu)買的退款時(shí),App Store Server會(huì)發(fā)送近乎實(shí)時(shí)的通知。 如果您跨多個(gè)平臺(tái)提供內(nèi)容(例如游戲中的寶石或硬幣),并且在服務(wù)器上更新玩家?guī)粲囝~,則接收退款通知非常重要。 通過(guò)解釋和處理退款信息來(lái)響應(yīng)退款通知,并在應(yīng)用程序中告知客戶您因退款而采取的任何措施。
接收一次性購(gòu)買的客戶退款通知
客戶以多種方式要求退款,例如:
- 聯(lián)系A(chǔ)pple客戶支持并要求退款
- 登錄并使用Apple的自助服務(wù)工具reportaproblem.apple.com要求退款
- 要求他們的付款方式發(fā)行人退款
當(dāng)App Store處理退款時(shí),App Store服務(wù)器會(huì)通過(guò)您配置的URL向您的服務(wù)器發(fā)送REFUND通知。 您的服務(wù)器必須使用200響應(yīng)碼來(lái)響應(yīng)該帖子。 要啟用通知,請(qǐng)參閱Enabling App Store Server Notifications。
REFUND通知僅適用于消耗性,非消耗性和非續(xù)訂性訂閱。 要檢測(cè)自動(dòng)續(xù)訂訂閱的退款,請(qǐng)參閱檢測(cè)退款。
解釋和處理退款通知
您的服務(wù)器負(fù)責(zé)解析和解釋來(lái)自App Store Server的所有通知。 對(duì)于“退款”通知,請(qǐng)從響應(yīng)中標(biāo)識(shí)特定交易,產(chǎn)品ID和相關(guān)日期:
- 通過(guò)檢查
purchase_date以選擇最新交易,從而在unified_receipt.latest_receipt_info中找到product_id的最新交易。 -
App Store發(fā)出退款的日期在此交易的cancel_date_ms字段中。
有關(guān)響應(yīng)的更多信息,請(qǐng)參閱App Store Server Notifications。
當(dāng)您收到退款通知時(shí),您有責(zé)任為每筆退款交易存儲(chǔ),監(jiān)控并采取適當(dāng)?shù)拇胧?例如,您可以構(gòu)建自己的游戲內(nèi)貨幣平衡邏輯,該邏輯通過(guò)將通知鏈接到玩家?guī)艋驎?huì)話來(lái)處理退款交易。
重要信息
當(dāng)您使用包含退款交易的收據(jù)來(lái)調(diào)用verifyReceipt端點(diǎn)時(shí),JSON響應(yīng)中不存在退款交易,自動(dòng)續(xù)訂訂閱除外。
通過(guò)在應(yīng)用程序中顯示上下文消息通知客戶有關(guān)退款所采取的任何措施。
識(shí)別退款濫用
通過(guò)將“退款”通知映射到服務(wù)器上的玩家?guī)?,減少退款濫用并識(shí)別重復(fù)的退款購(gòu)買。 監(jiān)視和分析您的數(shù)據(jù)以識(shí)別可疑的退款活動(dòng)。
如果您跨多個(gè)平臺(tái)提供內(nèi)容,請(qǐng)?jiān)诜?wù)器上更新用戶帳戶的余額。 使用App Store Server通知來(lái)獲取影響客戶的交易的近實(shí)時(shí)狀態(tài)更新。
恢復(fù)購(gòu)買的產(chǎn)品
為用戶提供可以在您的應(yīng)用中恢復(fù)購(gòu)買的功能,以維持對(duì)購(gòu)買內(nèi)容的訪問(wèn)權(quán)限。
總覽
用戶有時(shí)需要還原購(gòu)買的內(nèi)容,例如當(dāng)他們升級(jí)到新手機(jī)時(shí)。 在您的應(yīng)用程序中包含一些機(jī)制(例如“還原購(gòu)買”按鈕),以使他們還原購(gòu)買的內(nèi)容。
重要信息
不要自動(dòng)恢復(fù)購(gòu)買,特別是在啟動(dòng)應(yīng)用程序時(shí)。 恢復(fù)購(gòu)買會(huì)提示用戶輸入App Store憑據(jù),這會(huì)中斷您的應(yīng)用流程。
在大多數(shù)情況下,您只需要刷新應(yīng)用收據(jù)并交付收據(jù)上列出的產(chǎn)品即可。刷新后的收據(jù)包含用戶從該應(yīng)用商店帳戶登錄的任何設(shè)備上在該應(yīng)用中購(gòu)買商品的記錄。但是,在給定情況下,應(yīng)用可能需要其他方法:
- 您使用Apple托管的內(nèi)容-恢復(fù)已完成的交易,為您的應(yīng)用程序提供下載內(nèi)容所使用的交易對(duì)象。
- 您需要在沒(méi)有收據(jù)的設(shè)備上支持您的應(yīng)用程序-還原已完成的交易。
- 您的應(yīng)用使用非續(xù)訂訂閱-您的應(yīng)用負(fù)責(zé)恢復(fù)過(guò)程。
刷新收據(jù)不會(huì)創(chuàng)建新的交易;它會(huì)從App Store請(qǐng)求收據(jù)的最新副本。僅刷新一次收據(jù);連續(xù)刷新多次具有相同的結(jié)果。
恢復(fù)已完成的事務(wù)會(huì)為以前完成的每個(gè)事務(wù)創(chuàng)建一個(gè)新事務(wù),實(shí)質(zhì)上是為您的事務(wù)隊(duì)列觀察者重放歷史記錄。您的應(yīng)用程序保持其自身狀態(tài),以跟蹤其為何還原已完成的交易以及如何處理它們。多次還原會(huì)為每個(gè)完成的事務(wù)創(chuàng)建多個(gè)還原的事務(wù)。
注意
如果用戶嘗試購(gòu)買已經(jīng)購(gòu)買的產(chǎn)品,則App Store會(huì)創(chuàng)建常規(guī)交易而不是還原交易,但是不會(huì)再次向用戶收費(fèi)。 與原始交易一樣,解鎖這些交易的內(nèi)容。
為用戶提供對(duì)再次下載的內(nèi)容的適當(dāng)級(jí)別的控制。 例如,不要同時(shí)自動(dòng)下載三年的日?qǐng)?bào)或數(shù)百兆的游戲級(jí)別。
刷新應(yīng)用收據(jù)
創(chuàng)建收據(jù)刷新請(qǐng)求,設(shè)置委托,然后啟動(dòng)請(qǐng)求。 該請(qǐng)求支持可選屬性,用于在測(cè)試期間獲取各種狀態(tài)的收據(jù),例如過(guò)期的收據(jù)。 有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn)SKReceiptRefreshRequest的 initWithReceiptProperties:方法。
request = [[SKReceiptRefreshRequest alloc] init];
request.delegate = self;
[request start];
刷新應(yīng)用收據(jù)后,請(qǐng)對(duì)其進(jìn)行檢查并交付添加到收據(jù)中的所有產(chǎn)品。
恢復(fù)已完成的交易
您的應(yīng)用程序通過(guò)調(diào)用SKPaymentQueue的restoreCompletedTransactions方法開(kāi)始恢復(fù)已完成的交易。 此調(diào)用將請(qǐng)求發(fā)送到App Store,以恢復(fù)您所有應(yīng)用程序已完成的交易。 如果您的應(yīng)用為其付款請(qǐng)求的applicationUsername屬性設(shè)置了一個(gè)值,則在還原交易時(shí),可以使用restoreCompletedTransactionsWithApplicationUsername:方法提供相同的信息。
App Store會(huì)生成一個(gè)新交易,以還原每個(gè)先前完成的交易。 恢復(fù)的事務(wù)引用原始事務(wù):SKPaymentTransaction的實(shí)例具有originalTransaction屬性,而收據(jù)中的條目具有original_transaction_id字段值。
對(duì)于恢復(fù)的購(gòu)買,日期字段的含義略有不同。 有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn)responseBody.Receipt.In_app中的purchase_date和original_purchase_date字段。
StoreKit為每個(gè)已還原的事務(wù)調(diào)用狀態(tài)為SKPaymentTransactionStateRestored的事務(wù)隊(duì)列觀察器,如處理事務(wù)中所述。 此時(shí)要執(zhí)行的操作取決于應(yīng)用程序的設(shè)計(jì)。
如果您的應(yīng)用使用應(yīng)用收據(jù)并且沒(méi)有Apple托管的內(nèi)容,則不需要此代碼,因?yàn)槟膽?yīng)用不會(huì)還原已完成的交易。 立即完成所有還原的事務(wù)。
如果您的應(yīng)用使用應(yīng)用收據(jù)并且具有Apple托管的內(nèi)容,請(qǐng)讓用戶選擇要還原的產(chǎn)品,然后再開(kāi)始還原過(guò)程。 在還原過(guò)程中,請(qǐng)先下載用戶選擇的內(nèi)容,然后再完成這些事務(wù),并立即完成任何其他事務(wù)。
NSMutableArray *productIDsToRestore = <# From the user #>;
SKPaymentTransaction *transaction = <# Current transaction #>;
if ([productIDsToRestore containsObject:transaction.transactionIdentifier]) {
// Re-download the Apple-hosted content
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
如果您的應(yīng)用不使用應(yīng)用收據(jù),則會(huì)在還原所有已完成的交易時(shí)對(duì)其進(jìn)行檢查。 它使用與原始購(gòu)買邏輯相似的代碼路徑來(lái)使產(chǎn)品可用,然后完成交易。 具有多個(gè)產(chǎn)品(尤其是具有相關(guān)內(nèi)容的產(chǎn)品)的應(yīng)用程序使用戶可以選擇要還原的產(chǎn)品,而不是還原所有內(nèi)容。 這些應(yīng)用程序通過(guò)立即完成而不還原它們來(lái)跟蹤要還原的已完成事務(wù)以及要忽略的事務(wù)。