zoukankan      html  css  js  c++  java
  • iOS App 内购 Demo

    /*注意事项:

    1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。

    2.请务必使用真机来测试,一切以真机为准。

    3.项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。

    4.如果是你自己的设备上已经绑定了自己的AppleID账号请先注销掉,否则你哭爹喊娘都不知道是怎么回事。

    5.订单校验 苹果审核app时,仍然在沙盒环境下测试,所以需要先进行正式环境验证,如果发现是沙盒环境则转到沙盒验证。

    识别沙盒环境订单方法:

    1.根据字段 environment = sandbox。

    2.根据验证接口返回的状态码,如果status=21007,则表示当前为沙盒环境。

    苹果反馈的状态码:

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

    21002 订单数据不符合格式

    21003 订单无法被验证

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

    21005 订单服务器当前不可用

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

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

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

    */

    开发内购功能,首先需要一个开发者账号,在 App store connect -> 我的App -> 功能 中申请 如图:

    有四种类型,主要看项目的需求而决定(自动续期订阅这个最为麻烦,要签署协议,而已在App 启动是请求苹果服务器,是否存在自动续期订单)

    这个图是新增一项内购产品 

    参考名称:介绍这个内购产品 

    产品ID(主要用到的):支付的时候用到 

    其他的都是在app store 中看到的介绍

    首先引用 #import <StoreKit/StoreKit.h>

    封装一个内购功能,这个在app 中可能多个地方用到

    + (instancetype)shareIAPManager;
    
    //添加内购产品
    - (void)addPurchWithProductID:(NSString *)product_id completeHandle:(IAPCompletionHandleBlock)handle;
    复制代码

    SKProductsRequestDelegate代理方法
    交易结束后用到

    - (void)completeTransaction:(SKPaymentTransaction *)transaction {
    
        NSString * productIdentifier = transaction.payment.productIdentifier;
        NSData *data = [productIdentifier dataUsingEncoding:NSUTF8StringEncoding];
        NSString *receipt = [data base64EncodedStringWithOptions:0];
    
        YMLog(@"%@",receipt);
        if ([productIdentifier length] > 0) {
            // 向自己的服务器验证购买凭证
            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;
            NSString *goodID = transaction.payment.productIdentifier;
    
            //这里缓存receipt_data,transaction_id 因为后端做校验的时候需要用到这两个字段
            YMLog(@"%@",receipt_data);
            YMLog(@"%@",transaction_id);
    
            [self retquestApplePay:receipt_data transaction_id:transaction_id goodsID:goodID];
        }
        [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
    }
    复制代码
    • (void)completeTransaction:(SKPaymentTransaction *)transaction ;

    中 获取

    NSString * productIdentifier = transaction.payment.productIdentifier;

    NSData *data = [productIdentifier
    dataUsingEncoding:NSUTF8StringEncoding];

    NSString *receipt = [data base64EncodedStringWithOptions:0];

    NSString *receipt_data = [data base64EncodedStringWithOptions:0];

    /** 事务标识符(交易编号) 交易编号(必传:防止越狱下内购被破解,校验 in_app 参数)*/

    NSString *transaction_id = transaction.transactionIdentifier;

    NSString *goodID = transaction.payment.productIdentifier;

    得到的 transaction_id receipt_data goodID需要上传到app 服务器校验(具体看后端的需求)

    //private 提交订单数据到app 服务器校验

    - (void)retquestApplePay:(NSString *)receipt_data transaction_id:(NSString *)transaction_id goodsID:(NSString *)goodsId;
    复制代码

    // 交易失败

    - (void)failedTransaction:(SKPaymentTransaction *)transaction{
        if (transaction.error.code != SKErrorPaymentCancelled) {
            [self handleActionWithType:IAPPurchFailed data:nil];
        }else{
            [self handleActionWithType:IAPPurchCancel data:nil];
        }
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
    
    #pragma mark -- 结束上次未完成的交易 防止串单
    -(void)removeAllUncompleteTransactionBeforeStartNewTransaction{
        NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
        if (transactions.count > 0) {
            //检测是否有未完成的交易
            SKPaymentTransaction* transaction = [transactions firstObject];
            if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                return;
            }
        }
    }复制代码

    IAPDemo链接:Demo

    我的掘金:https://juejin.im/post/5dca8b526fb9a04a8e3be109

  • 相关阅读:
    23种设计模式
    doraemon的python Flask框架 websocket和redis
    doraemon的python Flask框架 路由和配置
    doraemon的python Flask框架 安装以及基础应用
    doraemon的python centos的入门(五)用户和用户组权限
    doraemon的python centos的入门(四)查询和压缩文件、文件夹
    doraemon的python centos的入门(三)vim
    doraemon的python centos的入门(二)文件目录操作
    doraemon的python centos的入门(一)增删改查命令
    doraemon的python CRM项目中公户与私户转换、搜索条件的应用
  • 原文地址:https://www.cnblogs.com/ljj-Andrew-519/p/11847045.html
Copyright © 2011-2022 走看看