iOS 內購IAP(In-App Purchases)代碼實現(上)

iOS 內購IAP(In-App Purchases)代碼實現(上)

iOS 內購,也叫內支付,是在iOS應用內部,向蘋果服務器發起購買請求的過程。我們在這邊來講一講代碼的實現過程。還有,在做內購的時候,常常會有丟單現象發生,每到這時候,我的內心幾乎是崩潰的。所以後面我們來講講如何有效地防止丟單。

這裏寫圖片描述

先簡述一下內購過程。比如說遊戲裏面,你要買一個裝備,這時候你點擊了購買按鈕;遊戲向蘋果服務器發送購買請求,彈出loading框;手機彈出蘋果賬戶和密碼輸入框;輸入完賬戶密碼後,錢打到蘋果那邊,然後提示你購買成功或者失敗;遊戲loading框消失,成功給你發放商品,失敗提示你購買失敗。整個購買過程結束。

當然,遊戲方必須事先在蘋果iTunes後臺,配置好相應的商品和價格。具體的配置過程,網絡上有很多教程,這邊我們就不再贅述。

這裏寫圖片描述

配置好以後,我們會獲得一個產品ID。這個東西,我們待會代碼中會用到。

準備

先要將StoreKit.framework加入工程中。
這裏寫圖片描述

然後工程中創建一個類,我們把它稱爲SDKAppStore。 import StoreKit頭文件。

#import <StoreKit/StoreKit.h>

然後在頭部寫上StoreKit的協議

@interface SDKAppStore : NSObject<SKProductsRequestDelegate,SKPaymentTransactionObserver>

SKProductsRequestDelegate是商品請求回調,用來告訴你有沒有這個商品。
SKPaymentTransactionObserver是交易觀察者,用來告訴你交易進行到哪個步驟了。

接下來使用單例的形式

#pragma mark - 獲取單例
+ (instancetype)sharedInstance {
    static SDKAppStore* instance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        instance = [[SDKAppStore alloc] init];
    });
    return instance;
}

請求商品信息

交易開始之前,我們先要用productId去請求一下商品的信息。

#pragma mark - 開始支付
- (void)startPay:(SDKPayInfo*)payInfo productId:(NSString*)productId {

    self.payInfo = payInfo;
    self.productId = productId;

    if ([SKPaymentQueue canMakePayments]) {
        [self requestProducts:productId];
    } else {
        //不允許程序內付費購買;
        [self.appStoreDelegate sdkAppStoreNotAllowPay];
    }
}

#pragma mark - 請求商品信息
- (void)requestProducts:(NSString*)productId {

    NSSet *productIds = [NSSet setWithObject:productId];
    SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIds];
    productsRequest.delegate = self;
    [productsRequest start];
}

當程序調用startPay這個方法,整個購買交易就開始了。payInfo是記錄了一些用戶信息,到時候要傳到遊戲服務器去的。productId就是上面說的商品ID。
首先用[SKPaymentQueue canMakePayments]判斷一下程序是否可以交易。
然後將productId放到SKProductsRequest裏面,調用一下start,這時候商品ID就被髮送到蘋果服務器了。
在此之前,我們要先設置好回調,不然接受不到返回來的商品信息

productsRequest.delegate = self;

然後重寫回調方法

pragma mark - appstore回調 請求商品信息回調
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {

    NSArray *products = response.products;
    SKProduct *product = [products count] > 0 ? [products objectAtIndex:0] : nil;
    if (product) {
        //添加付款請求到隊列
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    } else {
        //無法獲取商品信息
        [self.appStoreDelegate sdkAppStoreNoProductInfo];
        SDKLog(@"無法獲取商品信息");
    }   
}

這邊我們先把返回來的商品信息包裝一下,然後放到交易隊列裏面去
[[SKPaymentQueue defaultQueue] addPayment:payment];
這時候就開始交易啦!

交易回調

商品信息放到交易隊列裏面以後,會回調paymentQueue: updatedTransactions:這個方法這個方法。這個方法的作用是告訴你交易進行到哪個步驟了。

#pragma mark - appstore回調 付款請求回調
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction {
    for(SKPaymentTransaction *tran in transaction){

        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                //購買中
                [self.appStoreDelegate sdkAppStorePaying];
                break;
            case SKPaymentTransactionStateDeferred:
                //購買中 交易被推遲
                [self.appStoreDelegate sdkAppStorePaying];
                break;
            case SKPaymentTransactionStateFailed:
                //購買監聽 交易失敗
                [self failedTransaction:tran];
                break;
            case SKPaymentTransactionStatePurchased:
                //購買監聽 交易完成
                [self completeTransaction:tran];
                break;
            case SKPaymentTransactionStateRestored:
                //購買監聽 恢復成功
                [self restoreTransaction:tran];
                break;
            default:
                break;
        }
    }
}

這邊是一個switch,分別對應調用交易的各個步驟:

  • SKPaymentTransactionStatePurchasing 購買中。這時候你可以彈出一個loading框,提示用戶交易正在進行。
  • SKPaymentTransactionStateDeferred 交易被推遲。loading框還在。
  • SKPaymentTransactionStateFailed 交易失敗。我們調用交易失敗的事務,比如通知用戶交易失敗。取消交易也在交易失敗的範疇。
  • SKPaymentTransactionStatePurchased 交易成功。我們調用交易成功的事務,比如到服務器驗證票據和訂單信息。
  • SKPaymentTransactionStateRestored 恢復成功。這一個項目在遊戲中通常是沒有用到的。我們可以忽略。
#pragma mark - 交易事務處理
// 交易成功
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    [self checkReceipt:transaction];
    [self finishTransaction:transaction wasSuccessful:YES];
}
// 交易失敗
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    [self.appStoreDelegate sdkAppStorePayComplete:NO];
    [self finishTransaction:transaction wasSuccessful:NO];
}
// 交易恢復
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    [self.appStoreDelegate sdkAppStorePayComplete:YES];
    [self finishTransaction:transaction wasSuccessful:YES];
}
//結束交易事務
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful {
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

所有的事務,到最後都要調用一下finishTransaction這個方法來結束事務。這個方法裏面,我們把剛剛記錄下來的訂單刪除。並且調用一下SKPaymentQueue的finishTransaction。這樣一個完整的交易纔算結束。
如果沒有調用SKPaymentQueue的finishTransaction,交易不會關閉。

在這邊我們把交易的大概流程走了一遍。下一篇我們來講講如何驗證票據和防止丟單。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章