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线上的购买凭证验证地址:htpps://buy.itunes.apple.com/verifyreceipt

测试的验证地址:htpps://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] -----交易失败 --------
复制代码

 

程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!
本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/6087199.html ,如需转载请自行联系原作者
相关文章
|
7月前
|
iOS开发 开发者
【教程】苹果 iOS 证书制作教程
【教程】苹果 iOS 证书制作教程
|
7月前
|
iOS开发 开发者
苹果iOS App Store上架操作流程详解:从开发者账号到应用发布
很多开发者在开发完iOS APP、进行内测后,下一步就面临上架App Store,不过也有很多同学对APP上架App Store的流程不太了解,下面我们来说一下iOS APP上架App Store的具体流程,如有未涉及到的部分,大家可以及时咨询,共同探讨。
|
存储 网络安全 数据安全/隐私保护
最新版 苹果 IOS AppStore证书申请全流程 包括p12文件
最新版 苹果 IOS AppStore证书申请全流程 包括p12文件
|
网络协议 iOS开发
iOS 给服务器添加 ipv6 支持 以通过苹果审核
iOS 给服务器添加 ipv6 支持 以通过苹果审核
116 0
|
5月前
|
Unix 调度 Swift
苹果iOS新手开发之Swift 中获取时间戳有哪些方式?
在Swift中获取时间戳有四种常见方式:1) 使用`Date`对象获取秒级或毫秒级时间戳;2) 通过`CFAbsoluteTimeGetCurrent`获取Core Foundation的秒数,需转换为Unix时间戳;3) 使用`DispatchTime.now()`获取纳秒级精度的调度时间点;4) `ProcessInfo`提供设备启动后的秒数,不表示绝对时间。不同方法适用于不同的精度和场景需求。
172 3
|
1月前
|
存储 人工智能 安全
【通义】AI视界|苹果停止签署iOS 18.0.1,升级用户无法降级
本文由通义自动生成,涵盖24小时内精选的五条科技资讯:奥特曼谈OpenAI未来发展方向,ChatGPT新搜索功能上线遇故障,Perplexity AI选举搜索面临挑战,马斯克谈特斯拉造手机的可能性,以及苹果停止签署iOS 18.0.1。更多精彩内容,欢迎访问通通知道。
|
5月前
|
人工智能 搜索推荐 iOS开发
苹果发布iOS 18 Beta 4,新增CarPlay 壁纸等多项功能改进
本文首发于公众号“AntDream”,探索iOS 18 Beta 4新功能与改进: CarPlay壁纸、iCloud设置访问优化、相机控制记忆、隐藏文件夹设计变更、深色/浅色模式图标同步、股票应用图标调整、iPhone镜像功能增强、控制中心蓝牙切换键、AssistiveTouch新增Type to Siri等,以及Apple Intelligence暗示。开发者可通过苹果计划提前体验。
110 12
|
5月前
|
Swift iOS开发 Kotlin
苹果iOS新手开发之Swift中实现类似Kotlin的作用域函数
Swift可通过扩展实现类似Kotlin作用域函数效果。如自定义`let`, `run`, `with`, `apply`, `also`,增强代码可读性和简洁性。虽无直接内置支持,但利用Swift特性可达成相似功能。
78 7
|
5月前
|
调度 Swift Android开发
苹果iOS新手开发之Swift中的并发任务和消息机制
Swift的消息机制类似Android的Handler,实现任务调度有三种方式: 1. **Grand Central Dispatch (GCD)**:使用`DispatchQueue`在主线程或后台线程执行任务。 2. **OperationQueue**:提供高级接口管理`Operation`对象。 3. **RunLoop**:处理事件如输入源、计时器,类似Android的`Looper`和`Handler`。 **示例**: - GCD:在不同线程执行代码块。 - OperationQueue:创建操作并执行。 - RunLoop:用Timer添加到RunLoop中。
109 2
|
6月前
|
人工智能 数据安全/隐私保护 iOS开发
苹果在WWDC24上宣布的所有内容:Apple Intelligence、集成ChatGPT的Siri、iOS 18
苹果在WWDC24上宣布的所有内容:Apple Intelligence、集成ChatGPT的Siri、iOS 18