iOS内置付费开发笔记
2015-08-30 18:37
489 查看
原文链接:http://www.cocoachina.com/industry/20140528/8585.html
内置付费到底有多流行? 我截取了2014年5月12日的榜单大约70%左右的App都使用了内置付费的功能。本文向您介绍iOS开发内置付费流程。
一、概述
内置付费到底有多流行?
我截取了2014年5月12日的榜单,红色为包含内置付费的App,蓝色是不包含内置付费的app。只需要看看颜色,满眼一片红。大约70%左右的App都使用了内置付费的功能。
![](http://img.blog.csdn.net/20150830183003690?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
内置付费的工作流程
其实和所有的购物 并无二致,以沃尔玛商城为例:
1. 沃尔玛商城卖什么?
2. 把货物运放到货架上供客户挑选。
3. 沃尔玛收款。
App Store上也是如此:
1. Apple 允许卖什么 ?
2. 软件上架任由用户挑选。
3. Apple收款。
还有一点非常相似,沃尔玛需要收商家的进店费,上架费,店庆费... Apple则收取30%的分成。
Apple允许卖什么类型的内置付费产品?
四类:
1. Non-Consumable: 比如游戏中的关卡, 购买以后随意玩任意多次; 比如美国的房子,70年后还是你的。
2. Consumable: 比如游戏中的金币, 消耗品;比如汽油, 消耗品。用光了就得去再去买。
3. Auto-Renewable Subscriptions: 暂略下不表。
4. Free Subscriptions: 暂略下不表。
备注:(/article/8990856.html)
Non-Consumable:用户只需购买一次,不需要再次购买,即可在多台设备上拥有之(restore技术)。
Consumable:用户可以购买多次(不限定次数)。例如金币。
Auto-Renewable Subscriptions:为了收到app更新的内容,用户需要定期支付款项。(目前仅适用于杂志或者新闻类型的app)。
Free Subscriptions:类似于Auto-Renewable Subscriptions类型,但免费,仅适用于杂志类型的app。
Non-Renewing Subscriptions:假如你可用Auto-Renewable(你的app不属于杂志或者新闻类型),但是你仍想提供基于时间限制的访问内容,可以选择Non-Renewing来试试。比如你想仅允许用户一周的时间去访问某个特殊的功能,逾期则否。
注册商品信息(Create iTunes Connect record)
![](http://img.blog.csdn.net/20150830183116212?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在沃尔玛卖东西,必须先要登记卖的是西瓜,还是芝麻。在Apple也是如此,先要去登记一下。
注册的具体位置见下图:
![](http://img.blog.csdn.net/20150830183212919?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
Apple的开发文档在此
非常重要但不再详细展开的内容如下:
1. 填写税务,银行等信息 (详见苹果官方文档)。
2. 创建测试用户 (详见苹果官方文档)。
二、获取产品信息
编程步骤
购物的第一步是让用户看到商品,装潢门面比什么都重要!显示商品要尽量迅速(App Store的查询速度普遍要2-3秒,要不要建立自己的服务器?),不能显示缺货的商品(看到心仪的产品,商家告诉你缺货, 多么痛的领悟!),Store UI要人性,价格显示要到位...
Apple官网给出的流程图如下:
![](http://img.blog.csdn.net/20150830183301218?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第一步 在工程中引入storekit.framework并且在文件中
第二步 product identifier存在哪里?
* 可以保存在app bundle中。
* 也可以保存在自己的服务器上。
以下是Apple给出的存储在本地的一个例子(product_ids.plist)。
如果使用自己的服务器, 可以传输JSON格式的文件, Apple同样给出了相关的例子
第三步 读取本地product_ids.plist文件
第四步 根据Product IDs从App Store获取产品的信息
第五步 显示购买界面UI
5.1 判断用户是否关闭了内置付费,如果关闭了,就提示一下
5.2 购买界面UI上价格要显示的清楚明了
三、请求付款 Requesting Payment
当用户点击购买按钮以后,会向App Store发起一个购买操作。下图是Apple文档中的示意图(图中橙色部分):
![](http://img.blog.csdn.net/20150830183301218?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第一步: 创建Payment Request
第二步: 提交Payment Request
四、解锁产品
沃尔玛卖一支牙膏的流程是:
1.把商家的牙膏放到货柜上
2. 让用户自由选择
3.用户去收银台刷信用卡
4. 刷卡器交给用户,等待银行确认刷卡信息,如果返回付款确认信息,让用户拿走牙膏。
内置付费已经走完了前面的三步,用户要一手交钱了,我们也要准备一手交货喽。(只收钱不办事儿在App Store是行不通的,写软件易,建国家难,且写且珍惜。)
这一步的流程图如下:
![](http://img.blog.csdn.net/20150830183516340?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
处理支付信息 (Processes payment)
再回到沃尔玛购买牙膏的场景,当刷信用卡的时候,整个操作流程大体如下:
![](http://img.blog.csdn.net/20150830183553076?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
注:以上答案为知乎网友周宇的解答。
在内置付费购买环节中,App Store在此处也扮演了银联收单系统的角色,App Store会把扣款成功的信息返回给“售货员”, 这里的“售货员”是我们的一段代码,名字叫做transaction queue observer。这个“售货员”放在哪里有程序员自己来决定,大体上有两个地方比较好:
1. 对于非常小型的App, 可以放在 app delegate中
2.对大部分的Apps, 单独弄一个类,和其它与Store有关的代码放在一起就很不错。
这个名叫observer的"售货员"必须要"签署SKPaymentTransactionObserver协议才能完成工作。
签署了SKPaymentTransactionObserver协议的“售货员”必须遵从协议中的要求——执行paymentQueue:updatedTransactions: 这个函数。工作的职责是:
当交易状态(The Status of a Transaction)有任何的变化, 都要调用这个操作。操作的具体细节需要我们来完成。
交易的四种主要状态以及采取相应的行动:
1. SKPaymentTransactionStatePurchasing: 购买中,此时可更新UI来展现购买的过程。
2. SKPaymentTransactionStateFailed: 购买错误,此时要根据错误的代码给用户相应的提示。
3. SKPaymentTransactionStatePurchased: 购买成功,此时要提供给用户相应的内容。
4. SKPaymentTransactionStateRestored: 恢复已购产品,此时需要将已经购买的商品恢复给用户。
保存好购物凭证(Persisting the Purchase)
现实中,购物以后要给个发票或者购物小票。在这里,也需要这么做,永久存储交易记录。这样做至少有两个用处:
1. 程序启动以后,检查购买记录,让已购的功能生效。
2. 当用户需要恢复已购功能的时候, 可以读取这个记录。
保存购物凭证的方法有如下几种:
1. 对于非消耗(non-consumable) 品, 并且iOS 7以上,可以使用app receipt来记录
2. 对于非消耗(non-consumable)品,但是是iOS7以下,可以使用User Defaults system 或者 iCloud来记录
3. 对于消耗品(consumable), 因为不能在不同设备上同步,因此不需要做永久记录(有种强拆的感觉啊!)
将Value/Key保存在User Defaults 或者 iCloud中
解锁功能 Unlocking App Functionality
当用户购买成功以后,就需要对相应的产品功能进行解锁, 当使用Receipt的时候,代码应该类似于下面的样子:
当使用Key:Value来存储的时候, 代码应该类似于下面的样子:
在程序中写下如下相应的代码,判断是否可以使用高级一点的功能:
解锁资源Delivering Associated Content
如果购买是有关资源的,比如更多的声音,更多的图片,更多的素材等等,可以有三种方式来处理这种情况:
1. (Local Content) 内置一些热门资源(预期会大卖的资源),不要太大,顶多几M左右即可。
2. (Apple-hosted Content) 使用Apple提供的Apple-hosted服务,这样可以保证App的尺寸较为精简。支持iOS 6以上。
3. 使用自己的服务器。
结束交易 Finishing the Transaction
这里没什么好讲的,就是结束交易了。
需要注意的一点是,在交易结束之前,不要调用这个函数,会让Apple-hosted Content没法下载,因为在下载Apple-hosted内容之前,返回的transaction有一个SKDownload属性,如果贸然调用了此函数,有可能会导致下载中断,以及潜在的其它问题。
内置付费到底有多流行? 我截取了2014年5月12日的榜单大约70%左右的App都使用了内置付费的功能。本文向您介绍iOS开发内置付费流程。
一、概述
内置付费到底有多流行?
我截取了2014年5月12日的榜单,红色为包含内置付费的App,蓝色是不包含内置付费的app。只需要看看颜色,满眼一片红。大约70%左右的App都使用了内置付费的功能。
内置付费的工作流程
其实和所有的购物 并无二致,以沃尔玛商城为例:
1. 沃尔玛商城卖什么?
2. 把货物运放到货架上供客户挑选。
3. 沃尔玛收款。
App Store上也是如此:
1. Apple 允许卖什么 ?
2. 软件上架任由用户挑选。
3. Apple收款。
还有一点非常相似,沃尔玛需要收商家的进店费,上架费,店庆费... Apple则收取30%的分成。
Apple允许卖什么类型的内置付费产品?
四类:
1. Non-Consumable: 比如游戏中的关卡, 购买以后随意玩任意多次; 比如美国的房子,70年后还是你的。
2. Consumable: 比如游戏中的金币, 消耗品;比如汽油, 消耗品。用光了就得去再去买。
3. Auto-Renewable Subscriptions: 暂略下不表。
4. Free Subscriptions: 暂略下不表。
备注:(/article/8990856.html)
Non-Consumable:用户只需购买一次,不需要再次购买,即可在多台设备上拥有之(restore技术)。
Consumable:用户可以购买多次(不限定次数)。例如金币。
Auto-Renewable Subscriptions:为了收到app更新的内容,用户需要定期支付款项。(目前仅适用于杂志或者新闻类型的app)。
Free Subscriptions:类似于Auto-Renewable Subscriptions类型,但免费,仅适用于杂志类型的app。
Non-Renewing Subscriptions:假如你可用Auto-Renewable(你的app不属于杂志或者新闻类型),但是你仍想提供基于时间限制的访问内容,可以选择Non-Renewing来试试。比如你想仅允许用户一周的时间去访问某个特殊的功能,逾期则否。
注册商品信息(Create iTunes Connect record)
在沃尔玛卖东西,必须先要登记卖的是西瓜,还是芝麻。在Apple也是如此,先要去登记一下。
注册的具体位置见下图:
Apple的开发文档在此
非常重要但不再详细展开的内容如下:
1. 填写税务,银行等信息 (详见苹果官方文档)。
2. 创建测试用户 (详见苹果官方文档)。
二、获取产品信息
编程步骤
购物的第一步是让用户看到商品,装潢门面比什么都重要!显示商品要尽量迅速(App Store的查询速度普遍要2-3秒,要不要建立自己的服务器?),不能显示缺货的商品(看到心仪的产品,商家告诉你缺货, 多么痛的领悟!),Store UI要人性,价格显示要到位...
Apple官网给出的流程图如下:
第一步 在工程中引入storekit.framework并且在文件中
#import <StoreKit/StoreKit.h>
第二步 product identifier存在哪里?
* 可以保存在app bundle中。
* 也可以保存在自己的服务器上。
以下是Apple给出的存储在本地的一个例子(product_ids.plist)。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <array> <string>com.example.level1</string> <string>com.example.level2</string> <string>com.example.rocket_car</string> </array> </plist>
如果使用自己的服务器, 可以传输JSON格式的文件, Apple同样给出了相关的例子
[ "com.example.level1", "com.example.level2", "com.example.rocket_car" ]
第三步 读取本地product_ids.plist文件
NSURL *url = [[NSBundle mainBundle] URLForResource:@"product_ids" withExtension:@"plist"]; NSArray *productIdentifiers = [NSArray arrayWithContentsOfURL:url];
第四步 根据Product IDs从App Store获取产品的信息
- (void)validateProductIdentifiers:(NSArray *)productIdentifiers{ SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]]; productsRequest.delegate = self; [productsRequest start]; } // SKProductsRequestDelegate protocol method - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ self.products = response.products; for (NSString *invalidIdentifier in response.invalidProductIdentifiers) { // Handle any invalid product identifiers. 处理有效的ProductIdentifiers, 缺货的,错误的不能有! } [self displayStoreUI]; // Custom method 显示Store的UI }
第五步 显示购买界面UI
5.1 判断用户是否关闭了内置付费,如果关闭了,就提示一下
if ([SKPaymentQueue canMakePayments]) { [self displayStoreUI]; // Custom method } else { NSLog(@"用户禁止应用内付费购买."); }
5.2 购买界面UI上价格要显示的清楚明了
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [numberFormatter setLocale:product.priceLocale]; NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];
三、请求付款 Requesting Payment
当用户点击购买按钮以后,会向App Store发起一个购买操作。下图是Apple文档中的示意图(图中橙色部分):
第一步: 创建Payment Request
SKProduct *product = <# Product returned by a products request #>; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; payment.quantity = 2; //数量
第二步: 提交Payment Request
[[SKPaymentQueue defaultQueue] addPayment:payment];
四、解锁产品
沃尔玛卖一支牙膏的流程是:
1.把商家的牙膏放到货柜上
2. 让用户自由选择
3.用户去收银台刷信用卡
4. 刷卡器交给用户,等待银行确认刷卡信息,如果返回付款确认信息,让用户拿走牙膏。
内置付费已经走完了前面的三步,用户要一手交钱了,我们也要准备一手交货喽。(只收钱不办事儿在App Store是行不通的,写软件易,建国家难,且写且珍惜。)
这一步的流程图如下:
处理支付信息 (Processes payment)
再回到沃尔玛购买牙膏的场景,当刷信用卡的时候,整个操作流程大体如下:
注:以上答案为知乎网友周宇的解答。
在内置付费购买环节中,App Store在此处也扮演了银联收单系统的角色,App Store会把扣款成功的信息返回给“售货员”, 这里的“售货员”是我们的一段代码,名字叫做transaction queue observer。这个“售货员”放在哪里有程序员自己来决定,大体上有两个地方比较好:
1. 对于非常小型的App, 可以放在 app delegate中
2.对大部分的Apps, 单独弄一个类,和其它与Store有关的代码放在一起就很不错。
这个名叫observer的"售货员"必须要"签署SKPaymentTransactionObserver协议才能完成工作。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* 放一个“售货员” */ [[SKPaymentQueue defaultQueue] addTransactionObserver:observer]; }
签署了SKPaymentTransactionObserver协议的“售货员”必须遵从协议中的要求——执行paymentQueue:updatedTransactions: 这个函数。工作的职责是:
当交易状态(The Status of a Transaction)有任何的变化, 都要调用这个操作。操作的具体细节需要我们来完成。
交易的四种主要状态以及采取相应的行动:
1. SKPaymentTransactionStatePurchasing: 购买中,此时可更新UI来展现购买的过程。
2. SKPaymentTransactionStateFailed: 购买错误,此时要根据错误的代码给用户相应的提示。
3. SKPaymentTransactionStatePurchased: 购买成功,此时要提供给用户相应的内容。
4. SKPaymentTransactionStateRestored: 恢复已购产品,此时需要将已经购买的商品恢复给用户。
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { // Call the appropriate custom method. case SKPaymentTransactionStatePurchased: // 购买成功 [self completeTransaction:transaction]; break; case SKPaymentTransactionStateFailed: // 购买失败 [self failedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: // 恢复已购 [self restoreTransaction:transaction]; default: break; } } } - (void)completeTransaction:(SKPaymentTransaction *)transaction { NSString * productIdentifier = transaction.payment.productIdentifier; NSString * receipt = [transaction.transactionReceipt base64EncodedString]; if ([productIdentifier length] > 0) { // 向自己的服务器验证购买凭证 } // Remove the transaction from the payment queue. [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void)failedTransaction:(SKPaymentTransaction *)transaction { if(transaction.error.code != SKErrorPaymentCancelled) { NSLog(@"购买失败"); } else { NSLog(@"用户取消交易"); } [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void)restoreTransaction:(SKPaymentTransaction *)transaction { // 恢复已经购买的产品 [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; }
保存好购物凭证(Persisting the Purchase)
现实中,购物以后要给个发票或者购物小票。在这里,也需要这么做,永久存储交易记录。这样做至少有两个用处:
1. 程序启动以后,检查购买记录,让已购的功能生效。
2. 当用户需要恢复已购功能的时候, 可以读取这个记录。
保存购物凭证的方法有如下几种:
1. 对于非消耗(non-consumable) 品, 并且iOS 7以上,可以使用app receipt来记录
2. 对于非消耗(non-consumable)品,但是是iOS7以下,可以使用User Defaults system 或者 iCloud来记录
3. 对于消耗品(consumable), 因为不能在不同设备上同步,因此不需要做永久记录(有种强拆的感觉啊!)
将Value/Key保存在User Defaults 或者 iCloud中
#if USE_ICLOUD_STORAGE NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore]; #else NSUserDefaults *storage = [NSUserDefaults standardUserDefaults]; #endif [storage setBool:YES forKey:@"enable_rocket_car"]; [storage setObject:@15 forKey:@"highest_unlocked_level"]; [storage synchronize]; 将Receipt保存在User Defaults 或者 iCloud中 #if USE_ICLOUD_STORAGE NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore]; #else NSUserDefaults *storage = [NSUserDefaults standardUserDefaults]; #endif NSData *newReceipt = transaction.transactionReceipt; NSArray *savedReceipts = [storage arrayForKey:@"receipts"]; if (!receipts) { // Storing the first receipt [storage setObject:@[newReceipt] forKey:@"receipts"]; } else { // Adding another receipt NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt]; [storage setObject:updatedReceipts forKey:@"receipts"]; } [storage synchronize];
解锁功能 Unlocking App Functionality
当用户购买成功以后,就需要对相应的产品功能进行解锁, 当使用Receipt的时候,代码应该类似于下面的样子:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL]; // Custom method to work with receipts BOOL rocketCarEnabled = [self receipt:receiptData includesProductID:@"com.example.rocketCar"];
当使用Key:Value来存储的时候, 代码应该类似于下面的样子:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];
在程序中写下如下相应的代码,判断是否可以使用高级一点的功能:
if (rocketCarEnabled) { // Use the rocket car. } else { // Use the regular car. }
解锁资源Delivering Associated Content
如果购买是有关资源的,比如更多的声音,更多的图片,更多的素材等等,可以有三种方式来处理这种情况:
1. (Local Content) 内置一些热门资源(预期会大卖的资源),不要太大,顶多几M左右即可。
2. (Apple-hosted Content) 使用Apple提供的Apple-hosted服务,这样可以保证App的尺寸较为精简。支持iOS 6以上。
3. 使用自己的服务器。
结束交易 Finishing the Transaction
这里没什么好讲的,就是结束交易了。
SKPaymentTransaction *transaction = <# The current payment #>; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
需要注意的一点是,在交易结束之前,不要调用这个函数,会让Apple-hosted Content没法下载,因为在下载Apple-hosted内容之前,返回的transaction有一个SKDownload属性,如果贸然调用了此函数,有可能会导致下载中断,以及潜在的其它问题。
相关文章推荐
- iOS阶段学习第32天笔记(页面传值方法介绍)
- iOS开发中经常遇到的问题及解决办法1
- iOS调试——基础(一)
- IOS 开发笔记——二维码的生成和扫描
- iOS分类和扩展(Categories和Extensions)
- 将视频导入到iOS Simulator中
- 常见问题
- 如何获取IOS上所有安装的app?
- iOS Foundation框架初接触
- iOS“断点”(Break Point)你不知道多强大
- iOS弹幕效果
- 动态计算label和view宽度
- iOS: ARC和非ARC下使用Block属性的问题
- iOS开发——源代码管理——git(分布式版本控制和集中式版本控制对比,git和SVN对比,git常用指令,搭建GitHub远程仓库,搭建oschina远程仓库 )
- iOS8与iOS9的不同
- iOS开发常见问题集
- nagios详解
- IOS中通过给NSString分类实现计算一个字符串中的阿拉伯数字出现的个数
- 【Xamarin For IOS 开发需要的安装文件】
- iOS开发——源代码管理——SVN