iAP 的小指南
in-App Purchase 内购的缩写为 iAP。是 iOS 自带的一种应用内数字产品的支付方式,可以让用户不跳出 App 进行支付。 iAP 是什么相信各位开发者都非常熟悉,就不多做赘述了。
iAP 在使用时可分为三步:
_1488866505064.png- 取回产品信息:本地准备需要购买的 Product Information,向 App Store 服务器提交这些信息,App Store 会根据国家地区,和你的定价显示相应的价格与文案。
- 发起支付:用户选择具体购买的 Product 后,向 App Store 发起购买支付请求。
- 提供产品:成功支付扣款后, App Store 会通知 App,向用户提供所购买的产品。
根据 Apple 提供的官方文档 In-AppPurchase Programming,分为以下章节
设计 App 内的产品
「产品」指的是你希望通过内购方式在 App 内商店售卖的东西。 首先需要在 iTunes Connect 创建和设置产品的信息,在项目中,使用 SKProduct 和 SKProductsRequest 两个类来进行操作。
首先你要明白 iAP 可以帮你售卖哪些数字产品
「内容」:书籍、专辑、游戏的特别关卡、特殊的滤镜等。
「App 的高阶功能」:解锁特殊 App 的特殊功能、移除广告;譬如在单玩家模式的游戏中提供多玩家模式。
「服务」:向用户提供诸如基于大数据的一次性的即时人工语音会话与翻译支持。
但是 iAP 不适用于售卖现实世界中的商品或者不合适的服务
- 如果售卖真实的物品,可以使用其他支付方式,诸如 Apple Pay 、支付宝、微信支付
- 请不要使用 iAP 来售卖违反 App Review guidelines 的产品,诸如色情图片、反动言论、污蔑与杜撰
如果您对什么可以卖的问题有困扰,仔细阅读App Review guidelines,可以有效的防止因审核不通过的延期发布。如果有其他的问题可使用Apple Store 在线表单系统与 Apple 联系。
在解决了什么可以卖的问题后,我们可以开始在 iTunes Connect 中创建所需售卖的产品
在 iTunes Connect 中创建所需售卖的产品
在开始写代码之前,你需要在 iTunes Connect 做好产品的相关设置。一旦你开始开发,你仍然可以增加、修改或删除产品。
每种产品与一个特定的 App 相关联,不同的 App 之间不会共用同一个产品。即使是不同平台上的同一个软件,也不能共用「产品」。
在你的 App 被 Review 时,你的 iAP 的产品会同时被 Review。产品必须经过 Review,且在 iTunes Connect 中被设置为「cleared for sale」状态才是可供用户购买的状态。
有关 step by step 的 iTunes connect 操作,参照 In-App Purchase Configuration Guide for iTunes Connect ,这些工作往往是由产品或运营人员来管理。
产品的类型
产品的类型让你更灵活的提供数字产品的内容。你可以选择以下产品类型:
- 可消耗型:在 App 运行期间会被消耗掉的产品,诸如游戏内的宝石、点卡、 IP 国际通话时长
- 不可消耗型:在该用户名下所有设备上均可以永久使用的产品,且用户可以通过 restore purchase 来恢复,诸如书籍、游戏扩展关卡、移除广告服务等
- 自动续订的订阅:不定期的服务,上文中的不可消耗的产品,会有无限期的服务时间,但是订阅会有到期时间,,如此您可以持续的向用户收费。订阅到期后会自动续订,直至用户手动取消订阅
- 不自动续订的订阅:这种订阅也是提供一段时间内的服务,但是,到期后不会自动续订,用户手动需要续订才可以继续享受您的产品。
- 免费订阅:此种类型只在报刊类 App 中可用,免费订阅不收取费用,不会失效,但并不常用。
不同产品与订阅类型间的区别
产品类型 | 不可消耗 | 可消耗 |
---|---|---|
可购买次数 | 只可一次 | 多次 |
在 receipt 中出现 | 总是出现 | 只出现一次 |
跨设备同步 | 原则上可同步 | 取决于开发者 |
可恢复性 | 原则上可恢复 | 不可恢复 |
订阅类型 | 自动续订 | 不自动续订 | 免费 |
---|---|---|---|
可购买性 | 多次 | 多次 | 只可一次 |
在 receipt 中出现 | 总是出现 | 总是出现 | 总是出现 |
跨设备同步 | 原则上可同步 | 原则上可同步 | 原则上可同步 |
可恢复性 | 原则上可恢复 | 原则上可恢复 | 原则上可恢复 |
开发者可以根据上表来灵活根据自己的产品与服务来决定选用何种「产品类型」,以及订阅的自动续订性决定向用户提供产品与内容的多寡。
以下是面向开发者的一些 iAP 开发步骤中
取回产品信息
管理好产品的标识符列表
每种 iAP 产品都拥有一个独特的产品标识符「Product identifier」。你的 App 用此标识符来向 App Store服务器拉去产品的详细信息,诸如价格,并以此标识符来发起支付请求。这个表单放在 App 内或者服务器上均可。在此不赘述大家如何在 App 内如何持久化这个 Product Identifier 的 list ,以及如何从服务器上获得。
获取产品信息
事先向 App Store 服务器查询产品的可购买性,可以确保顾客可以切实购买到相应的产品。这是由于不同国家地区的定价规则与法规不同。
func checkValidate(productIdentifiers: [String]) {
let ccc = SKProductsRequest(productIdentifiers: Set(productIdentifiers))
self.request = productsRequest
productsRequest.delegate = self
productsRequest.start()
}
// 下面这个方法是 SKProductsRequest 的唯一一个代理方法,在成功获取服务器返回的有关产品信息时,会回调此方法
func productsRequest(_ request: SKProductsRequest, didReceiveResponse response: SKProductsResponse){
for product in response.products {
/*
product 是 SKProduct 的实例,可以参照 SKProduct 相关属性,处理 Product 的相关价格、描述等
product.localizedDescription
product.localizedTitle
product.price
*/
}
}
在获取完产品信息之后,准备 App 内商店的 UI 或 购买的 UI ,以下是苹果官方推荐的应用内商店
- 在用户需要付费的场景下,才展示 App 内商店
- App 内商店的出现应该自然而不突兀
- 生动形象的展示产品
- 说清楚产品为用户所带来的价值
- 明码标价,不要进行汇率换算,做好本地化适应
发起支付请求
上个步骤中,如果用户选择了一款产品并有意向支付,App 需要向 App Store 发起支付请求。会有以下步骤:
创建支付请求
用户选择完要买的产品,创建支付请求的实例
let payment = SKMutablePayment(product: product) // product 为用户选中的,上个步骤中取回的 SKProduct 实例
payment.quantity = 8 // 设置产品的购买数量
处理部分用户的不规则操作
大部分用户都会在自己的设备上使用同一个 Apple ID 来完成购买操作,但是少部分用户会在同一台设备上,切换 Apple ID 或者应用内的账号,Apple Store 无法处理这种行为,开发者需要自行处理用户在不同 Apple ID 、不同账户下下购买不同产品的逻辑,您可以在服务端将每次的购买行为与 App 内的账户所整合,来处理这些行为。
提交一次支付请求
支付请求由队列管理,多次提交的请求需要多次添加到队列中,用户也会被多次收费,此时会有一个与 App Store 服务器通信的等待时间。为了避免用户多次点击而造成多次支付,请在等待服务器响应时,添加不可响应的 UI 界面,响应后,App Store 将会提示用户购买内容和价格,此过程中开发者无需进行其他操作。购买成功、失败或取消后,均会进入下一个步骤
guard SKPaymentQueue.default().canMakePayments() else { // 在 iOS 的访问限制中,可以禁止 iAP 的功能,所以在发起支付请求前,需要判断
return
}
SKPaymentQueue.default().add(self) // 在添加任何的支付请求到队列中前, 必须设置好 PaymentQueue 的 Observer, 实际上是服从 SKPaymentTransactionObserver Protocol 的代理
SKPaymentQueue.default().add(payment)
向用户提供产品
iAP 的最后一个步骤是等待与处理 App Store 返回上个步骤提交的支付所返回的结果。
等待 App Store 来处理此次交易
在支付回话中,也就是向支付队列添加了支付请求后,支付的状态变化都会回调到 SKPaymentTransactionObserver 的方法。
SKPaymentTransactionState 有以下这些状态
SKPaymentTransactionState | App 内需要一些 UI 提示 |
---|---|
.purchasing | 开始转菊花,加载等操作,且最好采用不可交互的界面,避免重复提交,等待代理方法的下一次回调 |
.deferred | 采取一些操作来展现被延迟的操作,此状态只在 iOS8 之后可用,如果一个 transaction 不被 finish ,会进入此状态 |
.failed | 分析 transaction 的 error 属性来向用户展示购买失败的 UI |
.Purchased | 购买成功,向用户提供产品和订阅 |
.restored | 如果是不可消耗类,向用户恢复之前购买的产品与订阅 |
以下是应对 Transaction 所采取操作的 demo
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions:[SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchasing:
// 购买中,等待下次回调
case .deferred:
// 被延迟,等待下次回调
case .failed:
// 购买失败,分析 transaction 的 error ,想用户说明情况,并结束掉此 transaction
case .purchased:
// 购买成功,用户支付并扣款成功,自此向用户提供付费的产品,并结束掉此 transaction
case .restored:
// 恢复购买成功,向用户提供付费的产品,并结束掉此 transaction
}
}
}
SKPaymentTransactionObserver Protocol 还提供了以下 optional 方法来更为详细的跟踪 transaction 的状态,目的是为了更妥当的调整 UI ,一般不常用,大家根据名字也可以大概了解,不展开叙述
optional func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction])
optional func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error)
optional func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue)
optional func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload])
持久化购买记录
- 针对用户购买的除了可消耗类型的产品,其他产品均要提供用户恢复的选项,可消耗类型的视自己需求来决定是否保存记录。
- 针对不可消耗类型的产品或自动续订的订阅服务:在 iOS7 以前,你需要将购买记录保存在 iCloud 或者自己服务器上, iOS7 之后以 App Store 提供的 Receipt 来作为最终的购买记录。
- 针对不自动续订的订阅服务,请把订阅的起止时间保存在 iCloud 或 自己的服务器上。
结束一个 iAP 的交易 Transaction
结束一个 Transaction 意味着告诉 Store Kit 你已经处理完一次购买的所有事情。 未完成的交易会一直留在 SKPaymentQueue.default()
直至你手动 finish。 如果队列中有没有完成的交易,每次 App 启动都会回调 public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions:[SKPaymentTransaction])
以便你能处理未完成的交易。简言之,无论交易成功与否,你都需要将其 finish ,否则无法向队列中添加新的 Transaction
SKPaymentQueue.default().finishTransaction(transaction)
Apple 建议在结束一个 transaction 之前要完成以下操作:
- 持久化此次购买
- 必要时下载相关内容
- 为用户提供产品或服务
Apple 提供了从 Apple 服务器上下载产品的方法,但在实际开发中使用较少,具体可以参照 SKDownload 和 SKPaymentQueue 文档中相关方法, 在此不做赘述func start(_ downloads: [SKDownload])
本文完成于 3月份 一直在印象笔记里躺尸到 8 月才发布。。
WechatIMG140.jpeg
网友评论