iOS 內購詳解-代碼篇

一、分步驟說明

1、獲取商品列表

由於蘋果服務器返回很慢,並且一般我們都會有一個自己的商店界面,就不必向蘋果服務器去請求商品列表了。
購買時傳要購買商品的ID(在App Store Connect 創建的產品ID)就可以了。

/// 請求蘋果的服務器能夠銷售的商品
/// @param products 【產品ID】
- (void)requestProductsWithProductArray:(NSArray *)products
{
    NSSet *set = [[NSSet alloc] initWithArray:products];
    // "異步"請求蘋果能否銷售
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    request.delegate = self;
    // 啓動請求
    [request start];
}

2、蘋果服務器返回的可購買商品

#pragma mark - SKProductsRequestDelegate

/// 獲取請求結果,把商品加入到自己的商品列表
/// @param request 請求
/// @param response 返回結果
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    if (self.productDict == nil) {
        self.productDict = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
    }
    NSMutableArray *productArray = [NSMutableArray array];
    for (SKProduct *product in response.products) {
        //NSLog(@"%@", product.productIdentifier);
        [self.productDict setObject:product forKey:product.productIdentifier];
        [productArray addObject:product];
    }
    // 通知代理
    [self.delegate IAPToolGotProducts:productArray];
}

3、下單購買商品

拿到了蘋果服務器返回的可購買商品後,下單進行購買。

/// 下單購買商品
/// @param productID 商品ID
- (void)buyProduct:(NSString *)productID
{
	// 從自己的商品列表中取出要購買的商品
    SKProduct *product = self.productDict[productID];
    // 要購買的產品生成單據
    SKPayment *payment = [SKPayment paymentWithProduct:product];
    // 加入隊列準備付款購買
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

4、購買隊列狀態變化,判斷購買狀態是否成功

typedef NS_ENUM(NSInteger, SKPaymentTransactionState) {
    SKPaymentTransactionStatePurchasing,    //交易被添加到服務器隊列中
    SKPaymentTransactionStatePurchased,     // 交易正在排隊,用戶已被收費。客戶應完成交易
    SKPaymentTransactionStateFailed,        // 交易在添加到服務器隊列之前被取消或失敗
    SKPaymentTransactionStateRestored,      // 交易已從用戶的購買歷史中恢復
    SKPaymentTransactionStateDeferred,   // 等待外部操作
};
#pragma mark - SKPaymentTransaction Observer
#pragma mark 購買隊列狀態變化,判斷購買狀態是否成功

/// 當交易數組發生更改(添加或狀態更改)時發送。客戶端應該檢查交易的狀態並適當地完成
/// @param queue 隊列
/// @param transactions 交易
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    // 處理結果
    for (SKPaymentTransaction *transaction in transactions) {
        NSLog(@"隊列狀態變化 %@", transaction);
        // 如果收據狀態是購買完成
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased: { // 完成交易
                //NSLog(@"購買完成 %@", transaction.payment.productIdentifier);
                if (self.CheckAfterPay) {
                    // 需要向蘋果服務器驗證一下
                    // 通知代理
                    [self.delegate IAPToolBeginCheckingdWithProductID:transaction.payment.productIdentifier];
                    // 驗證購買憑據
                    [self verifyPruchaseWithID:transaction.payment.productIdentifier];
                } else {
                    // 不需要向蘋果服務器驗證
                    // 驗證憑據,獲取到蘋果返回的交易憑據
                    // appStoreReceiptURL iOS7.0增加的,購買交易完成後,會將憑據存放在該地址
                    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
                    // 從沙盒中獲取到購買憑據
                    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
                    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
                    // 通知代理
                    [self.delegate IAPToolBoughtProductSuccessedWithProductID:transaction.payment.productIdentifier
                                                                        andInfo:nil receipt:encodeStr];
                }
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateRestored: { // 恢復購買
                //NSLog(@"恢復成功 :%@", transaction.payment.productIdentifier);
                // 通知代理
                [self.delegate IAPToolRestoredProductID:transaction.payment.productIdentifier];
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed: { // 加入隊列之前取消或失敗
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                //NSLog(@"交易失敗");
                [self.delegate IAPToolCanceldWithProductID:transaction.payment.productIdentifier];
            }
                break;
            case SKPaymentTransactionStatePurchasing: { // 交易被添加到服務器隊列中
                NSLog(@"正在購買");
            }
                break;
            default: {
                NSLog(@"state:%ld",(long)transaction.transactionState);
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
        }
    }
}

5、交易驗證

// 發送網絡POST請求,對購買憑據進行驗證
// 沙盒測試 https://sandbox.itunes.apple.com/verifyReceipt
// 正式環境 https://buy.itunes.apple.com/verifyReceipt

當SKPaymentTransactionStatePurchased 完成交易時,我們需要驗證一下。
a、拿到收據上傳到自己的服務器進行驗證。

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
                    // 從沙盒中獲取到購買憑據
                    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
                    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

b、如果是單機就本地向蘋果服務器請求驗證。

/// 驗證購買憑據
/// @param ProductID 商品ID
- (void)verifyPruchaseWithID:(NSString *)ProductID
{
    // 驗證憑據,獲取到蘋果返回的交易憑據
    // appStoreReceiptURL iOS7.0增加的,購買交易完成後,會將憑據存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    // 發送網絡POST請求,對購買憑據進行驗證
    //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
    //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
    // Create a POST request with the receipt data.
    NSURL *url = [NSURL URLWithString:checkURL];
    NSLog(@"checkURL:%@",checkURL);

    // 國內訪問蘋果服務器比較慢,timeoutInterval需要長一點
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
    request.HTTPMethod = @"POST";
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = payloadData;
    
    // 提交驗證請求,並獲得官方的驗證JSON結果
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSData *result = data;
        // 官方驗證結果爲空
        if (result == nil) {
            //NSLog(@"驗證失敗");
            //驗證失敗,通知代理
            [self.delegate IAPToolCheckFailedWithProductID:ProductID
                                                   andInfo:result];
        }
        
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result
                                                             options:NSJSONReadingAllowFragments error:nil];
        //NSLog(@"RecivedVerifyPruchaseDict:%@", dict);
        if (dict != nil) {
            // 驗證成功,通知代理
            [self.delegate IAPToolBoughtProductSuccessedWithProductID:ProductID
                                                              andInfo:dict receipt:encodeStr];
        } else {
            //驗證失敗,通知代理
            [self.delegate IAPToolCheckFailedWithProductID:ProductID
                                                   andInfo:result];
        }
    }] resume];
}

