苹果内购小结 - iOS

Posted survivors

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了苹果内购小结 - iOS相关的知识,希望对你有一定的参考价值。

此篇针对 ios 支付进行一次小结,很久没碰这块了,有些方法 Apple 官方也进行了优化,故也将随之进行更新.

首先,code 部分将分为两部分,一部分在 appdelegate 中,另一部分单独封装在了一个类中执行,需要使用的地方调用的接口方法.

其次,大体支付流程为获取到充值价格订单列表后,选择对应的价格后向 Apple 发起支付请求,接收到 Apple 支付回调后,根据结果处理相关逻辑,最后将处理完成的结果反馈至用户.

其过程中会分为几个环节来处理:

若支付失败则执行异常处理并将最后处理结果信息反馈至用户;

若支付成功则对支付凭证校验,此篇文章中的校验过程分为两部分,先是由客户端自行校验,若校验成功则将相关用户信息和支付凭证发送至服务端进行二次校验;

其中,客户端优先进行交易凭证校验,校验失败则将校验的异常处理信息反馈至用户;反之,校验成功则再次将相关用户信息和本次支付凭证数据一并发送至服务端进行二次校验,最终将双重验证后的结果信息反馈至用户,从而为了避免刷单的情况.

最后,文章中具体处理逻辑中可能会因为需求的不同与实际有些小的出入,但大体流程应该是一致的,也会对应添加相应的注释,若存在不清楚的地方可以帖子下方留言沟通交流.

 

大致支付流程:

1.苹果APP(商家)

 2.告诉苹果Store服务器要卖的商品

 3.苹果审核完(告诉你是否可以卖)

 4.用户(买商品)

 5.苹果APP(商家)

 6.开发票给(用户)

 7.用户(拿着发票去苹果Store服务器付款)

 8.付款成功(用户在APP里获得服务商品)

 注:如果要模拟测试内购,需要用真机才可以测试

 

凭证校验地址:

 开发环境:  https://sandbox.itunes.apple.com/verifyReceipt

 生产环境:  https://buy.itunes.apple.com/verifyReceipt

 

凭证校验异常 code 参照码:

内购验证凭据返回结果状态码说明(status 状态)

             21000 App Store无法读取你提供的JSON数据

             21002 收据数据不符合格式

             21003 收据无法被验证             

             21004 你提供的共享密钥和账户的共享密钥不一致             

             21005 收据服务器当前不可用

             21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中

             21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证

             21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

 

Code 如下:

/*
 支付_管理类(Apple Pay)
 */
 
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
 
//充值金额类型
typedef NS_ENUM(NSInteger, buyCoinsTag) {
    IAP0p20=20,
    IAP1p100,
    IAP4p600,
    IAP9p1000,
    IAP24p6000,
};
 
@import WebKit;
 
@interface PaymentManager : NSObject <SKPaymentTransactionObserver, SKProductsRequestDelegate> {
    /** 购买类型*/
    int buyType;
}
 
/** 产品 ID*/
@property (nonatomic, strong) NSString *productID;
 
/** Init*/
+ (PaymentManager *)manager;
 
#pragma mark - 方法相关
/** 判断当前环境是否支持支付购买*/
- (void)buyInfo:(NSDictionary *)buyDic AndViewController:(UIViewController *)vc AndSn:(NSString *)sn;
 
/**
 校验交易凭证 - App Store (Plan A)
 
 该方法以客户端为基准:
 若客户端校验结果失败,则服务器不再进行二次校验;
 若客户端校验结果成功,则服务器再次进行二次校验.
 
 @param transaction 交易事物
 */
- (void)verifyTransaction:(SKPaymentTransaction *)transaction;
 
/** 交易失败*/
- (void)failedTransaction:(SKPaymentTransaction *)transaction;
 
 
 
@end

  

