iOS內(nèi)購(gòu)(IAP)流程記錄(業(yè)務(wù)篇)
之前已經(jīng)寫(xiě)過(guò)內(nèi)購(gòu)前期準(zhǔn)備資料的文章,這篇文章梳理下業(yè)務(wù)實(shí)現(xiàn)邏輯能岩。
前期需求:公司是有自己的訂單系統(tǒng),所以我們需要在發(fā)起支付的時(shí)候需要先去后臺(tái)獲取訂單號(hào)伐憾,拿到訂單號(hào)后再調(diào)用蘋(píng)果內(nèi)購(gòu)流程措拇,最后把訂單號(hào)和支付憑證返回給后臺(tái),由后臺(tái)去和蘋(píng)果再次校驗(yàn)交易結(jié)果了赵,最后返回訂單支付結(jié)果給我們潜支。(這是正常流程,內(nèi)購(gòu)存在漏單柿汛,異常訂單稍后講)冗酿。
支付流程圖:
了解流程之后,我們就開(kāi)始編寫(xiě)代碼了,我這邊是把內(nèi)購(gòu)的代碼封裝了一個(gè)單例
準(zhǔn)備:
需導(dǎo)入庫(kù):
StoreKit
頭文件需要引用:
#import <StoreKit/StoreKit.h>
代理添加:
<SKPaymentTransactionObserver,SKProductsRequestDelegate>
代碼實(shí)現(xiàn):
1.先去拿到后臺(tái)訂單號(hào)裁替,有了后臺(tái)訂單號(hào)之后再判斷是否有購(gòu)買(mǎi)權(quán)限
[SKPaymentQueue canMakePayments]
2.如果有購(gòu)買(mǎi)權(quán)限项玛,則通過(guò)產(chǎn)品id去獲取內(nèi)購(gòu)項(xiàng)目信息
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];
3.通過(guò)蘋(píng)果內(nèi)購(gòu)回調(diào)函數(shù)去處理
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
// 商品數(shù)組
NSArray *productArr = response.products;
if (productArr.count > 0) {
SKProduct *product = nil;
for (SKProduct *p in productArr) {
if ([p.productIdentifier isEqualToString:_productID]) {
product = p;
break;
}
}
// 發(fā)起內(nèi)購(gòu)
SKPayment *payMent = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payMent];
} else {
//項(xiàng)目id錯(cuò)誤
}
}
4.判斷交易狀態(tài):
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
// 獲取結(jié)果
// 驗(yàn)證成功與否都注銷交易,否則會(huì)出現(xiàn)虛假憑證信息一直驗(yàn)證不通過(guò),每次進(jìn)程序都得輸入蘋(píng)果賬號(hào)
for (SKPaymentTransaction *trans in transactions) {
switch (trans.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加進(jìn)列表");
break;
case SKPaymentTransactionStatePurchased:
NSLog(@"交易完成");
//自己可添加驗(yàn)證
[self completeTransaction:trans];
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateFailed:
NSLog(@"交易失敗");
[self failedTransaction:trans];//處理失敗邏輯
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateRestored:
NSLog(@"已經(jīng)購(gòu)買(mǎi)過(guò)商品");
[[SKPaymentQueue defaultQueue] finishTransaction:trans]; //消耗型商品不用寫(xiě)
break;
case SKPaymentTransactionStateDeferred:
break;
default:
break;
}
}
}
5.收到支付成功后把訂單號(hào)和交易憑證拋給后臺(tái):
- (void) completeTransaction:(SKPaymentTransaction *)transaction{
//這里要把SKPaymentTransaction整個(gè)對(duì)象給后臺(tái),記得攜帶訂單號(hào)弱判,先驗(yàn)證正式服務(wù)器,如果正式服務(wù)器返回21007再去蘋(píng)果測(cè)試服務(wù)器驗(yàn)證,沙盒測(cè)試環(huán)境蘋(píng)果用的是測(cè)試服務(wù)器
//正式環(huán)境:https://buy.itunes.apple.com/verifyReceipt
//沙箱環(huán)境:https://sandbox.itunes.apple.com/verifyReceipt
}
整合好之后襟沮,拿到后臺(tái)返回的訂單號(hào)每次發(fā)起內(nèi)購(gòu)調(diào)用下面這個(gè)函數(shù)就可以了
- (void)startIAPWithProductID:(NSString *)productID andOrderNo:(NSString *)orderNo completeHandle: (IAPCompletionHandle)handle{
_handle = handle;
_orderNo = orderNo;
if(productID && productID.length > 0) {
if ([SKPaymentQueue canMakePayments]) {
// 允許內(nèi)購(gòu)
_productID = productID;
NSSet *set = [NSSet setWithObjects:productID, nil];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
// 獲取內(nèi)購(gòu)項(xiàng)目信息
[request start];
} else {
// 不允許內(nèi)購(gòu)
}
} else {
NSLog(@"內(nèi)購(gòu)項(xiàng)目ID錯(cuò)誤");
}
}
不出意外,正常流程就是這樣了昌腰,但是為了防止漏單的情況开伏,所以增加了漏單機(jī)制。
異常訂單處理
我的處理邏輯是把蘋(píng)果返回成功但是后臺(tái)返回失敗的訂單存到異常隊(duì)列遭商,每次啟動(dòng)APP的時(shí)候把異常隊(duì)列輪詢一次固灵,就是把訂單數(shù)據(jù)再發(fā)送給后臺(tái),讓后臺(tái)再去校驗(yàn)株婴,如果校驗(yàn)成功怎虫,則在隊(duì)列中移除異常訂單,后臺(tái)添加購(gòu)買(mǎi)數(shù)據(jù)困介,更新訂單狀態(tài)大审。
//array是異常數(shù)組,包含訂單號(hào)座哩、交易憑據(jù)
-(void)anomalyOrderVerify:(NSMutableArray * )array{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = array.count; i > 0 ; i-- ) {
//循環(huán)判斷徒扶,通過(guò)信號(hào)量控制
dispatch_semaphore_signal(semaphore);
}
}
這只是一種異常訂單處理方法,還有其他的暫時(shí)沒(méi)有添加根穷,網(wǎng)上大神這么多姜骡,我也在借鑒他們漏單處理的方式,后面也還會(huì)再完善這方面內(nèi)容屿良,畢竟涉及到支付圈澈。