前言
說起內購,其實挺令開發者厭煩的,原因呢,先不說漏單的問題,首先蘋果要扣除30%的銷售額哦,可恨不?(我覺得可恨),有些想辦法先隱藏掉第三方支付(支付寶、微信等),等項目上線了,再跳過內購使用第三方支付,emmmm.......這個方法確實不錯,但是如果被蘋果發現了,APP內虛擬產品調用第三方支付,那好吧,直接下架吧(或許沒這么慘,但會慘不忍睹),不要說發現不了,會有人舉報哦(別問我怎么知道的);其次就是漏單問題的處理,這一直是個問題,我的項目里雖然做了處理,但是還是會避免不了漏單的,只是把漏單的幾率降到了很小,以確保我們維護,給大家分享下內購及漏單的處理。
內購集成
內購集成並不難,這里我用了一個Git上封裝好的(IAPHelper),自己封裝也簡單(不想造那么多輪子了),封裝的話,建議結合單例封裝,充值驗證都在單例里面進行;當然也可以不封裝直接就用的,這里不多說了;建議最好根據服務器的驗證方式與邏輯自己寫個單利工具。
支付邏輯
1.臨時單號
首先根據內購商品ID(此商品ID是在蘋果后台建好的內購商品)、用戶信息(后台要求),傳給服務器獲取一個臨時單號,然后先將該臨時單號保存到一個變量里。在此之前,需要用數據持久化,對用戶最后一次選擇的內功商品ID進行永久儲存,就算用戶付款成功卻充值失敗了,即使App卸載了,也可以拿到最后一次請求的內購商品ID 。
///獲取充值臨時單號 - (void)iapGetTemOrderIdWithProductId:(NSString *)productId{ self.productId = productId; [SVProgressHUD showWithStatus:@"請稍后..." ]; NSString *urlString = @""; [HttpTools getHttpRequestURL:urlString RequestSuccess:^(id repoes, NSURLSessionDataTask *task) { [SVProgressHUD dismiss]; NSDictionary *dicTem = [HttpTools respoesToDic:repoes]; if ([dicTem[@"code"] integerValue] == 1) { ///保存臨時單號 self.temporaryOrderId = dicTem[@"data"]; ///發起內購支付 [self iapStartRecharge]; } else{ [SVProgressHUD showErrorWithStatus:dicTem[@"errmsg"]]; [self errorPost:nil]; } } RequestFaile:^(NSError *error) { [SVProgressHUD showErrorWithStatus:[HttpTools error:error]]; [self errorPost:nil]; }]; }
2.蘋果充值
通過商品ID調取蘋果內購支付,蘋果充值成功后,在返回成功的方法里,首先將上一步中的臨時單號、用戶信息(這里我取userId)、蘋果充值成功返回的data,三個參數一起存入本地(我采用數據庫存儲)后,然后驗證服務器充值(如果蘋果充值驗證失敗,不必做任何操作)。
///發起內購支付 - (void)iapStartRecharge{ [SVProgressHUD showWithStatus:@"請稍后..."]; NSSet* dataSet = [[NSSet alloc] initWithObjects:self.productId, nil]; [IAPShare sharedHelper].iap = [[IAPHelper alloc] initWithProductIdentifiers:dataSet]; // 請求商品信息 [[IAPShare sharedHelper].iap requestProductsWithCompletion:^(SKProductsRequest* request,SKProductsResponse* response){ if(response.products.count > 0 ) { SKProduct *product = response.products[0]; [[IAPShare sharedHelper].iap buyProduct:product onCompletion:^(SKPaymentTransaction* trans){ if(trans.error){ [SVProgressHUD showErrorWithStatus:trans.error.userInfo.allValues[0]]; [self errorPost:nil]; } else if(trans.transactionState == SKPaymentTransactionStatePurchased) { NSLog(@"*********內部支付成功*********"); ///將臨時單號存在本地【此處做返回信息保存(臨時單號、用戶信息、返回的data)】 ///去服務器驗證充值
/// 備注:這里要做兩個判斷,一是直接支付成功后回調的,二是App打開后,對上次驗證失敗回調到這里的
} else if(trans.transactionState == SKPaymentTransactionStateFailed) { NSLog(@"*********支付失敗*********"); if (trans.error.code == SKErrorPaymentCancelled) { } else if (trans.error.code == SKErrorClientInvalid) { } else if (trans.error.code == SKErrorPaymentInvalid) { } else if (trans.error.code == SKErrorPaymentNotAllowed) { } else if (trans.error.code == SKErrorStoreProductNotAvailable) { } else{ } [SVProgressHUD showErrorWithStatus:trans.error.userInfo.allValues[0]]; [self errorPost:nil]; } }]; }else{ // ..未獲取到商品 [SVProgressHUD showErrorWithStatus:@"暫未獲取到商品"]; [self errorPost:nil]; } }]; }
3.服務器驗證充值(上一步成功后驗證)
在蘋果充值成功后,根據充值成功返回的數據data、臨時單號、用戶信息(后台要求)去服務器驗證充值,如果驗證成功,將上一步存在本地數據庫的數據(臨時單號、用戶信息(這里我取userId)、蘋果充值成功返回的data)刪除;如果充值失敗,即為漏單,但是已經將驗證服務器充值的數據存在了本地數據庫,可再次嘗試,或者稍候嘗試,根據自己的提示操作而定。
///向服務器驗證進行充值 - (void)iapPayOValidData:(NSString *)strReceipt temOrder:(NSString *)temOrder{ ///驗證充值 [SVProgressHUD showWithStatus:@"正在為您充值..."]; NSString *urlSting = @""; ///post data【驗證參數】 NSMutableDictionary *dicPost = [NSMutableDictionary dictionary]; [HttpTools postHttpRequestURL:urlSting RequestPram:dicPost RequestSuccess:^(id respoes) { [SVProgressHUD dismiss]; NSDictionary *dicValid = [HttpTools respoesToDic:respoes]; if ([dicValid[@"code"] integerValue] == 1) {
///刪除本地存的驗證信息【臨時單號、用戶信息、蘋果支付成功返回的data】 } else{ [self errorPost:dicValid[@"errmsg"]]; } } RequestFaile:^(NSError *erro) { [SVProgressHUD dismiss]; [self errorPost:[HttpTools error:erro]]; }]; }
結束
至此,整個內購充值流程已完畢,以上傳遞的參數、存儲的參數,是根據服務器后台要求,可根據自己服務器后台商量,怎么做更好,如果大家有更好的方案,希望能借鑒!謝謝!
最后還是要說說,不管怎么做漏單處理,總會有幾個漏單的,但是幾率很小,而且還想說,什么時候可以有辦法躲過這30%的抽成.......