/*
 1.苹果APP(商家)
 2.告诉苹果Store服务器要卖的商品
 3.苹果审核完(告诉你是否可以卖)
 4.用户(买商品)
 5.苹果APP(商家)
 6.开发票给(用户)
 7.用户(拿着发票去苹果Store服务器付款)
 8.付款成功(用户在APP里获得服务商品)
 
 注:如果要模拟测试内购,需要用真机才可以测试
 */
 
#import "PaymentManager.h"
#import <objc/runtime.h>
#import "GTMBase64.h"
#import "RequestToolManager.h"
 
 
//在内购项目中创的商品单号
#define ProductID_IAP0p20       @"***此处与实际内购价格配置表为准***" //20
#define ProductID_IAP1p100      @"***此处与实际内购价格配置表为准***" //100
#define ProductID_IAP4p600      @"***此处与实际内购价格配置表为准***" //600
#define ProductID_IAP9p1000     @"***此处与实际内购价格配置表为准***" //1000
#define ProductID_IAP24p6000    @"***此处与实际内购价格配置表为准***" //6000
 
#define PaySucceed      @"充值成功"
#define PayFailed       @"充值失败"
#define PayException    @"订单发生异常,请联系客服"
#define RequestError    @"支付成功,等待验证"
 
/*
 AppStore增加了验证内购(In App Purchasement)的方法, 就是苹果提供一个url地址:
 
 当购买成功时, 会得到苹果返回的一个收据(receipt), 苹果推荐的方法是将收据发给开发者的 server 服务端, 由 server 服务端向上述地址发起请求(post http)消息, 进行验证, 苹果将校验结果返回.此次交易凭证是真购买凭证还是伪购买凭证.
 
 开发环境地址:  https://sandbox.itunes.apple.com/verifyReceipt
 生成环境地址:  https://buy.itunes.apple.com/verifyReceipt
 */
 
#define SANDBOX_VERIFY_RECEIPT_URL          [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"]
#define APP_STORE_VERIFY_RECEIPT_URL        [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]
 
#ifdef DEBUG
#define VERIFY_RECEIPT_URL SANDBOX_VERIFY_RECEIPT_URL
#else
#define VERIFY_RECEIPT_URL APP_STORE_VERIFY_RECEIPT_URL
#endif
 
 
@interface PaymentManager () <SKPaymentTransactionObserver,SKProductsRequestDelegate, SKRequestDelegate>
 
/** 订单编号*/
@property (nonatomic, strong) NSString *tradeNo;
 
@end
 
@implementation PaymentManager
 
#pragma mark - Init
+ (PaymentManager *)manager {
    static id sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}
 
#pragma mark - ************************************ 支付初始化
/**
 Pay Method 支付初始化
 具体方法介绍详见上面注释中连接地址
 注:切记绑定 <SKPaymentTransactionObserver> 事件,设置购买队列的监听器,实时监听跟踪订单状态,避免发送丢单的意外,即 [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; 
 
 @param buyDic  支付所对应的产品信息
 @param vc      当前 VC 控件
 @param sn      用户信息
 */
