iOS內(nèi)購總結(jié)(含漏單嘁捷、串單造成、流程、測試點(diǎn))

這兩天因為appstore審核被拒的原因雄嚣,不得不把微信支付寶等支付替換成蘋果內(nèi)購晒屎。經(jīng)過兩天的研究和學(xué)習(xí)我發(fā)現(xiàn)了內(nèi)購的好多個坑,我在這里做了一個總結(jié)缓升,希望能對大家有所幫助鼓鲁,有不對的地方還請大家無情指出并嘲諷之。最后還有我最終的解決方案分享給大家港谊。

一骇吭、內(nèi)購的坑

  1. app被卸載后使用SKReceiptRefreshReques重新獲取內(nèi)購票據(jù)。
{
 SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
       receiptRefreshRequest.delegate = self;
       [receiptRefreshRequest start];
       return;
}

#pragma mark - SKRequestDelegate
- (void)requestDidFinish:(SKRequest *)request{
    // 刷新成功票據(jù)的代理
}

這個的坑在于重新獲取的票據(jù)雖然客戶端能驗證成功歧寺,但是驗證成功的信息里面有一個關(guān)鍵字段“in_app”為空數(shù)組燥狰。把這個票據(jù)傳給后臺后是不能驗證通過的,所以就不能為用戶充值成功斜筐,所以這個方法大家慎用龙致。

  1. finishTransaction
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction]
    finishTransaction是去告訴蘋果這次交易已經(jīng)結(jié)束,如果不執(zhí)行或者執(zhí)行失斍炅础(為什么說會執(zhí)行失敗呢目代?因為這個方法是異步網(wǎng)絡(luò)請求,網(wǎng)絡(luò)不好的時候就會失斷土贰)下次用戶購買同樣的商品的時候購買成功后會提示“您已經(jīng)免費(fèi)恢復(fù)”的字樣榛了。我看了好多博客,好多博主都說一定要在給用戶充值完以后在執(zhí)行這個方法煞抬,我這里有不同意見霜大,因為如果網(wǎng)絡(luò)不好充值失敗了,沒有執(zhí)行到這個方法的話革答,不僅有上面的問題僧诚,因為沒執(zhí)行成功這個方法的話,下次進(jìn)入app或者進(jìn)入充值界面(取決你在哪里[[SKPaymentQueue defaultQueue] addTransactionObserver:self])的時候就會自動回調(diào)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions,這讓我的充值流程變得非常不可控也不可見蝗碎。下面的我的解決方案會提到怎么解決這個問題湖笨。

3.異常訂單存入數(shù)據(jù)庫
數(shù)據(jù)庫是會隨著app卸載而刪除的,我開始沒有想到這一點(diǎn)蹦骑,后來測試發(fā)現(xiàn)了才把這個方案取消了慈省,費(fèi)時又費(fèi)力,希望大家不要踩坑 。

二边败、我的解決方案

內(nèi)購的步驟

不要把問題想得那么復(fù)雜袱衷,大象裝進(jìn)冰箱需要三步呢,可內(nèi)購在我看來就兩步笑窜。

  1. 用戶付錢給蘋果完畢
  2. 客戶端發(fā)送請求給服務(wù)器驗證票據(jù)進(jìn)行充值成功致燥。
思考?
  1. 什么時候會漏單呢排截?很簡單嫌蚤,第一步完成,第二步?jīng)]完成断傲。
  2. 什么時候第一步完成呢脱吱?因該是在paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions里面transaction.transactionState = SKPaymentTransactionStatePurchased
  3. 什么時候第二步?jīng)]完成认罩?不管什么原因箱蝠,肯定沒來到充值接口成功的回調(diào)里面。
