内购

zhun#1.通过苹果应用程序商店有三种主要赚钱的方式:
(1)直接收费(与国内大部分用户的消费习惯相悖)
(2)广告
O2O -> Online推广 & Offline交易,闭环
不要砍功能,增加内容,而不是增加功能
(3)内购:应用程序本身的增值产品,游戏装备,应用程序中增值功能同样可以内购
第三方支付:跟应用程序无关的
内购:三(苹果)七(开发商)开

2.内购的五种产品类别

(1)非消耗品(Nonconsumable)一旦购买,终身拥有
指的是在游戏中一次性购买并拥有永久访问权的物品或服务。非消耗品物品可以被用户再次下载,并且能够在用户的所有设备上共享
(2)消耗品(Consumable),买了就用,用了就没
消耗品购买不可被再次下载,根据其特点,消耗品不能在用户的设备之间跨设备使用,除非自定义服务在用户的账号之间共享这些信息

以下三种类别在iBooks中使用,目前iBooks不支持大陆市场
ISBN:每本书的一个ID
(3)免费订阅(Free subscriptions)
(4)自动续费订阅(Auto-renewing subscriptions)
(5)非自动续费订阅(Nonrenewing subscriptions)

3.内购流程

屏幕快照 2017-06-23 下午8.52.39.png
屏幕快照 2017-06-23 下午8.53.58.png

4.要使用内购,需要导入StoreKit框架

5.内购的常用方法

(1)请求有效的产品代号集合

// 1) 实例化产品请求
SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:identifiers];
// 2) 设置代理
[request setDelegate:self];

// 3) 启动请求
[request start];

提示:

  1. 实例化请求时,必须指定有效的identifiers集合,之所以如此处理,主要是为了确保提交的内购商品真的通过了苹果的审批,处于可用状态!
  2. 要想获取到准确的可用产品集合,需要通过代理方法实现
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

3 . 越狱用户无法测试内购,但是可以购买

(2)购买指定产品

  1. 内购的交易过程是通过SKPaymentTransactionObserver监控的,因此需要为IAPHelper添加交易观察者:
// 添加交易观察者对象
[[SKPaymentQueue defaultQueue]addTransactionObserver:sharedInstance];
  1. 由于发起交易需要使用SKProduct对象,因此需要使用字典记录所有可用的商品
NSMutableDictionary         *_productsDict;

(3)验证购买(在购买完成之后,验证)

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        // 购买完成
            if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
            NSLog(@"购买完成 %@", transaction.payment.productIdentifier);

            [queue finishTransaction:transaction];
        } else if (transaction.transactionState == SKPaymentTransactionStateFailed) {
            if (transaction.error.code != SKErrorPaymentCancelled) {
                NSLog(@"交易失败: %@", transaction.error.localizedDescription);
            }
        }
    }
}

(4)恢复购买(针对非消耗品)

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

购买数据记录问题——系统偏好

[[NSUserDefaults standardUserDefaults]setBool:isPurchased forKey:productId];
[[NSUserDefaults standardUserDefaults]synchronize];

案例

#import "ViewController.h"
#import <StoreKit/StoreKit.h>

@interface ViewController () <SKProductsRequestDelegate, UITableViewDataSource, UITableViewDelegate, SKPaymentTransactionObserver>

/** 所有的商品的数组 */
@property (nonatomic, strong) NSArray *products;

- (IBAction)restore:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 去自己的服务器请求所有想卖商品的ProductIds
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"iapdemo.plist" ofType:nil];
    NSArray *productArray = [NSArray arrayWithContentsOfFile:filePath];
    NSArray *productIdArray = [productArray valueForKeyPath:@"productId"];
    
    // 去苹果服务器请求可卖的商品
    NSSet *productIdSet = [NSSet setWithArray:productIdArray];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdSet];
    request.delegate = self;
    [request start];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    // 3.添加观察者(代理是一对一的关系/观察者一对多)
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    // 移除观察者
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

#pragma mark - 实现SKProductsRequest的代理方法
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    // 展示商品
    self.products = [response.products sortedArrayWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(SKProduct *obj1, SKProduct *obj2) {
        return [obj1.price compare:obj2.price];
    }];
    
    // 2.刷新表格
    [self.tableView reloadData];
}

#pragma mark - 实现tableView的数据源和代理方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.products.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"ProductCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }
    
    // 1.取出模型
    SKProduct *product = self.products[indexPath.row];
    
    // 2.给cell设置数据
    cell.textLabel.text = product.localizedTitle;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"价格:%@", product.price];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.取出模型
    SKProduct *product = self.products[indexPath.row];
    
    // 2.购买商品
    [self buyProduct:product];
}

#pragma mark - 购买商品
- (void)buyProduct:(SKProduct *)product
{
    // 1.创建票据
    SKPayment *payment = [SKPayment paymentWithProduct:product];
    
    // 2.将票据加入到交易队列中
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

#pragma mark - 实现SKPaymentQueue的回调方法
/*
 队列中的交易发生改变时,就会调用该方法
 */
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    /*
     SKPaymentTransactionStatePurchasing,    正在购买
     SKPaymentTransactionStatePurchased,     已经购买(购买成功)
     SKPaymentTransactionStateFailed,        购买失败
     SKPaymentTransactionStateRestored,      恢复购买
     SKPaymentTransactionStateDeferred       未决定
     */
    for (SKPaymentTransaction *transation in transactions) {
        switch (transation.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"用户正在购买");
                break;
                
            case SKPaymentTransactionStatePurchased:
                NSLog(@"购买成功,将对应的商品给用户");
                
                // 将交易从交易队列中移除
                [queue finishTransaction:transation];
                break;
                
            case SKPaymentTransactionStateFailed:
                NSLog(@"购买失败,告诉用户没有付钱成功");
                
                // 将交易从交易队列中移除
                [queue finishTransaction:transation];
                break;
                
            case SKPaymentTransactionStateRestored:
                NSLog(@"恢复商品,将对应的商品给用户");
                // transation.payment.productIdentifier
                // 将交易从交易队列中移除
                [queue finishTransaction:transation];
                break;
                
            case SKPaymentTransactionStateDeferred:
                NSLog(@"未决定");
                break;
            default:
                break;
        }
    }
}

#pragma mark - 恢复购买
- (IBAction)restore:(id)sender {
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 内购——应用内购买 通过苹果应用程序商店有三种主要赚钱的方式: 直接收费(与国内大部分用户的消费习惯相悖) 广告 ...
    JonesCxy阅读 904评论 1 2
  • 一.真机调试 1.什么是真机调试? 简单理解就是将App项目运行到真机上进行测试. 2.真机调试的必要 真机和模拟...
    夏天不冷阅读 1,243评论 0 7
  • 内购和广告 1. 什么是内购? 2. 为什么做内购? 3. 内购的产品类型? 非消耗品(Nonconsumable...
    翻这个墙阅读 831评论 0 0
  • 一.总说内购的内容 协议、税务和银行业务 信息填写 内购商品的添加 添加沙盒测试账号 内购代码的具体实现 内购的注...
    默默_David阅读 3,724评论 0 6
  • 在开发中我们会遇到应用内购买的场景,特别是一些虚拟物品的购买,就要用到内购. 一 什么时候需要用到内购? 大家都知...
    雪上踏痕阅读 3,262评论 2 1