- (void)buyInfo:(NSDictionary *)buyDic AndViewController:(UIViewController *)vc AndSn:(NSString *)sn {
    //判断网络是否可用
    if (k_NetWorkIsReachable) {// 可用
        self.productID = [NSString stringWithFormat:@"%@", [buyDic objectForKey:@"productId"]];
        // 获取订单编号
        NSMutableDictionary *dicParameter = [NSMutableDictionary dictionary];
        [dicParameter setValue:@"Apple Pay" forKey:@"payType"];
        [dicParameter setValue:sn forKey:@"user"];
        [dicParameter setValue:@"iPhone" forKey:@"deviceType"];
        [dicParameter setValue:self.productID forKey:@"productId"];
        kWeakSelf(self);
        [[HttpRequestManager shareInstance] PayPOST:URL_ApplePay parameters:dicParameter isEncipherment:YES success:^(id responseObject) {
            NSDictionary *result = [NSDictionary dictionaryWithDictionary:responseObject];
            // 订单编号
            NSString *tradeNo = [NSString stringWithFormat:@"%@", [result objectForKey:@"tradeNo"]];
            
            //设置购买队列的监听器
            [[SKPaymentQueue defaultQueue] addTransactionObserver:weakself];
            
            //判断当前是否可支付
            if ([SKPaymentQueue canMakePayments]) {
                NSLog(@"允许程序内付费购买");
                
                //请求产品数据
                [weakself fetchProductInformationForIds:weakself.productID AndTradeNo:tradeNo];
                
            } else {
                NSLog(@"不允许程序内付费购买");
 
                // Callback
                //[MBProgressHUD showError:@"请开启手机内付费购买功能" toView:vc.view];
                [self hudAlertMessage:@"请开启手机内付费购买功能"];
            }
            
        } failure:^(NSError *error) {
            NSLog(@"buyInfo: %@",error);
 
            // Callback
            //[MBProgressHUD showError:@"服务异常,请重新尝试" toView:vc.view];
            [self hudAlertMessage:@"服务异常,请重新尝试"];
        }];
        
        
    }
    else {
        // Callback
        //[MBProgressHUD showError:@"暂无网络" toView:vc.view];
        [self hudAlertMessage:@"暂无网络"];
    }
    
}
 
 
 
 
/**
 获取对应的产品数据信息
 @param productIds  产品 id
 @param tradeNo     订单编号
 */
- (void)fetchProductInformationForIds:(NSString *)productIds AndTradeNo:(NSString *)tradeNo {
    NSLog(@"------------请求对应的产品信息------------");
    self.tradeNo = tradeNo;
    NSArray *product = [[NSArray alloc] initWithObjects:productIds, nil];
    //为该产品标识符创建一个集合
    NSSet *nsSet = [NSSet setWithArray:product];
    //创建该产品请求对象,并将上面的集合进行初始化它
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsSet];
    request.delegate = self;
    //向 App Store 发起请求
    [request start];
}
 
 
 
#pragma mark - ************************************ 交易处理中
/**
 获取 App Store 产品反馈信息
 注:设置请求协议代理:    <SKProductsRequestDelegate>
 
 @param request     请求
 @param response    应用结果
 */
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSLog(@"------------收到产品反馈消息------------");
    /** 当前产品信息*/
    NSArray *myProduct = response.products;
    if (0 == myProduct.count) {
        NSLog(@"------------暂无商品------------");
        // Callback
        //[MBProgressHUD showError:@"App Store支付异常,请重新尝试" toView:vc.view];
        [self hudAlertMessage:@"App Store支付异常,请重新尝试"];
        
        return;
        
    }
    else {
        NSLog(@"------------预购商品------------");
        
        SKProduct *p = nil;
        for (SKProduct *product in myProduct) {
            NSLog(@"*** 产品信息相关[product info] ***
1.描述信息(SKProduct): %@
2.产品标题(Title): %@
3.产品描述信息(Description): %@
4.产品价格(Price): %@
5.产品 id(Product id): %@", product, product.localizedTitle, product.localizedDescription, product.price, product.productIdentifier);
 
            if([product.productIdentifier isEqualToString:self.productID]){
                p = product;
            }
        }
        
        if (p != nil) {
            // 将要购买的产品
            SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p];// The product is available, let‘s submit a payment request to the queue
            NSLog(@"------------发送购买请求------------");
            // 发起准备购买流程(异步)
            [[SKPaymentQueue defaultQueue] addPayment:payment];
        }
    }
}
 
 
 
#pragma mark - ************************************ 交易校验中
/**
 校验交易凭证 - App Store
 该方法以客户端为基准:
 若客户端校验结果失败,则服务器不再进行二次校验;
 若客户端校验结果成功,则服务器再次进行二次校验.
 
 @param transaction 交易事物
 */