6、拿到的收據信息是,此App所有購買的記錄

{
    environment = Sandbox;
    receipt =     {
        "adam_id" = 0;
        "app_item_id" = 0;
        "application_version" = 5;
        "bundle_id" = "com.fly.app";
        "download_id" = 0;
        "in_app" =         (
                        {
                "is_trial_period" = false;
                "original_purchase_date" = "2020-07-04 12:56:09 Etc/GMT";
                "original_purchase_date_ms" = 1587214569000;
                "original_purchase_date_pst" = "2020-07-04 05:56:09 America/Los_Angeles";
                "original_transaction_id" = 1000000653555670;
                "product_id" = VIP1;
                "purchase_date" = "2020-07-04 12:56:09 Etc/GMT";
                "purchase_date_ms" = 1587214569000;
                "purchase_date_pst" = "2020-07-04 05:56:09 America/Los_Angeles";
                quantity = 1;
                "transaction_id" = 1000000653555670;
            },
                        {
                "is_trial_period" = false;
                "original_purchase_date" = "2020-07-04 14:38:05 Etc/GMT";
                "original_purchase_date_ms" = 1587220685000;
                "original_purchase_date_pst" = "2020-04-18 07:38:05 America/Los_Angeles";
                "original_transaction_id" = 1000000653568586;
                "product_id" = VIP2;
                "purchase_date" = "2020-07-04 14:38:05 Etc/GMT";
                "purchase_date_ms" = 1587220685000;
                "purchase_date_pst" = "2020-07-04 07:38:05 America/Los_Angeles";
                quantity = 1;
                "transaction_id" = 1000000653568586;
            }
        );
        "original_application_version" = "1.0";
        "original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT";
        "original_purchase_date_ms" = 1375340400000;
        "original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles";
        "receipt_creation_date" = "2020-07-04 07:08:53 Etc/GMT";
        "receipt_creation_date_ms" = 1593832014000;
        "receipt_creation_date_pst" = "2020-07-04 00:08:53 America/Los_Angeles";
        "receipt_type" = ProductionSandbox;
        "request_date" = "2020-07-04 07:08:55 Etc/GMT";
        "request_date_ms" = 1593832035030;
        "request_date_pst" = "2020-07-04 00:08:55 America/Los_Angeles";
        "version_external_identifier" = 0;
    };
    status = 0;
}

