zoukankan      html  css  js  c++  java
  • iOS: 实现苹果的内购

    一、介绍:

    在个人开发的app上架到AppStore后,苹果官方允许我们将自己的app在appstore上进行付费使用,也就是所谓的内购。其中,支付方式规定的必须是苹果的支付方式:应用内支付。

    二、流程:

    1、后台设置

    (1)配置Developer.apple.com,为应用建立一个不带通配符的App ID

    (2)用该应用的App ID生成和安装相应的Provisioning Profile文件

    2、配置iTunes Connect

    (1)用该App ID创建一个新的应用;

    (2)在该应用中,创建应用内付费项目,选择付费类型,通常可选的是可重复消费的(Consumenable)和永久有效的(Non-Consumenable)两种,然后设置好价格、Product ID、购买介绍和截图,这里的Product ID是必须记住的,后面开发的时候要用到;

    (3)添加一个用于在sandbox付费的测试用户,注意苹果对测试用户的密码要求和正是账号一样,至少8位,并且包包含数字和大小写字母;

    (4)填写相关的税务。银行和联系人

    3、iOS端开发

    (1)在工程中引入storeKit.framework和#import <storeKit/storeKit.h>;

    (2)获取所有的付费Product ID列表。这个可以用常量存储到本地,也可以由自己的服务器返回;

    (3)制作一个界面(如tableView),显示所有的应用内付费项目。这些应用内付费项目的价格和介绍信息可以是自己的服务器返回。但如果是不带服务器的单机游戏应用或者工具类应用,则可以通过向App Store查询所得;

    (4)当用户点击一个IAP项目,我们需要先查询用户是否允许应用内付费,如果不允许则不进行接下来的步骤;

    (5)先通过该IAP的ProductID向AppStore查询,获取SKPayment实例,然后通过SKPaymentQueue的addPayment方法发起一个购买的操作;

    (6)在ViewdidLoad方法中,将购买页面设置成购买额observe;

    (7)当用户购买的操作有结果时,就会触发调用回调函数,相应的进行处理;

    (8)服务器验证凭证(可选项)。如果购买成功,我们需要将凭证发送到服务器上进行验证。考虑到网路异常情况,iOS端的发送凭证操作应该可以持久化,如果程序退出、崩溃或者网络异常,可以恢复重试。

    4、服务端开发

    (1)接收iOS端发过来的购买凭证;

    (2)判断凭证是否已经存在,是否验证过,然后进行存储;

    (3)将该凭证发送到苹果的服务器验证,并将结果返回给客户端;

    (4)如果需要,修改用户相应的会员权限。

    注意:考虑到网络异常的情况,服务器的验证应该是一个可恢复的队列,如果失败了,应该进行重试。

    苹果AppStore线上的购买凭证验证地址:https://buy.itunes.apple.com/verifyreceipt

    测试的验证地址:https://sandbox.itunes.apple.com/verifyreceipt

    三、iOS基本代码如下:

    //  ViewController.m
    //
    //  Created by 夏远全 on 16/11/20.
    //  Copyright © 2016年 广州市东德网络科技有限公司. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <StoreKit/StoreKit.h>
    
    @interface ViewController ()<SKProductsRequestDelegate,SKPaymentTransactionObserver>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //监听购买结果
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    }
    
    -(void)dealloc{
        
        //移除购买监听
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    
    
    //用户点击一个IAP项目时,首先查询用户是否允许应用内付费(tableViewCell点击时,传递内购商品ProductId,ProductID可以提前存储到本地,用到时直接获取即可)
    -(void)validateIsCanBought{
        
        if ([SKPaymentQueue canMakePayments]) {
            [self getProductInfo:@[@"ProductIds"]];
        }else{
            NSLog(@"失败,用户禁止应用内付费购买");
        }
    }
    
    //通过该IAP的Product ID向App Store查询,获取SKPayment实例,接着通过SKPaymentQueue的addPayment方法发起一个购买的操作
    //下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目,否则会查询失败
    -(void)getProductInfo:(NSArray *)productIds{
        
        NSSet *set = [NSSet setWithArray:productIds];
        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
        request.delegate = self;
        [request start];
    }
    
    #pragma mark - SKProductsRequestDelegate
    //查询的回调函数
    -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
        
        //获取到的所有内购商品
        NSArray *myProducts = response.products;
        
        //判断个数
        if (myProducts.count==0) {
            NSLog(@"无法获取产品信息,购买失败。");
            return;
        }
        
        //发起一个购买操作
        SKPayment *payment = [SKPayment paymentWithProduct:myProducts[0]];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    
    #pragma mark - SKPaymentTransactionObserver
    //当用户购买的操作有结果时,就会触发下面的回调函数,相应进行处理
    -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
        
        for (SKPaymentTransaction *transaction in transactions) {
            
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased:  //交易完成
                    NSLog(@"transactionIdentifier = %@",transaction.transactionIdentifier);
                    [self completeTransaction:transaction];
                    break;
                case SKPaymentTransactionStateFailed:     //交易失败
                    [self failedTransaction:transaction];
                    break;
                case SKPaymentTransactionStateRestored:  //已经购买过该商品
                    [self restoreTransaction:transaction];
                    break;
                case SKPaymentTransactionStatePurchasing: //商品添加进列表
                    NSLog(@"商品添加进列表");
                    break;
                default:
                    break;
            }
        }
    }
    
    //交易完成后的操作
    -(void)completeTransaction:(SKPaymentTransaction *)transaction{
        
        NSString *productIdentifier = transaction.payment.productIdentifier;
        NSData *transactionReceiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
        NSString *receipt = [transactionReceiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
        
        if ([productIdentifier length]>0) {
            //向自己的服务器验证购买凭证
            NSLog(@"%@",receipt);
        }
        
        //移除transaction购买操作
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
    
    //交易失败后的操作
    -(void)failedTransaction:(SKPaymentTransaction *)transaction{
        
        if (transaction.error.code != SKErrorPaymentCancelled) {
            NSLog(@"购买失败");
        }else{
            NSLog(@"用户取消交易");
        }
        //移除transaction购买操作
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
    
    //已经购买过该商品
    -(void)restoreTransaction:(SKPaymentTransaction *)transaction{
     
        //对于已购买商品,处理恢复购买的逻辑
        //移除transaction购买操作
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
    
    @end

     

    三、个人参考demo

    https://github.com/xiayuanquan/AppleStoreKitDemo.git

    效果图:

     

    打印日志:

    2017-02-28 18:21:08.009166 StoreKit[18455:370113] [MC] Reading from private effective user settings.
    2017-02-28 18:21:08.012 StoreKit[18455:370113] ---------请求对应的产品信息------------
    2017-02-28 18:21:08.015 StoreKit[18455:370113] 允许程序内付费购买
    2017-02-28 18:21:09.193 StoreKit[18455:370113] -----------收到产品反馈信息--------------
    2017-02-28 18:21:09.193 StoreKit[18455:370113] 产品Product ID:(
    )
    2017-02-28 18:21:09.194 StoreKit[18455:370113] 产品付费数量: 1
    2017-02-28 18:21:09.194 StoreKit[18455:370113] product info
    2017-02-28 18:21:09.194 StoreKit[18455:370113] SKProduct 描述信息<SKProduct: 0x600000004950>
    2017-02-28 18:21:09.194 StoreKit[18455:370113] 产品标题 1元=10金币
    2017-02-28 18:21:09.195 StoreKit[18455:370113] 产品描述信息: 通过虚拟金币充值,获取会员资格
    2017-02-28 18:21:09.195 StoreKit[18455:370113] 价格: 0.99
    2017-02-28 18:21:09.195 StoreKit[18455:370113] Product id: www.biaojiepay.com.StoreKit01
    2017-02-28 18:21:09.195 StoreKit[18455:370113] ---------发送购买请求------------
    2017-02-28 18:21:09.196 StoreKit[18455:370113] -----paymentQueue--------
    2017-02-28 18:21:09.196 StoreKit[18455:370113] -----商品添加进列表 --------
    2017-02-28 18:21:09.196 StoreKit[18455:370113] ----------反馈信息结束--------------
    2017-02-28 18:21:10.125470 StoreKit[18455:370288] subsystem: com.apple.BackBoardServices.fence, category: Observer, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
    2017-02-28 18:21:10.127183 StoreKit[18455:370113] subsystem: com.apple.BackBoardServices.fence, category: Workspace, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
    2017-02-28 18:21:10.127707 StoreKit[18455:370113] subsystem: com.apple.BackBoardServices.fence, category: Trace, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
    2017-02-28 18:21:12.044 StoreKit[18455:370113] -----paymentQueue--------
    2017-02-28 18:21:12.045 StoreKit[18455:370113] 失败
    2017-02-28 18:21:12.048 StoreKit[18455:370113] -----交易失败 --------
  • 相关阅读:
    android报表图形引擎(AChartEngine)demo解析与源码
    eclipse开发android程序常见问题解决办法
    android中监听layout布局
    android中LayoutInflater详解与使用
    WordPress标题函数wp_title()详解
    内网DMZ外网之间的访问规则
    ARTS打卡计划第一周-Share-系统字典模块的设计
    ARTS打卡计划第一周-Tips-ControllerAdvice的使用
    ARTS打卡计划第一周-Review
    ARTS打卡计划第一周-Algorithm
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/6087199.html
Copyright © 2011-2022 走看看