- (void)verifyTransaction:(SKPaymentTransaction *)transaction {
    /*
        使用如下方法获取购买凭证也 ok,则需要对 data 进行判空操作,
        NSData *transactionReceipt = [PaymentManager receiptDataFromTransaction:transaction];
        // 若 data 为空则执行如下方法
        SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
        receiptRefreshRequest.delegate = self;
        [receiptRefreshRequest start];
        return;
    */
    // 从沙盒中获取到购买凭据
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
        NSData *transactionReceipt = [NSData dataWithContentsOfURL:receiptURL];
        NSString *encodeStr = [transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
        /*
         验证自动订阅的有效 receipt 示例
         {
             "receipt-data"  : "...",
             "password"      : "..."
         }
         */
        // 拼接请求数据 
        NSString *bodyStr = [NSString stringWithFormat:@"{"receipt-data" : "%@"}", encodeStr];
        NSData *bodyData = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
        // 创建请求,验证凭证,苹果服务器比较坑,建议超时时长设置稍稍长一些
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:VERIFY_RECEIPT_URL
                                                               cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                           timeoutInterval:10.0f];
        request.HTTPBody = bodyData;
        request.HTTPMethod = @"POST";
        
        NSError *error = nil;
        // 创建连接并发送同步请求,获得官方的验证JSON结果
        NSData *responseData = [NSURLConnection sendSynchronousRequest:request
                                                     returningResponse:nil
                                                                 error:&error];
        if (error) {
            NSLog(@"App Store 验证购买过程中发生错误,错误信息: %@",error.localizedDescription);
            
            // Callback
            //[MBProgressHUD showError:@"网络请求超时,请重试" toView:vc.view];
            [self hudAlertMessage:@"网络请求超时,请重试"];
            
        }
        else {
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:responseData
                                                                   options:NSJSONReadingAllowFragments
                                                                     error:&error];
            
            /*
             内购验证凭据返回结果状态码说明(status 状态)
             
             21000 App Store无法读取你提供的JSON数据
             
             21002 收据数据不符合格式
             
             21003 收据无法被验证
             
             21004 你提供的共享密钥和账户的共享密钥不一致
             
             21005 收据服务器当前不可用
             
             21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
             
             21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
             
             21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
             */
            NSString *status = [NSString stringWithFormat:@"%@", [result objectForKey:@"status"]];
            if (0 == [status intValue]) {
                NSLog(@"App Store 验证购买 --- 成功");
                NSDictionary *dicReceipt = [NSDictionary dictionaryWithDictionary:[result objectForKey:@"receipt"]];
                NSArray *arrInApp = [dicReceipt objectForKey:@"in_app"];
                // 注:此处 in_app 字段中数据可能为多个,需进行循环
                NSMutableDictionary *dicInAppReult = [NSMutableDictionary dictionary];
                for (NSDictionary *dict in arrInApp) {
                    /** 产品标识*/
                    NSString *product_id = [NSString stringWithFormat:@"%@", [dict objectForKey:@"product_id"]];
                    /** 事物标识*/
                    NSString *transaction_id = [NSString stringWithFormat:@"%@", [dict objectForKey:@"transaction_id"]];
                    [dicInAppReult setValue:product_id forKey:transaction_id];
                }
 
                NSString *bundle_id = [NSString stringWithFormat:@"%@", [dicReceipt objectForKey:@"bundle_id"]];
                NSString *application_version = [NSString stringWithFormat:@"%@", [dicReceipt objectForKey:@"application_version"]];
                if ([bundle_id isEqualToString:kGetBundleId] &&
                    [application_version isEqualToString:kAppBundle] &&
                    [dicInAppReult.allKeys containsObject:transaction.transactionIdentifier] &&
                    [[dicInAppReult objectForKey:transaction.transactionIdentifier] isEqualToString:transaction.payment.productIdentifier]
                    ) {
                    NSLog(@"App Store 凭证验证 --- 成功");
                    
                    // 交易成功且凭证验证成功向服务端提交凭证进行处理
                    [self commitSeversSucceeWithTransaction:transaction];
                    
                }
                else {
                    NSLog(@"App Store 凭证验证 --- 失败");
                    
                    // Remove the transaction from the payment queue.
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    
                    // Callback
                    //[MBProgressHUD showError:PayFailed toView:vc.view];
                    [self hudAlertMessage:PayFailed];
                }
                
            }
            else {
                NSLog(@"App Store 凭证验证 --- 失败: %@", error.localizedDescription);
                
                // Remove the transaction from the payment queue.
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                
                // Callback
                //[MBProgressHUD showError:PayFailed toView:vc.view];
                [self hudAlertMessage:PayFailed];
            }
        }
        
    }
    else {
        SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
        receiptRefreshRequest.delegate = self;
        [receiptRefreshRequest start];
        
        return;
    }
}
 