7、恢復商品

非消耗型項目一定要有恢復商品的功能,不會被拒絕哦~

/// 恢復商品
- (void)restorePurchase
{
    // 恢復已經完成的所有交易.(僅限永久有效商品)
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

二、詳細代碼

封裝內購工具類
FSInAppPurchaseTool.h

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

NS_ASSUME_NONNULL_BEGIN

/// 內購工具的代理
@protocol FSInAppPurchaseToolDelegate <NSObject>

/// 系統錯誤
-(void)IAPToolSysWrong;

/// 可購買商品
/// @param products 商品數組
-(void)IAPToolGotProducts:(NSMutableArray *)products;

/// 購買成功
/// @param productID 購買成功的商品ID
/// @param infoDic 官方驗證結果
/// @param receipt 憑證
-(void)IAPToolBoughtProductSuccessedWithProductID:(NSString *)productID
                                            andInfo:(nullable NSDictionary *)infoDic receipt:(nonnull NSString *)receipt;

/// 取消購買
/// @param productID 商品ID
-(void)IAPToolCanceldWithProductID:(NSString *)productID;

/// 購買成功,開始驗證購買
/// @param productID 商品ID
-(void)IAPToolBeginCheckingdWithProductID:(NSString *)productID;

/// 驗證失敗
/// @param productID 商品ID
/// @param infoData 官方驗證結果
-(void)IAPToolCheckFailedWithProductID:(NSString *)productID
                                 andInfo:(NSData *)infoData;

/// 恢復了已購買的商品(永久性商品)
/// @param productID 商品ID
-(void)IAPToolRestoredProductID:(NSString *)productID;

/// 恢復了已購買的商品(永久性商品)
/// @param products 商品信息
/// @param receipt 憑證
-(void)IAPToolPaymentQueueRestoreCompletedTransactionsFinished:(NSArray *)products receipt:(nonnull NSString *)receipt;

@optional

/// 連接itunes store 錯誤
/// @param error 錯誤信息
-(void)IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:(NSString *)error;

@end

#pragma mark -FSInAppPurchaseTool

/// 內購工具
@interface FSInAppPurchaseTool : NSObject

@property (nonatomic, weak) id <FSInAppPurchaseToolDelegate> delegate;

/// 購買完後是否在iOS端向服務器驗證一次,默認爲YES
@property (nonatomic, assign) BOOL CheckAfterPay;

+ (FSInAppPurchaseTool *)defaultTool;

/// 詢問蘋果的服務器能夠銷售哪些商品
/// @param products 商品ID的數組
- (void)requestProductsWithProductArray:(NSArray *)products;

/// 用戶決定購買商品
/// @param productID 商品ID
- (void)buyProduct:(NSString *)productID;

/// 恢復商品(僅限永久有效商品)
- (void)restorePurchase;

@end

NS_ASSUME_NONNULL_END

FSInAppPurchaseTool.m

#import "FSInAppPurchaseTool.h"

/// 蘋果服務器購買憑據進行驗證
#ifdef DEBUG
#define checkURL @"https://sandbox.itunes.apple.com/verifyReceipt"
#else
#define checkURL @"https://buy.itunes.apple.com/verifyReceipt"
#endif

@interface FSInAppPurchaseTool ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>

/// 商品字典
@property(nonatomic,strong)NSMutableDictionary *productDict;

@end

@implementation FSInAppPurchaseTool

static FSInAppPurchaseTool *storeTool;

+ (FSInAppPurchaseTool *)defaultTool{
    if(!storeTool){
        storeTool = [FSInAppPurchaseTool new];
        [storeTool setup];
    }
    return storeTool;
}

#pragma mark  初始化

- (void)setup{
    self.CheckAfterPay = YES;
    // 設置購買隊列的監聽器
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}

#pragma mark 請求蘋果的服務器能夠銷售的商品

/// 請求蘋果的服務器能夠銷售的商品
/// @param products 產品
- (void)requestProductsWithProductArray:(NSArray *)products
{
    // 能夠銷售的商品
    NSSet *set = [[NSSet alloc] initWithArray:products];
    // "異步"請求蘋果能否銷售
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    request.delegate = self;
    // 啓動請求
    [request start];
}

#pragma mark - SKProductsRequestDelegate

/// 獲取請求結果,把商品加入到自己的商品列表
/// @param request 請求
/// @param response 返回結果
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    if (self.productDict == nil) {
        self.productDict = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
    }
    
    NSMutableArray *productArray = [NSMutableArray array];
    
    for (SKProduct *product in response.products) {
        //NSLog(@"%@", product.productIdentifier);
        [self.productDict setObject:product forKey:product.productIdentifier];
        [productArray addObject:product];
    }
    // 通知代理
    [self.delegate IAPToolGotProducts:productArray];
}

