2018最新研究應(yīng)用內(nèi)支付2:代碼封裝學(xué)習(xí)

衣帶漸寬終不悔褐鸥,為伊消得人憔悴,經(jīng)過近幾天的刻苦研讀和高效整理,終有成果锹安!

應(yīng)用內(nèi)支付流程展示學(xué)習(xí).gif

目錄:

一.應(yīng)用內(nèi)支付Code展示學(xué)習(xí)

二.支付深度理解層次

三.支付遇到的一些坑

四.手把手集成我的Demo

一.應(yīng)用內(nèi)支付Code展示學(xué)習(xí)

接著上篇文章最后對應(yīng)用內(nèi)支付流程梳理步驟進(jìn)行深化;本篇同樣分為支付前倚舀,支付中叹哭,支付后流程。

1.支付前Code展示

1.1注冊通知以下3個通知:

-(void)addAllNoti {
    
    // 獲取商品信息通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(responseToOrderDatas:)
                                                 name:IAPZWGoodsRequestNotification
                                               object:[IAPZWGoodsManager sharedInstance]];
    
    // 是否下單成功的通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(responseToAllGoodsIsSuccess:)
                                                 name:IAPZWBuyResultNotification
                                               object:[IAPZWGoodsNoti sharedInstance]];
    // 取消支付的通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(responseToAllGoodsCancle:)
                                                 name:IAPZWBuyResultNotificationCancle
                                               object:[IAPZWGoodsNoti sharedInstance]];
}

當(dāng)然也要在dealloc方法中注銷通知

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:IAPZWGoodsRequestNotification
                                                  object:[IAPZWGoodsNoti sharedInstance]];
    
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:IAPZWBuyResultNotification
                                                  object:[IAPZWGoodsNoti sharedInstance]];
    
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:IAPZWBuyResultNotificationCancle
                                                  object:[IAPZWGoodsNoti sharedInstance]];
}

1.2獲取商品信息的方法:

// 獲取所有的商品信息
-(void)getAllAppleProductInfoDatas
{
    // Query the App Store for product information if the user is is allowed to make purchases.
    // Display an alert, otherwise.
    if([SKPaymentQueue canMakePayments])
    {
        [[DisplayHelper shareDisplayHelper]showLoading:self.view noteText:@"加載商品中..."];
        // 正確方式為從后臺獲取,這里本地寫死商品id
        NSArray *productIds = @[@"1008612345",@"1008611111"];
        [[IAPZWGoodsManager sharedInstance] requestGetAllGoodsProductIds:productIds];
//        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//            [[DisplayHelper shareDisplayHelper]hideLoading:self.view];
//
//        });
        
    }
    
    else
    {
        
    }

}

1.3在剛才的通知中回調(diào)對應(yīng)的數(shù)據(jù)如下方法:

-(void)responseToOrderDatas:(NSNotification *)notification
{
    IAPZWGoodsManager *goodsManager = (IAPZWGoodsManager*)notification.object;
    IAPGoodsRequestStatus status = goodsManager.status;
    
    [[DisplayHelper shareDisplayHelper]hideLoading:self.view];
    
    if (status == IAPGoodsRequestResponse)
    {

        
        self.dataArr = goodsManager.availableProducts;
        [self.tableView reloadData];
    }
}

2.支付中Code展示(類似微信支付和支付寶支付的下單流程)

2.1用戶點(diǎn)擊Cell發(fā)起下單請求:

