苹果内购接入的坑与核心流程 - Swift
通过苹果开发文档、搜索与挣扎摸索,我已经在App上实现了【消耗型商品】的内购。总结流程如下:
- 完成必须的准备工作
- 用有效的商品id创建
SKProductRequest
请求苹果服务器返回商品(SKProduct
)。 - 用返回的有效的商品创建payment,并将payment加入苹果的支付队列。
- 监听队列,并根据监听到的交易状态进行适当的处理,尤其需要在交易完毕时关闭交易。
这方面的文档,我觉得苹果官方的Programming Guide就写的很好。可以仔细读一下。
我实际遇到的坑
-
必须先签署协议才能进行内购开发,否则无法返回商品。
-
创建沙箱测试员时,密码必须是强密码,即同时包含大写字母、小写字母和数字。否则,会报错:Unknown Errors while creating Sandbox Tester, Please check Error Log, email=xxx。
-
请求商品的
SKProductRequest
实例的delegate
必须在离开页面时viewDidDisappear
设置为nil
,因为即使你已经离开了页面,苹果依然会尝试向SKProductRequest
的代理发送消息,然后App就崩溃了。 -
必须在
AppDelegate
中声明遵守SKPaymentTransactionObserver
协议、实现协议方法updatedTransactions
、并在didFinishLaunching
方法中加入如下代码SKPaymentQueue.default().add(self)
。这一行代码是将AppDelegate作为内购队列的监听者。这样,即使你离开了内购页面、即使上次支付未完成等情况,都能够在这里得到及时的处理。
如果不在这里这么做,可能的报错有:
This In-App purchase has already been bought. It will be restored for free.
如果需要在内购页面上实现交互,可以也在内购界面上添加监听,并进行交互上面适当的处理。但无路如何,不能省掉
AppDelegate
中的那部分。
内购接入核心流程
以下是我实际接入中使用的代码。
请求商品
商品是由苹果服务器返回的,请求有三个步骤:获取商品id字符串、发起请求、代理协议中处理结果。
获取商品id
获取商品id,这个可以请求服务器,也可以保存在App上。我的App是用的后者。
首先创建一个名为ProductID
的plist
文件。文件内容是一个Array
,里面是保存的商品id字符串。
这里需要说明的是,商品id,即product identifier,就是你在iTunes Connect 内购列表页面上看到的商品id,不需要再拼接包名bundle identifier。
|
|
验证商品id
请求苹果服务器验证我们提供的商品id是否有效。不过记得要把内购Controller设置为遵循SKProductsRequestDelegate
协议,否则无法收到通知。
|
|
在协议中处理商品
请求支付
在获取了有效商品后,就可以用商品创建SKPayment
,并将payment
实例加入苹果支付队列中。
处理支付
这个步骤核心的点就是创建并添加SKPaymentTransactionObserver
。
AppDelegate添加observer
- 在AppDelegate声明遵循
SKPaymentTransactionObserver
协议。 - 实现协议方法,实现后实现者就可以作为observer。
|
|
- 在
didFinishLaunchingWithOptions
方法中添加观察者:SKPaymentQueue.default().add(self)
其他界面添加observer
假如用户一直呆在内购界面等待结束,我们很可能需要在内购界面也要一套前端的展示机制。跟AppDelegate的流程一样,我们将内购界面也作为SKPaymentTransactionObserver
添加到队列中。这样,内购界面也能收到通知了。
离开其他界面时,记得调用
SKPaymentQueue.default().remove(self)
来移除observer,否则可能会崩溃。
当内购界面可用的时候,就可以让内购界面去处理一些前端交互,比如弹出窗口,关闭当前界面等。
恢复支付
如果用户购买的是非消耗型商品,比如是一个去广告服务。用户卸载后,如果重新安装回来,是可以申请恢复记录的。我使用的办法比较简陋,没有用加密和服务端验证,只是对比了下transaction.payment.productIdentifier
。
首先,在用户发起「恢复购买记录」后,调用SKPaymentQueue.default().restoreCompletedTransactions()
。
然后,在updateTransactions
协议方法中,在case .restored
中验证用户的transaction.payment.productIdentifier
,若符合,则修改UerDefaults
中的去广告标记。
本人初学,有不当或错漏之处,感谢斧正!