#pragma mark - 下單購買商品

/// 下單購買商品
/// @param productID 商品ID
- (void)buyProduct:(NSString *)productID
{
    // 從自己的商品列表中取出要購買的商品
    SKProduct *product = self.productDict[productID];
    // 要購買的產品生成單據
    SKPayment *payment = [SKPayment paymentWithProduct:product];
    // 加入隊列準備付款購買
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

#pragma mark - SKPaymentTransaction Observer
#pragma mark 購買隊列狀態變化,判斷購買狀態是否成功

/// 當交易數組發生更改(添加或狀態更改)時發送。客戶端應該檢查事務的狀態並適當地完成
/// @param queue 隊列
/// @param transactions 交易
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    // 處理結果
    for (SKPaymentTransaction *transaction in transactions) {
        NSLog(@"隊列狀態變化 %@", transaction);
        // 如果收據狀態是購買完成
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased: { // 完成交易
                //NSLog(@"購買完成 %@", transaction.payment.productIdentifier);
                if (self.CheckAfterPay) {
                    // 需要向蘋果服務器驗證一下
                    // 通知代理
                    [self.delegate IAPToolBeginCheckingdWithProductID:transaction.payment.productIdentifier];
                    // 驗證購買憑據
                    [self verifyPruchaseWithID:transaction.payment.productIdentifier];
                } else {
                    // 不需要向蘋果服務器驗證
                    // 驗證憑據,獲取到蘋果返回的交易憑據
                    // appStoreReceiptURL iOS7.0增加的,購買交易完成後,會將憑據存放在該地址
                    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
                    // 從沙盒中獲取到購買憑據
                    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
                    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
                    // 通知代理
                    [self.delegate IAPToolBoughtProductSuccessedWithProductID:transaction.payment.productIdentifier
                                                                        andInfo:nil receipt:encodeStr];
                }
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateRestored: { // 恢復購買
                //NSLog(@"恢復成功 :%@", transaction.payment.productIdentifier);
                // 通知代理
                [self.delegate IAPToolRestoredProductID:transaction.payment.productIdentifier];
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed: { // 加入隊列之前取消或失敗
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                //NSLog(@"交易失敗");
                [self.delegate IAPToolCanceldWithProductID:transaction.payment.productIdentifier];
            }
                break;
            case SKPaymentTransactionStatePurchasing: { // 交易被添加到服務器隊列中
                NSLog(@"正在購買");
            }
                break;
            default: {
                NSLog(@"state:%ld",(long)transaction.transactionState);
                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
        }
    }
}

/// 當用戶購買歷史中的所有事務都已成功添加回隊列時發送。
/// @param queue 隊列
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
    NSMutableArray *products = [[NSMutableArray alloc] init];
    NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
    for (SKPaymentTransaction *transaction in queue.transactions)
    {
        [products addObject:transaction.payment.productIdentifier];
    }
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    [self.delegate IAPToolPaymentQueueRestoreCompletedTransactionsFinished:products receipt:encodeStr];
}

/// 當事務從隊列中移除時發送(通過finishTransaction:)
/// @param queue 隊列
/// @param transactions 交易
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions API_AVAILABLE(ios(3.0), macos(10.7)) {
    
}

/// 在將用戶購買歷史記錄中的事務添加回隊列時遇到錯誤時發送
/// @param queue 隊列
/// @param error 錯誤信息
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error API_AVAILABLE(ios(3.0), macos(10.7)) {
    NSDictionary *userinfo = [[NSDictionary alloc] initWithDictionary:error.userInfo];
    if(userinfo) {
        NSString *str = userinfo[NSLocalizedDescriptionKey];
        if (str.length) {
            if ([self.delegate respondsToSelector:@selector(IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:)]) {
                [self.delegate IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:str];
            }
        }
    } else {
        if ([self.delegate respondsToSelector:@selector(IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:)]) {
            [self.delegate IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:@""];
        }
    }
}

#pragma mark - 恢復商品

/// 恢復商品
- (void)restorePurchase
{
    // 恢復已經完成的所有交易.(僅限永久有效商品)
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

#pragma mark 驗證購買憑據

/// 驗證購買憑據
/// @param ProductID 商品ID
- (void)verifyPruchaseWithID:(NSString *)ProductID
{
    // 驗證憑據,獲取到蘋果返回的交易憑據
    // appStoreReceiptURL iOS7.0增加的,購買交易完成後,會將憑據存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    // 發送網絡POST請求,對購買憑據進行驗證
    //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
    //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
    // Create a POST request with the receipt data.
    NSURL *url = [NSURL URLWithString:checkURL];
    NSLog(@"checkURL:%@",checkURL);

    // 國內訪問蘋果服務器比較慢,timeoutInterval需要長一點
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
    request.HTTPMethod = @"POST";
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = payloadData;
    
    // 提交驗證請求,並獲得官方的驗證JSON結果
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSData *result = data;
        // 官方驗證結果爲空
        if (result == nil) {
            //NSLog(@"驗證失敗");
            //驗證失敗,通知代理
            [self.delegate IAPToolCheckFailedWithProductID:ProductID
                                                   andInfo:result];
        }
        
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result
                                                             options:NSJSONReadingAllowFragments error:nil];
        //NSLog(@"RecivedVerifyPruchaseDict:%@", dict);
        if (dict != nil) {
            // 驗證成功,通知代理
            [self.delegate IAPToolBoughtProductSuccessedWithProductID:ProductID
                                                              andInfo:dict receipt:encodeStr];
        } else {
            //驗證失敗,通知代理
            [self.delegate IAPToolCheckFailedWithProductID:ProductID
                                                   andInfo:result];
        }
    }] resume];
}