// 下單商品
-(void)buyAllGood:(SKProduct *)product
{
    SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

注意如果發(fā)現(xiàn)上次還有未驗(yàn)證的訂單可通過下面方法重新驗(yàn)證:

// 如果存在的話即為上次未來的及驗(yàn)證的訂單重新驗(yàn)證(這里沒有做相關(guān)邏輯)
-(void)restoreAllGoods
{
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

2.2在調(diào)起蘋果支付頁面后回調(diào)到以下這個方法

// 支付過程中的回調(diào)
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for(SKPaymentTransaction * transaction in transactions)
    {
        switch (transaction.transactionState )
        {
            // 1.第一次調(diào)起蘋果支付框時走這里
            case SKPaymentTransactionStatePurchasing:
                break;
                
            case SKPaymentTransactionStateDeferred:

                break;
                // 2.這里有時會走2次痕貌,其中比較關(guān)心的一次是 IAPZWBuyGoodsSucceeded 這一次
            case SKPaymentTransactionStatePurchased:
            {
                self.purchasedID = transaction.payment.productIdentifier;
                [self.goodProductIds addObject:transaction];
                

                if(transaction.downloads && transaction.downloads.count > 0)
                {
                    [self completeTransaction:transaction forStatus:IAPZWDownOrderStarted];
                }
                else
                {
                    [self completeTransaction:transaction forStatus:IAPZWBuyGoodsSucceeded];
                }
            }
                break;
            case SKPaymentTransactionStateRestored:
            {
                self.purchasedID = transaction.payment.productIdentifier;
                [self.goodRestoredIds addObject:transaction];
                
                NSLog(@"SKPaymentTransactionStateRestored %@",transaction.payment.productIdentifier);
                if(transaction.downloads && transaction.downloads.count > 0)
                {
                    [self completeTransaction:transaction forStatus:IAPZWDownOrderStarted];
                }
                else
                {
                    [self completeTransaction:transaction forStatus:IAPZWRestoredSucceeded];
                }
            }
                break;
                // 3.測試中會發(fā)現(xiàn)有時走這個方法
            case SKPaymentTransactionStateFailed:
            {
                self.errorMsg = [NSString stringWithFormat:@"SKPaymentTransactionStateFailed %@ ",transaction.payment.productIdentifier];
                [self completeTransaction:transaction forStatus:IAPZWBuyGoodsFailed];
            }
                break;
            default:
                break;
        }
    }
}

注意:在調(diào)用第一次彈出支付框风罩,如下所示

支付流程輸入賬號和密碼等.png

輸入賬號和密碼后,且密碼正確的情況下舵稠,點(diǎn)擊好后會調(diào)用第二次該方法超升,之后可能調(diào)用第三次該方法

2.3在成功調(diào)用上面方法之后,會通過以下方式發(fā)通知

// 完成支付
-(void)completeTransaction:(SKPaymentTransaction *)transaction forStatus:(NSInteger)status
{
    self.status = status;
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:IAPZWBuyResultNotification object:self];
    }
    
    if (status == IAPZWDownOrderStarted) // 開始下單
    {
        [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
    }
    else // 已經(jīng)下單購買完成
    {
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
}

之后回調(diào)到下面的方法中

// 完成支付
-(void)completeTransaction:(SKPaymentTransaction *)transaction forStatus:(NSInteger)status
{
    self.status = status;
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:IAPZWBuyResultNotification object:self];
    }
    
    if (status == IAPZWDownOrderStarted) // 開始下單
    {
        [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads];
    }
    else // 已經(jīng)下單購買完成
    {
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
}