/**
 获取交易凭证
 @param transaction 交易事物
 @return 结果集
 */
+ (NSData *)receiptDataFromTransaction:(SKPaymentTransaction *)transaction {
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
        // 从沙盒中获取到购买凭据
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
        if (!receiptData) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
            if ([transaction respondsToSelector:@selector(transactionReceipt)]) {
                //Works in iOS3 - iOS8, deprected since iOS7, actual deprecated (returns nil) since
                receiptData = transaction.transactionReceipt;
            }
#pragma clang diagnostic pop
        }
        return receiptData;
        
    }
    else {
        return nil;
    }
}
 
 
 
#pragma mark - ************************************ 交易成功 - 向公司服务器验证购买凭证
/**
 交易成功 - 向公司服务器验证购买凭证
 status:0:订单开始,1:充值成功,2:充值失败
 
 @param transaction 交易事务
 */
- (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"------------交易成功向公司服务器验证购买凭证------------");
    
#pragma mark - 交易凭证相关
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
        // 取 receipt 的时候要判空,如果文件不存在,就要从苹果服务器重新刷新下载 receipt 了
        // SKReceiptRefreshRequest 刷新的时候,需要用户输入 Apple ID,同时需要网络状态良好
        SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
        receiptRefreshRequest.delegate = self;
        [receiptRefreshRequest start];
        return;
    }
    NSData *data = [NSData dataWithContentsOfURL:receiptURL];
    /** 交易凭证*/
    NSString *receipt_data = [data base64EncodedStringWithOptions:0];
    /** 事务标识符(交易编号)  交易编号(必传:防止越狱下内购被破解,校验 in_app 参数)*/
    NSString *transaction_id = transaction.transactionIdentifier;
    
    // 此处忽略,纯好奇心所驱,一个神奇的 data,拆不出来 ... 你赢了