@end

應用內購工具類

// 創建單例
{
	FSInAppPurchaseTool *IAPTool = [FSInAppPurchaseTool defaultTool];
	IAPTool.delegate = self;
}

/// 請求store產品
/// @param payno 服務器訂單號
/// @param pid store 產品id
- (void)startApplePayWithPayno:(NSString*)payno PID:(NSString*)pid {
    if (!payno.length) {
        ALERT(@"下訂單失敗,請稍後操作!");
        return;
    }
    if (!pid.length) {
        ALERT(@"請稍後操作!");
        return;
    }
    [self showHUD];
    [[FSInAppPurchaseTool defaultTool] requestProductsWithProductArray:@[pid]];
}

/// 恢復購買
- (IBAction)onRestoreButtonClick:(id)sender {
    [self showHUD];
    //檢查是否有恢復購買項
    [[FSInAppPurchaseTool defaultTool] restorePurchase];
}

#pragma mark -------- FSInAppPurchaseToolDelegate

/// 蘋果返回可購買的商品,前往購買
/// @param products 商品數組【SKProduct】
-(void)IAPToolGotProducts:(NSMutableArray *)products {
    NSLog(@"GotProducts:%@",products);
    [self hideHUD];
    if (products.count) {
        SKProduct *product = products[0];
        [self showHUD];
        [[FSInAppPurchaseTool defaultTool] buyProduct:product.productIdentifier];
    }
    
//    for (SKProduct *product in products){
//        NSLog(@"localizedDescription:%@\nlocalizedTitle:%@\nprice:%@\npriceLocale:%@\nproductID:%@",
//              product.localizedDescription,
//              product.localizedTitle,
//              product.price,
//              product.priceLocale,
//              product.productIdentifier);
//        NSLog(@"--------------------------");
//    }
}