3.支付后Code展示(重點(diǎn)在雙重認(rèn)證

根據(jù)是否支付成功哺徊,若支付成功的話室琢,開始雙重認(rèn)證

3.1先本地認(rèn)證是否可以獲得到receipt

- (void)completeAllDownOrderIsSuccess:(NSString *)productIdentifier {

    NSData *receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
    NSString *receipt = [receiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    [[DisplayHelper shareDisplayHelper]hideLoading:self.view];
    if ([receipt length] > 0 && [productIdentifier length] > 0) {

        /**
         可以將receipt發(fā)給服務(wù)器進(jìn)行購買驗(yàn)證
         */
        
        [self sendRequestReceiptToAppStore:receipt];
        
        
        
    }
    //    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

3.2客戶端去蘋果服務(wù)器認(rèn)證

-(void)sendRequestReceiptToAppStore:(NSString *)receipt
{
    NSError *error;
    NSDictionary *requestContents = @{@"receipt-data": receipt};
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
    
    if (!requestData) {
        
    }else{
        
    }
    //    NSURL *storeURL;
    //#ifdef DEBUG
    //    storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
    //#else
    //    storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
    //#endif
    
    NSURL *storeURL;
    if (kIsProduction) {
        storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
    }else {
        storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
    }
    
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    WS(ws);
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               if (connectionError) {
                                   /* 處理error */
                               } else {
                                   NSError *error;
                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                   if (!jsonResponse) { // 出錯的情況
                                       /* 處理error */
                                       dispatch_async(dispatch_get_main_queue(), ^{
                                           [DisplayHelper displayWarningAlert:@"支付失敗!"];
                                       });

                                   }else{
                                       /* 處理驗(yàn)證結(jié)果 */
                                       if ([[jsonResponse allKeys]containsObject:@"status"]) {
                                           NSString *statusStr =jsonResponse[@"status"];
                                           if ([statusStr intValue] ==0) { // 成功后發(fā)服務(wù)器進(jìn)行第二層驗(yàn)證
                                               // 重發(fā)我們自己服務(wù)器的驗(yàn)證
                                               [ws requestPayIsSuccess:receipt];
                                           }else {
                                               dispatch_async(dispatch_get_main_queue(), ^{
                                                   if ([statusStr intValue] ==21002) { // 請求過于頻繁,有刷單嫌疑落追,需重發(fā)服務(wù)器
                                                       // 重發(fā)我們自己服務(wù)器的驗(yàn)證
                                                       [ws requestPayIsSuccess:receipt];
                                                   }else { // 其他失敗的情況
                                                       [DisplayHelper displayWarningAlert:@"支付失敗!"];
                                                   }
                                                   
                                               });
                                               
                                           }
                                           
                                       }
                                       
                                   }
                               }
                           }];
    
}

3.3客戶端讓我們的服務(wù)端去蘋果服務(wù)器認(rèn)證
3.4服務(wù)端修改對應(yīng)的商品字段盈滴,有必要的話,客戶端也要進(jìn)行修改

二.支付深度理解層次

1.區(qū)分應(yīng)用內(nèi)支付中內(nèi)和外的關(guān)系

一般的微信支付寶支付時轿钠,是調(diào)用支付接口時直接跳轉(zhuǎn)到對應(yīng)的App巢钓,可以很明顯的感覺到支付前后在App的內(nèi)外關(guān)系,而對于蘋果應(yīng)用內(nèi)支付呢疗垛,則必須區(qū)分開邏輯的內(nèi)外症汹,才算是真正的明白該支付的特點(diǎn)。

以本次的Demo為例:

當(dāng)支付的輸入輸出框展示在您的面前時继谚,就證明在應(yīng)用外開始支付了烈菌,而當(dāng)該框消失或者支付成功后如Demo中展示“支付成功!”時,就證明該進(jìn)入應(yīng)用內(nèi)完成支付了花履!

所以處理好 支付:應(yīng)用內(nèi)到應(yīng)用外芽世;支付完成:應(yīng)用外回調(diào)到應(yīng)用內(nèi)

2.商品列表請求和處理流程注意

2.1請求商品列表最好為接口通過接口返回而不是本地寫死的情況

2.2支付過程中

  • (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions

中的狀態(tài)為SKPaymentTransactionStatePurchased 時回調(diào)2次的區(qū)分為第一次為開始,第二次為成功請注意區(qū)分诡壁,本人當(dāng)時就是爬的這里的坑造成的

2.3支付成功后不要忘了雙重認(rèn)證服務(wù)端

三.支付遇到的一些坑

1.雙重校驗(yàn)的問題

什么是雙重驗(yàn)證济瓢?
第一層認(rèn)證為客戶端和蘋果那邊認(rèn)證是否支付成功,第二層認(rèn)證為服務(wù)端和蘋果那邊認(rèn)證是否支付成功

注意:客戶端和蘋果那邊驗(yàn)證完成返回0為正常支付成功妹卿,但是返回status為21002時旺矾,官方解釋是請求太頻繁蔑鹦,有刷單嫌疑。故而這種情況也要把對應(yīng)的數(shù)據(jù)發(fā)給服務(wù)端驗(yàn)證箕宙。

2.關(guān)于沙盒賬戶的使用

測試前需要先把AppStore中真實(shí)AppID給退出嚎朽,之后再進(jìn)行測試,避免調(diào)用失敿砼痢哟忍;實(shí)際測試中發(fā)現(xiàn)一個沙盒賬戶只能綁定一個手機(jī),請各位自己測試告知陷寝。

3.在類線上支付測試時充值問題

對真實(shí)AppID進(jìn)行充值支付時需要驗(yàn)證以前的密碼以及密保問題等

4.測試賬號添加問題

測試賬號添加時锅很,密碼不要設(shè)置過于簡單,不然就會出現(xiàn)添加測試賬號失敗的問題凤跑;同樣沙盒模式下登錄前請退出以前的登錄賬戶才能登錄沙盒賬戶爆安。另外沙盒模式驗(yàn)證完畢后請記得退出原來的沙盒賬戶,之后登錄自己正式的賬戶仔引,不然后續(xù)下載軟件就會下載不了扔仓,總之,驗(yàn)證沙河模式后及時重新替換成自己的賬戶肤寝,避免來回使用麻煩当辐。

5. SKPaymentTransactionStatePurchased 方法回調(diào)

下單完成后回調(diào)到此地時,一般會走二次鲤看,第一次為開始下訂單缘揪;第二次為下訂單成功的情況,詳見Demo中區(qū)分情況义桂。

四.手把手集成我的Demo

1.導(dǎo)入的類庫及介紹

#import "IAPZWGoodsNoti.h"
#import "IAPZWGoodsManager.h"
#import <StoreKit/StoreKit.h>

2.Demo中類的介紹

IAPZWGoodsNoti 主要為獲取商品列表的類
IAPZWGoodsManager 主要為支付過程中監(jiān)聽的幫助類

3.其他注意細(xì)節(jié)

按照以上的步驟執(zhí)行后發(fā)現(xiàn)找筝,咦,怎么還是無法支付成功呢慷吊?嘿嘿干貨肯定要在最后展示哈袖裕!

3.1AppDeleage注冊監(jiān)聽和取消監(jiān)聽問題

#pragma mark - UIApplication Methods

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    
    // Remove the observer
    [[SKPaymentQueue defaultQueue] removeTransactionObserver: [IAPZWGoodsNoti sharedInstance]];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [[UINavigationBar appearance]setBarTintColor:RGBCOLOR(129, 188, 53)];
    [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
    
    self.window =[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    [self initRootViewController];
    [self.window makeKeyAndVisible];
#warning 記得調(diào)用初始化分享或支付SDK的代碼
//    // 初始化ShareSDK
//    [self initSharedKey];
//        
//    // 初始化微信支付
//    [self initWpay];
    
    [[SKPaymentQueue defaultQueue] addTransactionObserver:[IAPZWGoodsNoti sharedInstance]];

    

    return YES;
}

3.2如BoundId問題
更換 BoundId為您的可上線項目的BoundId

3.3是否是生產(chǎn)環(huán)境問題
全局搜索 kIsProduction 修改為0表示沙盒模式,為1為線上環(huán)境溉瓶,注意急鳄!

3.4請求商品列表問題
從后臺獲取對應(yīng)的商品列表之后再為下單處理,而不是本地寫死堰酿,避免上線后無法更改疾宏。

3.5雙重認(rèn)證再次強(qiáng)調(diào)問題
保險起見,一定要做雙重認(rèn)證触创,另外返回status為21002時坎藐,也要發(fā)給后臺進(jìn)行是否支付成功的驗(yàn)證。

參考資料:

1.iOS之支付:http://www.reibang.com/p/a9e17f50df9e
2.iOS開發(fā)內(nèi)購全套圖文教程:http://www.reibang.com/p/86ac7d3b593a
3.iOS 內(nèi)購支付兩種模式:http://www.reibang.com/p/15086493768a
4.談?wù)勌O果應(yīng)用內(nèi)支付(IAP)的坑:http://www.reibang.com/p/c3c0ed5af309
5.【iOS】蘋果IAP(內(nèi)購)中沙盒賬號使用注意事項:http://www.reibang.com/p/1ef61a785508

最后附上:
2018最新研究應(yīng)用內(nèi)支付1:準(zhǔn)備工作:http://www.reibang.com/p/f090740991dd
最終的Demo地址:https://github.com/zxwIsCode/IAPZWDemo

最后期待下一篇,應(yīng)用內(nèi)支付上線注意的問題等

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岩馍,一起剝皮案震驚了整個濱河市碉咆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛀恩,老刑警劉巖疫铜,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛绘,死亡現(xiàn)場離奇詭異也榄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)南蹂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門佃乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驹尼,你說我怎么就攤上這事趣避。” “怎么了新翎?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵程帕,是天一觀的道長。 經(jīng)常有香客問我地啰,道長愁拭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任亏吝,我火速辦了婚禮岭埠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔚鸥。我一直安慰自己惜论,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布止喷。 她就那樣靜靜地躺著馆类,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弹谁。 梳的紋絲不亂的頭發(fā)上乾巧,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音预愤,去河邊找鬼沟于。 笑死,一個胖子當(dāng)著我的面吹牛鳖粟,可吹牛的內(nèi)容都是我干的社裆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼向图,長吁一口氣:“原來是場噩夢啊……” “哼泳秀!你這毒婦竟也來了标沪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嗜傅,失蹤者是張志新(化名)和其女友劉穎金句,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吕嘀,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡违寞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了偶房。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趁曼。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棕洋,靈堂內(nèi)的尸體忽然破棺而出挡闰,到底是詐尸還是另有隱情,我是刑警寧澤掰盘,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布摄悯,位于F島的核電站,受9級特大地震影響愧捕,放射性物質(zhì)發(fā)生泄漏奢驯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一次绘、第九天 我趴在偏房一處隱蔽的房頂上張望瘪阁。 院中可真熱鬧,春花似錦断盛、人聲如沸罗洗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伙菜。三九已至,卻和暖如春命迈,著一層夾襖步出監(jiān)牢的瞬間贩绕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工壶愤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淑倾,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓征椒,卻偏偏與公主長得像娇哆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容