//    NSString * test1 = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//    NSString * test2 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//    NSError *error;
//    NSDictionary * test3 = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
    
    // 判空相关
    if (receipt_data == nil) {
        receipt_data = @"";
    }
    
    if (transaction_id == nil) {
        transaction_id = @"";
    }
    
    NSLog(@"交易凭证:
%@", receipt_data);
    NSLog(@"事务标识符(交易编号):
%@",  transaction_id);
    NSLog(@"产品标识符(内购产品编号) --- productIdentifier:
%@", transaction.payment.productIdentifier);
//    NSLog(@"交易日期
Date: %@,Date(String): %@", transaction_date, strTransaction_date);
//    NSLog(@"事物状态:
%@", transaction_state);
    
    
    
    NSMutableDictionary *dicParameter = [NSMutableDictionary dictionary];
    [dicParameter setValue:kAppBundle forKey:@"appBundle"];
    [dicParameter setValue:transaction_id forKey:@"transactionId"];// 查明交易标识符(防止越狱下内购被破解,校验 in_app 参数)
    [dicParameter setValue:receipt_data forKey:@"receiptData"];// 收到的收据,即收据证明 transactionReceipt
    kWeakSelf(self);
    // Request
    [[HttpRequestManager shareInstance] PayPOST:URL_ApplePay parameters:dicParameter isEncipherment:NO success:^(id responseObject) {
        NSDictionary *result = [NSDictionary dictionaryWithDictionary:responseObject];
        NSString *status = [NSString stringWithFormat:@"%@", [result objectForKey:@"payStatus"]];
        NSLog(@"Pay Callback Result: %@", result);
        // Callback
        if ([status isEqualToString:@"success"]) {// 成功
            // Callback
            //[MBProgressHUD showError:PaySucceed toView:vc.view];
            [self hudAlertMessage:PaySucceed];
        }
        else {
            // Callback
            //[MBProgressHUD showError:PayException toView:vc.view];
            [self hudAlertMessage:PayException];
        }
        
        // Remove the transaction from the payment queue.
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        
    } failure:^(NSError *error) {
        NSLog(@"commitSeversSucceeWithTransaction: %@", error.localizedDescription);
        
        // Callback
        //[MBProgressHUD showError:RequestError toView:vc.view];
        [self hudAlertMessage:RequestError];
    }];
}
 
 
 
#pragma mark - ************************************ 交易失败相关
/*
 
 内购验证凭据返回结果状态码说明
 
 21000 App Store无法读取你提供的JSON数据
 
 21002 收据数据不符合格式
 
 21003 收据无法被验证
 
 21004 你提供的共享密钥和账户的共享密钥不一致
 
 21005 收据服务器当前不可用
 
 21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中
 
 21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证
 
 21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
 
 */
#pragma mark - 交易失败 -> 弹出错误信息 Error
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"*** 交易失败 Error code: (%d)",transaction.error.code);
    if (transaction.error != nil) {
        switch (transaction.error.code) {
                
            case SKErrorUnknown:
                NSLog(@"SKErrorUnknown --- 未知的错误,您可能正在使用越狱手机");
                break;
                
            case SKErrorClientInvalid:
                NSLog(@"SKErrorClientInvalid --- 当前苹果账户无法购买商品(如有疑问,可以询问苹果客服)");
                break;
                
            case SKErrorPaymentCancelled:
                NSLog(@"SKErrorPaymentCancelled --- 订单已取消");
                break;
                
            case SKErrorPaymentInvalid:
                NSLog(@"SKErrorPaymentInvalid --- 订单无效(如有疑问,可以询问苹果客服)");
                break;
                
            case SKErrorPaymentNotAllowed:
                NSLog(@"SKErrorPaymentNotAllowed --- 当前苹果设备无法购买商品(如有疑问,可以询问苹果客服)");
                break;
                
            case SKErrorStoreProductNotAvailable:
                NSLog(@"SKErrorStoreProductNotAvailable --- 当前商品不可用");
                break;
                
            default:
                
                NSLog(@"No Match Found for error -- 未知错误");
                break;
        }
    }
    
    // Callback
    //[MBProgressHUD showError:PayFailed toView:vc.view];
    [self hudAlertMessage:PayFailed];
 
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
 
-(void)requestDidFinish:(SKRequest *)request {
    NSLog(@"------------反馈信息结束------------");
}
 
#pragma mark - 交易失败 - 请求失败 -> 弹出错误信息 Error
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"------------弹出错误信息------------");
    
    // Callback
    //[MBProgressHUD showError:error.localizedDescription toView:vc.view];
    [self hudAlertMessage:error.localizedDescription];
}
 
 
 
#pragma mark - 交易恢复处理
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
 
#pragma mark - 完成付款队列恢复完成交易
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentTransaction *)transaction {
    NSLog(@"****** 完成付款队列恢复完成交易: %@", transaction.transactionIdentifier);
 
    /*
     NSMutableArray *productIDsToRestore =  From the user ;
     SKPaymentTransaction *transaction =  Current transaction ;
     if ([productIDsToRestore containsObject:transaction.transactionIdentifier]) {
        // Re-download the Apple-hosted content, then finish the transaction
        // and remove the product identifier from the array of product IDs.
     } else {
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
     }
     */
 
}
 
