關(guān)于iOS的應(yīng)用內(nèi)支付(IAP), 我曾在項(xiàng)目開(kāi)發(fā)過(guò)程中接觸過(guò)兩次,本篇文章將詳細(xì)介紹一下整個(gè)的開(kāi)發(fā)流程, 我將會(huì)介紹IAP商品支付和驗(yàn)證流程
適用場(chǎng)景
IAP大量應(yīng)用于iOS系統(tǒng)中的游戲當(dāng)中,像一些游戲中的金幣肌厨,寶石的交易都是采用IAP支付鼻由。蘋(píng)果官方規(guī)定,當(dāng)APP涉及到虛擬貨幣的交易時(shí),只能使用IAP進(jìn)行支付,否則會(huì)在APP審核過(guò)程中被拒絕。而我們?cè)S多生活應(yīng)用使用微信支付论泛、支付寶支付卻仍可通過(guò)審核,是因?yàn)槎加糜诂F(xiàn)實(shí)物品的交易蛹屿。此外蘋(píng)果會(huì)收取虛擬貨幣盈利的百分之30屁奏。
類(lèi)型說(shuō)明
- 消耗型商品
- 非消耗型商品
- 非續(xù)期訂閱
- 自動(dòng)續(xù)期訂閱
消耗型商品
顧名思義, 可以消耗使用的商品, 比如游戲中的金幣, 鉆石等, 可以用來(lái)購(gòu)買(mǎi)應(yīng)用內(nèi)虛擬物品的貨幣
非消耗型商品
無(wú)法被消耗的商品,比如一些教育型APP中的課程, 再比如一些賽車(chē)游戲中的賽道, 這類(lèi)商品需要在審核添加恢復(fù)購(gòu)買(mǎi)按鈕, 用于用戶(hù)購(gòu)買(mǎi)過(guò)后再誤刪除或其他原因卸載APP后的恢復(fù)流程, 否則提交審核會(huì)被拒絕
非續(xù)期訂閱
此類(lèi)商品與消耗型商品類(lèi)似, 比如一個(gè)月的會(huì)員, 一個(gè)季度的會(huì)員等, 與消耗型商品的差異在于, 這類(lèi)商品在驗(yàn)證憑證時(shí)需要傳遞共享秘鑰
自動(dòng)續(xù)期訂閱
此類(lèi)商品網(wǎng)上介紹比較少, 這類(lèi)商品和其他商品的流程也有些許不同, 應(yīng)用比如視頻APP中的連續(xù)包月會(huì)員, 此類(lèi)商品到期會(huì)自動(dòng)扣費(fèi), 服務(wù)器的驗(yàn)證邏輯也會(huì)有所不同
以上為商品類(lèi)型的介紹說(shuō)明, 我會(huì)在下文中就這支付流程做詳細(xì)介紹
準(zhǔn)備工作
iTunes Connetct后臺(tái)創(chuàng)建商品, 建立沙盒測(cè)試賬號(hào)
整個(gè)IAP測(cè)試階段, 只能用沙盒測(cè)試賬號(hào)測(cè)試IAP支付, 且憑證驗(yàn)證只能發(fā)送至測(cè)試驗(yàn)證環(huán)境
由于本部分較為簡(jiǎn)單, 本文不做具體介紹, 直接在iTunes Connetct后臺(tái)創(chuàng)建按照說(shuō)明創(chuàng)建即可
需要注意的是如果應(yīng)用是第一次進(jìn)行IAP開(kāi)發(fā), 首先要完善蘋(píng)果商店內(nèi)的個(gè)人信息 (銀行卡信息错负、 稅務(wù)相關(guān)信息)才能創(chuàng)建相關(guān)商品, 而且需要在下一個(gè)發(fā)布版本中審核商品, 如果曾經(jīng)審核過(guò)IAP開(kāi)發(fā), 可直接在后臺(tái)進(jìn)行新增商品審核
支付驗(yàn)證流程
首先簡(jiǎn)單說(shuō)明一下整個(gè)流程, 此處以我們APP開(kāi)發(fā)為例, 說(shuō)明客戶(hù)端進(jìn)行支付, 服務(wù)器端進(jìn)行驗(yàn)證的邏輯, 保證整個(gè)IAP支付的安全性
整個(gè)流程大體為
- 客戶(hù)端請(qǐng)求商品訂單
- 獲取IAP商品id
- IAP商品查詢(xún)
- 用戶(hù)支付
- 客戶(hù)端發(fā)送訂單號(hào)+支付憑證到服務(wù)器
- 服務(wù)器驗(yàn)證憑證是否合法
- 返回結(jié)果到客戶(hù)端
- 客戶(hù)端業(yè)務(wù)邏輯處理
下面我會(huì)針對(duì)非續(xù)期訂閱以及自動(dòng)續(xù)期訂閱做詳細(xì)說(shuō)明, 消耗型商品和非續(xù)期訂閱類(lèi)似且相對(duì)簡(jiǎn)單
非續(xù)期訂閱支付流程(以一個(gè)月會(huì)員為例)
首先向服務(wù)器下單, 攜帶后臺(tái)創(chuàng)建的商品id, 向自己服務(wù)器下單, 在成功回調(diào)中獲取單號(hào)并存儲(chǔ)
/**
下vip訂單
@param params 參數(shù) @"item_id" : @(itemID),
@param success 成功回調(diào)
@param fail 失敗回調(diào)
*/
- (void)makeVipOrderWithParams:(NSDictionary *)params
success:(RequestOrderSuccess)success
fail:(RequestOrderfailBlock)fail;
下單成功后, 對(duì)商品進(jìn)行支付, 這里支付過(guò)程網(wǎng)絡(luò)上demo較多, 不做說(shuō)明, 具體參考github上的工具類(lèi) IAPHelper
/**
購(gòu)買(mǎi)對(duì)應(yīng)商品identifier后的回調(diào)
@param identifier 商品identifier
@param completion 回調(diào)
*/
- (void)payProductsWithIdentifier:(NSString *)identifier
completion:(IAPbuyProductCompleteResponseBlock)completion;
當(dāng)用戶(hù)支付成功后, 在回調(diào)中獲取到憑證, 以憑證 訂單號(hào) 用戶(hù)uid等為參數(shù)請(qǐng)求服務(wù)器, 服務(wù)器向蘋(píng)果服務(wù)器驗(yàn)證憑證是否支付
/**
查詢(xún)vipIAP支付結(jié)果
@param orderID 訂單ID
@param receipt 憑證
@param uid 用戶(hù)uid
@param success 成功回調(diào)
@param fail 失敗回調(diào)
*/
- (void)requestIAPResultWithOrderID:(long long)orderID
receipt:(NSString *)receipt
uid:(NSString *)uid
success:(RequestQuerySuccess)success
fail:(RequestQueryFail)fail;
此處, 服務(wù)器驗(yàn)證憑證時(shí), 因?yàn)闉榉抢m(xù)期訂閱支付, 需攜帶上文中的共享秘鑰和憑證進(jìn)行驗(yàn)證, 蘋(píng)果驗(yàn)證結(jié)果會(huì)返回訂單的詳細(xì)信息, 服務(wù)器根據(jù)返回信息來(lái)進(jìn)行業(yè)務(wù)處理
客戶(hù)端在收到驗(yàn)證結(jié)果后, 刷新界面即完成整個(gè)流程
自動(dòng)續(xù)期訂閱支付流程(連續(xù)包月)
支付流程和上方一致, 不同的特殊處理是, 服務(wù)器在驗(yàn)證成功后會(huì)儲(chǔ)存用戶(hù)的這個(gè)憑證,當(dāng)用戶(hù)此階段會(huì)員到期時(shí), 再次查詢(xún)憑證, 當(dāng)查詢(xún)憑證有效期發(fā)生了變化, 根據(jù)具體請(qǐng)求結(jié)果, 為用戶(hù)延長(zhǎng)一個(gè)月的會(huì)員, 否則, 到期取消會(huì)員
丟單處理
由于IAP服務(wù)器無(wú)法保證質(zhì)量, 或者自己服務(wù)器驗(yàn)證憑證出現(xiàn)問(wèn)題時(shí), 可能會(huì)出現(xiàn)丟單(用戶(hù)付費(fèi)成功, 但是憑證無(wú)法成功向自己服務(wù)器驗(yàn)證)的情況, 對(duì)于這種情況, 我們可以這樣處理
在用戶(hù)下單成功后, 儲(chǔ)存訂單&uid&憑證
/**
存儲(chǔ) 訂單&uid&憑證
@param orderID 訂單
@param uid 用戶(hù)uid
@param receipt 憑證
@param saveKey 儲(chǔ)存key
*/
- (void)saveOrderReceiptWithOrderID:(long long)orderID
uid:(NSString *)uid
receipt:(NSString *)receipt
saveKey:(NSString *)saveKey;
在用戶(hù)向服務(wù)器驗(yàn)證成功后或者非網(wǎng)絡(luò)原因造成的失敗后, 刪除此條記錄,
/**
刪除 訂單&憑證
@param orderID 訂單
@param receipt 憑證
@param saveKey 儲(chǔ)存key
*/
- (void)removeOrderReceiptWithOrderID:(long long)orderID
receipt:(NSString *)receipt
saveKey:(NSString *)saveKey;
這樣如果由于網(wǎng)絡(luò)問(wèn)題或者服務(wù)器出現(xiàn)問(wèn)題造成丟單, 我們可以在下一次用戶(hù)啟動(dòng)APP再次去進(jìn)行驗(yàn)證這筆訂單, 重復(fù)上面流程
/**
核對(duì)支付成功但是驗(yàn)證失敗的訂單
*/
- (void)checkLocalLostVipOrder;
偽造訂單處理
IAP支付難免會(huì)出現(xiàn)一些偽造憑證的驗(yàn)證, 對(duì)此, 服務(wù)器端對(duì)于憑證的驗(yàn)證一定要十分謹(jǐn)慎, 我們APP曾收到過(guò)偽造憑證的驗(yàn)證, 可以參考一下驗(yàn)證:
- 核對(duì)憑證驗(yàn)證后itemID
- 核對(duì)憑證是否為正式環(huán)境的憑證
- 核對(duì)憑證的有效時(shí)間
- 對(duì)于越獄用戶(hù)的處理, 之前做消耗品IAP支付的時(shí)候, 對(duì)于越獄用戶(hù)由于有一些IAP插件的存在, 我們選擇對(duì)于越獄用戶(hù)直接進(jìn)行微信支付, 隨著后來(lái)判斷邏輯的增加, 對(duì)于越獄用戶(hù)也啟用了IAP支付
審核需知
IAP審核時(shí), 需要提供沙盒測(cè)試賬號(hào)和一個(gè)APP的測(cè)試賬號(hào), 在審核過(guò)程時(shí), 我們整個(gè)流程都已經(jīng)切換為正式環(huán)境, 但審核人員仍然使用測(cè)試憑證去進(jìn)行驗(yàn)證, 我們服務(wù)器需要在審核階段, 對(duì)于此uid的憑證仍然去測(cè)試驗(yàn)證接口去驗(yàn)證, 否則會(huì)被拒絕通過(guò)
具體審核問(wèn)題詳見(jiàn)我寫(xiě)的這篇文章應(yīng)用內(nèi)支付自動(dòng)續(xù)費(fèi) 連續(xù)包月 審核注意問(wèn)題