/// 支付失敗/取消
/// @param productID 商品id
-(void)IAPToolCanceldWithProductID:(NSString *)productID {
    NSLog(@"canceld:%@",productID);
    [self hideHUD];
}

/// 支付成功 本地向蘋果服務器進行驗證 (CheckAfterPay爲YES 執行此步驟)
/// @param productID 商品id
-(void)IAPToolBeginCheckingdWithProductID:(NSString *)productID {
    FSLog(@"BeginChecking:%@",productID);
    [self hideHUD];
}

/// 支付成功,拿到收據傳到自己的服務器進行驗證 (CheckAfterPay爲NO 執行此步驟,爲YES時,驗證成功也會執行)
/// @param productID 商品id
/// @param infoDic 驗證結果
/// @param receipt 收據
-(void)IAPToolBoughtProductSuccessedWithProductID:(NSString *)productID
                                            andInfo:(nullable NSDictionary *)infoDic receipt:(nonnull NSString *)receipt {
    FSLog(@"BoughtSuccessed:%@",productID);
    FSLog(@"successedInfo:%@",infoDic);
    [self hideHUD];
    NSString *payno = self.payinfo[productID];
    if (payno.length) {
    // 請求自己服務器進行驗證
        [self requestPortCheckApplePay:receipt Payno:payno];
    } else {
        FSLog(@"訂單號有誤");
    }
}

/// 支付成功了,但向蘋果服務器驗證失敗了
/// @param productID 商品id
/// @param infoData 驗證結果
-(void)IAPToolCheckFailedWithProductID:(NSString *)productID
                                 andInfo:(NSData *)infoData {
    FSLog(@"CheckFailed:%@",productID);
    [self hideHUD];
}

/// 挨個返回已購買的商品(僅限永久有效商品)
/// @param productID 商品id
-(void)IAPToolRestoredProductID:(NSString *)productID {
    FSLog(@"Restored:%@",productID);
}

/// 返回所有已購買的記錄
/// @param products 商品數組
/// @param receipt 收據
-(void)IAPToolPaymentQueueRestoreCompletedTransactionsFinished:(NSArray *)products receipt:(NSString *)receipt {
    FSLog(@"Restored Finished%@",products);
    [self hideHUD];
    self.restoreProducts = products;
    self.restoreReceipt = receipt;
    
    if (products.count) {
        /// 有購買記錄就顯示恢復購買
        self.restoreButton.hidden = NO;
        if (self.restoreReceipt.length) {
        // 向自己服務器請求恢復購買
            [self requestPortRestore:self.restoreReceipt];
        }
    } else {
        ALERT(@"沒有購買記錄,無需購買恢復");
    }
}

/// 恢復購買失敗 (登錄appleid 彈窗 取消)
- (void)IAPToolPaymentQueueRestoreCompletedTransactionsFailedWithError:(NSString *)error {
    FSLog(@"Restored Failed%@",error);
    [self hideHUD];
}

//內購系統錯誤了
-(void)IAPToolSysWrong {
    FSLog(@"SysWrong");
    [self hideHUD];
}

三、內購項目-App Store Connect 詳解篇

iOS App Store Connect 內購詳解.

祝您好運~

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