#pragma mark - 付款队列
- (void)paymentQueue:(SKPaymentQueue *)paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error {
    NSLog(@"------------付款队列(当从用户的购买历史记录向队列添加事务时遇到错误时发送)------------
%@", error.localizedDescription);
}
 
#pragma mark - 购买交易
- (void)purchasedTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"------------购买交易------------");
    NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
    [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
}
 
 
 
/**
 提示框
 
 @param msg 提示语
 */
- (void)hudAlertMessage:(NSString *)msg {
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@""
                                                        message:msg
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"确定",nil)
                                              otherButtonTitles:nil];
    [alerView show];
}
 
 
 
#pragma mark - ************************************ Connection delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    NSLog(@"connection delegate --- %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    
}
 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    switch([(NSHTTPURLResponse *)response statusCode]) {
        case 200:
        case 206:
            break;
        case 304:
            break;
        case 400:
            break;
        case 404:
            break;
        case 416:
            break;
        case 403:
            break;
        case 401:
        case 500:
            break;
        default:
            break;
    }
}
 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"connection:(NSURLConnection *)connection didFailWithError:(NSError *)error: %@", error.localizedDescription);
}
 
- (void)dealloc {
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];// 解除监听
}

  

AppDelegate 中 Code:

#import "AppDelegate.h"
// Apple pay
#import <StoreKit/StoreKit.h>
#import "PaymentManager.h"
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    // 将观察者添加到支付队列中
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
    return YES;
}
 
 
 
#pragma mark - ****************************** Apple Pay
/**
 监听购买交易结果 transactions
 
 @param queue           交易队列
 @param transactions    交易事物
 */
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions {
    
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:// 商品添加进列表
                // Transaction is being added to the server queue.
                NSLog(@"------------商品添加进列表------------");
                /* 不用finish,继续观察支付队列,等待transaction状态改变 */
                
                break;
            case SKPaymentTransactionStateDeferred:
                // The transaction is in the queue, but its final status is pending external action.
                NSLog(@"------------事务在队列中,但其最终状态是等待外部操作");
                /* 不用finish,继续观察支付队列 */
                
                break;
            case SKPaymentTransactionStateFailed:// 交易失败
                // Transaction was cancelled or failed before being added to the server queue.
                NSLog(@"------------交易失败: %@", transaction.error.localizedDescription);
                /* 检查错误并根据需要处理,然后调用 finishTransaction */
                
                [[PaymentManager manager] failedTransaction:transaction];
                
                break;
            case SKPaymentTransactionStatePurchased:// 交易完成
                // Transaction is in queue, user has been charged.  Client should complete the transaction.
                NSLog(@"------------交易完成: %@", transaction.payment.productIdentifier);
                /* 分发内容给用户,然后调用 finishTransaction */
                [[PaymentManager manager] verifyTransaction:transaction];
                
                break;
            case SKPaymentTransactionStateRestored:// 已经购买过该商品
                // Transaction was restored from user‘s purchase history.  Client should complete the transaction.
                NSLog(@"------------已经购买过该商品");
                /* 分发内容给用户,然后调用 finishTransaction */
                [[PaymentManager manager] verifyTransaction:transaction];
                
                break;
                
            default:
                break;
        }
    }
}

  

以上便是此次内购支付相关小结,还望多多指点交流!

以上是关于苹果内购小结 - iOS的主要内容,如果未能解决你的问题,请参考以下文章

iOS苹果内购(详细步骤)

iOS:苹果内购实践

Flutter iOS内购(代码篇-全网最全)

怎么跨区内购苹果

iOS: 实现苹果的内购

苹果内购艰辛审核之路