App内购之 Ray Wenderlich 的 IAP 文章的学习笔记。
原创文章,转载请保留地址:http://blog.youkuaiyun.com/abware/article/details/12165933
前面几步的流程请看原文,很详细,有中文版。这里主要是讲解一下代码,这块代码对新手来说有点难懂。
一、配置。这块比较繁琐,填很多表,信用卡帐号等要填
1. AppID。应该是不能用*
2. ProductID
3. Test帐号
二、支付流程
1. 无独立服务器模式
2. 有独立服务器模式
三、编码
1. 初始化
把IAPHelper.h与IAPHelper.m两个文件导入工程。
创建自己的xxxxIAPHelper类,继承自IAPHelper。
将xxxxIAPHelper写为单例,并在单例初始化时调用- ( id )initWithProductIdentifiers:(NSSet *)productIdentifiers函数,同时给定输入参数,该输入参数即ProductID列表。需要与itunes connect中设定的一致。另外初始化函数中还做了一件事:注册为支付事务的观察者。
该单例的第一次初始化放在AppDelegate中,原因如后面所述。
2. 查询
查询请求的入口函数被封装为- ( void )requestProductsWithCompletionHandler:( RequestProductsCompletionHandler )completionHandler,其中输入参数是一个Block,在请求结果的返回中,会调用到该Block,这个Block的内容则需要自己来写,用来对返回结果进行处理。另一个隐含的输入是成员变量 _productIdentifiers ,该成员变量在初始化时被赋值。另外该函数还用到成员变量 _productsRequest ,该变量用来保存该查询请求的句柄。
查询结果的返回需要要实现 SKProductsRequestDelegate 协议,该协议的两个函数分别用来处理请求成功和请求失败两种情况。这两个函数都会将成员变量 _productsRequest 设置为nil,同时打印一些日志信息,最后都会调用 RequestProductsCompletionHandler 这个Block,即发送请求时指定的Block。该Block的定义为 typedef void (^RequestProductsCompletionHandler)( BOOL success, NSArray * products);,可以看到有两个输入参数,请求成功时第一个参数赋为YES,第二个参数为 NSArray 是包含了所有产品列表,是从StoreKit框架标准返回参数 SKProductsResponse 中得到的response. products ;请求失败时第一个参数赋为NO;第二个参数为nil。
注:Block如果不太理解,可以暂把它理解为一个回调,或者函数指针。实际上Block是一个函数对象,有其自身的作用域。
3. 支付购买
发送支付请求的入口函数被封装为- ( void )buyProduct:( SKProduct *)product,其中需要增加一点的是[ SKPaymentQueue canMakePayments ]判断。该函数内会把输入参数封装为 SKPayment ,然后加入到 SKPaymentQueue 队列中。实际这里有一个类似事务的概念在里面,一次支付行为的payment加到队列后,会自动持久化保存,防止app中断执行后,支付事务丢失。当然这些事情都由 StoreKit 框架来完成,开发者知道就行,不用另外编码。支付过程中可能app会中断,开发者可以在AppDelegate的didFinishLaunchingWithOptions函数中把IAP类初始化,由于初始化里会注册为事务的观察者[[ SKPaymentQueue defaultQueue ] addTransactionObserver : self ];因此可以在app重新打开后继续接收未完成的支付行为的返回消息。
支付请求的返回要实现 SKPaymentTransactionOBserver 协议,该协议有一个必须实现的接口和四个可选的接口。
IAPHelper中实现了必选接口- ( void )paymentQueue:( SKPaymentQueue *)queue updatedTransactions:( NSArray *)transactions。其功能是根据支付返回结果的状态transaction. transactionState 来进行分发处理流程,这里的transaction是输入参数中transactions容器中的一个遍历值。用switch case分发处理流程到另外三个可选的接口函数:
- ( void )completeTransaction:( SKPaymentTransaction *)transaction
- ( void )restoreTransaction:( SKPaymentTransaction *)transaction
- ( void )failedTransaction:( SKPaymentTransaction *)transaction
这三个函数中都进行了日志打印,最后都调用了[[ SKPaymentQueue defaultQueue ] finishTransaction : transaction]来关闭该支付事务。另外前两个函数的中在关闭事务之前都调用IAPHelper的一个封装函数- ( void )provideContentForProductIdentifier:( NSString *)productIdentifier。 该函数做的事情是:把已购项写到 NSUserDefaults 中,然后利用 NSNotificationCenter 把消息 IAPHelperProductPurchasedNotification 广播一下。支付成功或恢复成功后发通知,通过这个消息里的内容,外部类就可以获得相关支付的信息。因此开发者需要注册为该通知的观察者(注册操作需要自己编码完成,要记得在合适的时候删除注册),收到通知后响应。注册为该消息的观察者时要提供一个回调,这个回调函数需要开发者实现。- ( void )productPurchased:( NSNotification *)notification。其输入参数中保存了产品标识,可这样获得: NSString * productIdentifier = notification. object 。
4. 恢复已购买
恢复购买的请求的入口函数被封装为- ( void )restoreCompletedTransactions。该函数只有一句[[ SKPaymentQueue defaultQueue ] restoreCompletedTransactions ]。即把恢复请求加入到支付队列中。
恢复购买的请求的返回在上述 SKPaymentTransactionOBserver 协议中的一个可选接口中已经实现了。即把返回内容写到 NSUserDefaults ,同时发通知。
5. 总结
开发者导入了IAPHelper之后,自己需要完成的编码工作主要有:
a. 创建一个xxxxIAPHelper类。可参考原文示例中的相关类,很简单,就是一个单例。在AppDelegate中进行该单例的初始化。
b. 在自己的类(不是xxxxIAPHelper类)中加入两个私有成员变量。可在.m文件中加。
NSArray *_products; NSNumberFormatter * _priceFormatter;
其中_products是保存查询结果(产品列表)的,示例中是每次程序运行后会发查询请求,请求结果被保存在成员变量 _products 中,这是一个强指针 NSArray* (因为是强引用,所以该值不设置为nil时会一直保留上次查询结果),保存了该应用所提供的全部产品列表,其中的元素是 SKProduct *,包含了产品的ProductID、本地化名称、价格等级等信息。
_priceFormatter则是格式化显示数字,主要是为了本地化显示货币(产品的价格)。
c. 写查询请求中的那个Block。主要在这里实现一些更新显示的逻辑,根据查询结果更新显示。用下面的方式查询。
[[ xxxxIAPHelper sharedInstance ] requestProductsWithCompletionHandler :^( BOOL success, NSArray *products) {
// 这里添加需要编码的Block内容,查询结果先保存到_products中,然后加自己的处理逻辑
if (success) { _products = products; // 更新 产品列表并展示相关信息及购买按钮 / 已购状态 } }];
d. 写购买按钮的响应处理函数。购买时根据 _products 中的值来构造 SKProduct ,做为支付入口的输入参数。
e. 注册为消息 IAPHelperProductPurchasedNotification 的观察者。[[ NSNotificationCenter defaultCenter ] addObserver : self selector : @selector (productPurchased:) name : IAPHelperProductPurchasedNotification object : nil ];
在回调中处理购买完成后的程序逻辑。示例中用了一个Block来遍历产品列表,找出需要更新的项进行处理。(这是又一个Block的使用技巧,可以学习一下:相当于该Block是该容器的迭代器回调)
在适当的时候记得移除注册。[[ NSNotificationCenter defaultCenter ] removeObserver : self ];
f. 因为IAPHelper会将购买结果保存在 NSUserDefaults 中,键值即为ProductID,因此可以在需要的时候直接查询 NSUserDefaults 。
g. 支付事务观察者一直未移除注册,如果需要移除注册可以这样:[[ SKPaymentQueue defaultQueue ] removeTransactionObserver : self ]。因为示例中在单例初始化时注册的,单例在程序启动后就一直存在,所以未移除注册。

本文深入解析了App内购流程,包括配置、支付流程和编码实现细节,通过示例代码帮助开发者理解如何在iOS应用中实现内购功能。
3783

被折叠的 条评论
为什么被折叠?



