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 内购详解.

祝您好运~

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是 PHP 代码实现 iOS 内购订单验证的示例: ```php <?php // 定义 app id 和 app secret $app_id = 'your_app_id'; $app_secret = 'your_app_secret'; // 获取验证请求参数 $receipt = $_POST['receipt']; $password = $_POST['password']; // 发送验证请求到 Apple 服务器 $url = 'https://buy.itunes.apple.com/verifyReceipt'; // 正式环境 //$url = 'https://sandbox.itunes.apple.com/verifyReceipt'; // 沙盒环境 $data = json_encode(array('receipt-data' => $receipt, 'password' => $password)); $options = array( 'http' => array( 'header' => "Content-type: application/json\r\n", 'method' => 'POST', 'content' => $data ) ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context); // 解析验证结果 $response = json_decode($result, true); if ($response['status'] == 0) { // 验证成功 $latest_receipt_info = $response['latest_receipt_info']; $transaction_id = $latest_receipt_info[0]['transaction_id']; $product_id = $latest_receipt_info[0]['product_id']; $purchase_date = $latest_receipt_info[0]['purchase_date']; // TODO: 在这里处理订单验证成功后的逻辑 } else { // 验证失败 $error_code = $response['status']; $error_message = $response['status_message']; // TODO: 在这里处理订单验证失败后的逻辑 } ?> ``` 以上代码中,首先定义了 app id 和 app secret,然后获取了验证请求参数 receipt 和 password,接着发送验证请求到 Apple 服务器,并解析验证结果。如果验证成功,可以获取到订单的相关信息,如 transaction_id、product_id 和 purchase_date 等,可以在这里处理订单验证成功后的逻辑;如果验证失败,可以获取到错误码和错误信息,可以在这里处理订单验证失败后的逻辑。需要注意的是,验证请求需要发送到正式环境或者沙盒环境,根据实际情况选择相应的 URL 即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值