iOS內(nèi)購(gòu)也算是老生常談的一個(gè)技術(shù)點(diǎn)了, 但是對(duì)于我這種剛剛接觸的人來(lái)講, 還是踩了不少的坑, 這幾天剛搞完, 來(lái)簡(jiǎn)單的總結(jié)一下內(nèi)購(gòu)的流程.
暫且不說(shuō)申請(qǐng)稅務(wù), 添加銀行卡 這些步驟, 直接進(jìn)入代碼.
首先了解一下iOS IAP內(nèi)購(gòu)的總的流程, 如下圖:
內(nèi)購(gòu)大致的流程圖
那么根據(jù)上圖所述6點(diǎn)進(jìn)行 代碼闡述.
- 在
Appdelegate
中應(yīng)用程序啟動(dòng)時(shí) 設(shè)置transectionObserver:
SKPaymentQueue.default().add(self)
- 根據(jù)后端返回的產(chǎn)品id 或者 產(chǎn)品id 的集合, 進(jìn)行產(chǎn)品的查詢.
@discardableResult
public func requestProductsInfo(productIds: [String]) -> SKProductsRequest {
let request = SKProductsRequest(productIdentifiers: Set(productIds))
request.delegate = self
request.start()
return request
}
- 在
SKProductsRequestDelegate
代理方法中 可以找到接收到蘋(píng)果服務(wù)器返回的查詢產(chǎn)品方法
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let products = response.products
print("products : \(products)")
/// 拿到數(shù)據(jù)集合 進(jìn)行UI界面的更新
#warning UpdateUI
}
4, 5 . 如果后端有訂單系統(tǒng)的話, 那么當(dāng)點(diǎn)擊某一款產(chǎn)品時(shí), 要向后端請(qǐng)求生成訂單號(hào). 沒(méi)有訂單系統(tǒng), 不要訂單號(hào)也是可以的. 然后根據(jù)product創(chuàng)建payment實(shí)例, 并將其放入paymentQueue中, 發(fā)起付款請(qǐng)求:
public func purchaseProduct(product: SKProduct, orderId: String?) {
let payment = SKMutablePayment(product: product)
//設(shè)置訂單號(hào)
if let orderId = orderId {
payment.applicationUsername = orderId
}
SKPaymentQueue.default().add(payment)
}
- . 收到付款的回調(diào), 回調(diào)中會(huì)將transection 帶出來(lái), 可以根據(jù)transection.transactionState 來(lái)判斷交易的狀態(tài), 根據(jù)交易狀態(tài)來(lái)進(jìn)行不同的處理, 要記住, 處理結(jié)束之后 一定要調(diào)用結(jié)束交易的代碼:
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
// print("有交易更新")
switch transaction.transactionState {
case .purchasing:
printLog("商品添加進(jìn)列表")
case .purchased:
printLog("交易完成")
case .failed:
if let swiftError = transaction.error {
let error = swiftError as NSError
if error.code == SKError.Code.paymentCancelled.rawValue {
// printLog("交易取消")
} else {
// printLog("交易失敗")
}
}
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
// printLog("已經(jīng)購(gòu)買(mǎi)過(guò)商品")
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred:
break
@unknown default:
break
}
}
}
要注意:
- 發(fā)起productRequest之前要判斷當(dāng)前設(shè)備或者AppId是否支持IAP:
func canMakePurchases() -> Bool {
return SKPaymentQueue.canMakePayments()
}
-
如果當(dāng)前產(chǎn)品為非消耗類型的, 那么只能購(gòu)買(mǎi)一次, 再次購(gòu)買(mǎi)的話就會(huì)出現(xiàn)以下圖片:
非消耗類型產(chǎn)品, 多次購(gòu)買(mǎi)
如果你的產(chǎn)品中有非消耗類型的產(chǎn)品, 那么需要判斷當(dāng)前產(chǎn)品是否已經(jīng)購(gòu)買(mǎi)過(guò), 如果購(gòu)買(mǎi)過(guò), 蘋(píng)果要求要有一個(gè)恢復(fù)購(gòu)買(mǎi)按鈕, 用來(lái)恢復(fù)當(dāng)前appid的權(quán)益, 否則會(huì)被guildline 3.1.1 拒絕掉. 可通過(guò)下面的步驟查詢到當(dāng)前已經(jīng)購(gòu)買(mǎi)過(guò)的產(chǎn)品:
1. 在進(jìn)入商品列表或者購(gòu)買(mǎi)頁(yè)面的時(shí)候 向PaymentQueue中添加存儲(chǔ)已經(jīng)完成交易的操作: SKPaymentQueue.default().restoreCompletedTransactions()
2. 接下來(lái)會(huì)出發(fā)SKPaymentTransactionObserver中的完成交易的代理方法, 在代理方法中 可以將已經(jīng)完成交易的產(chǎn)品 存儲(chǔ)起來(lái):
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
// 處理已經(jīng)購(gòu)買(mǎi)過(guò)的商品.
for transaction in queue.transactions {
let productId = transaction.payment.productIdentifier
printLog("已經(jīng)購(gòu)買(mǎi)過(guò)的產(chǎn)品id = \(productId)")
/// 可以將產(chǎn)品或者產(chǎn)品id放入集合中 存儲(chǔ)起來(lái).
}
}
如果產(chǎn)品類型屬于非續(xù)期訂閱, 那么以上方法是查不出來(lái)的. 因?yàn)榉抢m(xù)期訂閱 是可以無(wú)限次購(gòu)買(mǎi)的產(chǎn)品, 產(chǎn)品有有效期, 買(mǎi)的越多, 有效期越長(zhǎng), 例如為期一年的已歸檔文章目錄訂閱.