開始解決
  1. 用戶付錢給蘋果完畢 使用鑰匙串(鑰匙串不會隨著app卸載而刪除垦垂,會永久性的存儲宦搬,除非用戶換手機(jī),我這里使用的是SAMKeychain)存儲用戶付款的憑證(receipt)和交易id(transactionId)劫拗。儲存憑證在付款成功的回調(diào)里面间校,存儲的時候要區(qū)分用戶,所以監(jiān)聽蘋果隊列的代碼[[SKPaymentQueue defaultQueue] addTransactionObserver:self]要寫在登錄之后杨幼,否則無法正確存儲憑證。

  2. 充值接口成功的回調(diào)里面刪除用戶付款的憑證(receipt)和交易id(transactionId)聂渊。

  3. 由于各種原因?qū)е鲁渲凳÷﹩瘟嗽趺崔k差购?在用戶再次想充值的時候,判斷鑰匙串中有沒有存儲的數(shù)據(jù)汉嗽,如果有的話給一個攔截的操作欲逃,我這邊是給一個彈窗,提示用戶處理異常訂單饼暑。這里很關(guān)鍵稳析,這個操作保證了用戶在進(jìn)行內(nèi)購操作的時候,最多只能有一個異常訂單所以我們不擔(dān)心有串單的風(fēng)向弓叛,也不會有大量漏單的情況出現(xiàn)彰居。點(diǎn)擊彈窗的按鈕“立即處理異常訂單”會從鑰匙串中獲取之前存儲的憑證和交易id繼續(xù)向公司的服務(wù)器發(fā)起驗證并充值,如果充值成功刪除憑證和交易id撰筷。如果失敗了陈惰,不用進(jìn)行任何操作。當(dāng)用戶點(diǎn)擊充值的時候還是會有一個彈窗提示毕籽。

  4. finishTransaction的調(diào)用抬闯,上面的坑說到了這個方法井辆。所以我們?yōu)榱四軌蜃约嚎刂瞥渲盗鞒蹋瑪[脫蘋果的控制溶握,我們不用在乎他什么時候調(diào)用杯缺,但一定盡可能保證他調(diào)用完成。我是怎么做的呢睡榆?

  • 用戶付錢給蘋果完畢調(diào)用一次萍肆。
  • 充值成功調(diào)用一次。
  • 充值失敗調(diào)用一次肉微。
  • 充值前把當(dāng)前所有的transaction進(jìn)行finish操作匾鸥。

如果這樣還不能避免由于網(wǎng)絡(luò)等原因沒有執(zhí)行完[[SKPaymentQueue defaultQueue] finishTransaction: transaction]怎么辦呢?這我也做了處理碉纳。我加了一個屬性isNewtranstion,意思就是這個交易是新的嗎勿负,或者換句話說,這個transtion(交易)是從buyProduct:(SKProduct *)productIdentifier onCompletion:(IAPbuyProductCompleteResponseBlock)completion這個方法中來的嗎劳曹?
所以我在這個方法中為這個字段進(jìn)行了賦值操作奴愉。

- (void)buyProduct:(SKProduct *)productIdentifier onCompletion:(IAPbuyProductCompleteResponseBlock)completion {
    SKPayment *payment = [SKPayment paymentWithProduct:productIdentifier];
    if ([SKPaymentQueue defaultQueue]) {
        [[SKPaymentQueue defaultQueue] addPayment:payment];
        [CHBUserDataCenterModel sharedInstance].isNewtranstion = YES;
    }
}

如果是舊的交易,就是沒有被finish的交易的話我就再finish一下(我就不信干不了你L酢6稹!)

if (![CHBUserDataCenterModel sharedInstance].isnewtranstion) {
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        return;
    }

但是這么做會引發(fā)一個問題蜕劝,用戶付錢給蘋果完畢之前殺死app檀头,但是因為是發(fā)送給蘋果的請求還是會彈出您的購買已完成,就無法響應(yīng)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions中將憑證和交易id保存到鑰匙串的操作了岖沛,所以要把保存放到第一步暑始,而且要加判斷,防止憑證或者交易id為空的transtion進(jìn)入回調(diào)壞事婴削。

 // 獲取小票
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據(jù)
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    NSString *payload = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    NSString* payload1 = [SAMKeychain passwordForService:receiptservice account:[CHBUserDataCenterModel sharedInstance].passportId];
    NSString* transactionId1 = [SAMKeychain passwordForService:transactionIdservice account:[CHBUserDataCenterModel sharedInstance].passportId];
    if (payload1 == nil) {
        BOOL issaved = [SAMKeychain setPassword:payload forService:receiptservice account:[CHBUserDataCenterModel sharedInstance].passportId];
        while (!issaved) {
            [SAMKeychain setPassword:payload forService:receiptservice account:[CHBUserDataCenterModel sharedInstance].passportId];
        }
    }
    
    if (transactionId1 == nil){
        BOOL issaved1 = [SAMKeychain setPassword:transaction.transactionIdentifier forService:transactionIdservice account:[CHBUserDataCenterModel sharedInstance].passportId];
        while (!issaved1) {
            [SAMKeychain setPassword:transaction.transactionIdentifier forService:transactionIdservice account:[CHBUserDataCenterModel sharedInstance].passportId];
        }
    }
  1. 還有一個問題:調(diào)用充值接口的時候廊镜,app被殺死了。
    這種情況的話會產(chǎn)生一種現(xiàn)象唉俗,post請求發(fā)出了嗤朴,后臺接收到了,充值成功了虫溜,但是客戶端收不到響應(yīng)了雹姊,也不能執(zhí)行充值成功后的代碼了。這時需要后臺提供一個接口:驗證鑰匙串中的憑證和交易id充值成功過沒有衡楞,如果充值成功過容为,就刪除鑰匙串中內(nèi)容,避免用戶充值的時候有個提示框攔截操作,如果沒有充值成功過坎背,那就是多慮了替劈,還是按之前的流程走。
三得滤、相關(guān)思路
  1. 內(nèi)購流程圖


    流程圖.png
  2. 內(nèi)購測試點(diǎn)


    測試點(diǎn).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載陨献,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末懂更,一起剝皮案震驚了整個濱河市眨业,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沮协,老刑警劉巖龄捡,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慷暂,居然都是意外死亡聘殖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門行瑞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奸腺,“玉大人,你說我怎么就攤上這事血久⊥徽眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵氧吐,是天一觀的道長讹蘑。 經(jīng)常有香客問我,道長筑舅,這世上最難降的妖魔是什么座慰? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮豁翎,結(jié)果婚禮上角骤,老公的妹妹穿的比我還像新娘隅忿。我一直安慰自己心剥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布背桐。 她就那樣靜靜地躺著优烧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪链峭。 梳的紋絲不亂的頭發(fā)上畦娄,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼熙卡。 笑死杖刷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驳癌。 我是一名探鬼主播滑燃,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颓鲜!你這毒婦竟也來了表窘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甜滨,失蹤者是張志新(化名)和其女友劉穎乐严,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衣摩,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昂验,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了昭娩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛篙。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栏渺,靈堂內(nèi)的尸體忽然破棺而出呛梆,到底是詐尸還是另有隱情,我是刑警寧澤磕诊,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布填物,位于F島的核電站,受9級特大地震影響霎终,放射性物質(zhì)發(fā)生泄漏滞磺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一莱褒、第九天 我趴在偏房一處隱蔽的房頂上張望击困。 院中可真熱鬧,春花似錦广凸、人聲如沸阅茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脸哀。三九已至,卻和暖如春扭吁,著一層夾襖步出監(jiān)牢的瞬間撞蜂,已是汗流浹背盲镶。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝌诡,地道東北人溉贿。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像浦旱,于是被迫代替她去往敵國和親顽照。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355