Session: WWDC2018 Best Practices and What's New with In-App Purchases https://developer.apple.com/videos/play/wwdc2018/704/
对于一个标准的 IAP 流程, 大致如下图所示
翻译成中文就是
设置商品 ID(跟 ITC 上面的 ID 一致)
根据商品 ID 去苹果后台获取商品信息, 这时会获得商品名字, 价格等信息
把 IAP 的购买界面展示给用户, 用户可以同意购买并点击购买按钮.
用户授权购买, 客户端向服务器发送购买请求.
此时购买流程状态变更, 客户端根据苹果规定的状态机流程处理回调
如果购买请求验证通过, 客户端此时解锁内容或者提供金币.
至此, 整个交易流程结束.
以上是苹果文档上的流程, 应用到 App 的实际操作中, 我们还应该包含以下流程
营销活动配置
试用功能控制
沙箱联调流程
安全性校验
本次 WWDC 议题很好地描述了上述的细节
App 定价策略
一般的 App 营销活动有如下的类型
在 iOS11.2 的时候, 苹果增加了如下类和接口便于开发者实现上述业务流程(只针对自动续期订阅的场景)
- SK_EXTERN_CLASS_AVAILABLE(11_2) @interface SKProductDiscount : NSObject
- @property(nonatomic, readonly) NSDecimalNumber *price NS_AVAILABLE_IOS(11_2);
- @property(nonatomic, readonly) NSLocale *priceLocale NS_AVAILABLE_IOS(11_2);
- @property(nonatomic, readonly) SKProductSubscriptionPeriod *subscriptionPeriod NS_AVAILABLE_IOS(11_2);
- @property(nonatomic, readonly) NSUInteger numberOfPeriods NS_AVAILABLE_IOS(11_2);
- @property(nonatomic, readonly) SKProductDiscountPaymentMode paymentMode NS_AVAILABLE_IOS(11_2);
- @end
以上的 api 对应于 App Store Connect 后台中设定的推介促销价, 可以设置为折扣价格或免费试用, 根据适当的条件, App 可以为符合条件的用户显示促销价. 接下来我们先了解一些基本概念, 然后再看看如何通过以上的 api 来完成对应的业务流程.
创建推介促销价
在 App Store Connect 中可以为 App 内购买项目的每个订阅设置和管理推介促销价, 并可以针对每个地区设置一个当前价格和一个未来推介促销价. 如果创建的新价格出现重叠日期, 则最新一次操作将覆盖现有的推介促销价. 操作流程如下
在首页上, 点按 "我的 App", 然后选择与该 App 内购买项目相关联的 App.
在工具栏中, 点按 "功能", 然后在左列中点按 "App 内购买项目".
点按自动续期订阅, 前往 "订阅价格" 部分, 然后点按 "添加" 按钮(+).
选择 "设置推介促销价".
选择想要提供折扣价格的地区, 然后再点按 "下一步".
选择开始和结束日期. 如果想让推介促销价无限期使用, 可以选择 "无结束日期". 然后点按 "下一步".
选择 "随用随付","提前支付" 或者 "免费试用", 然后选择合适的时限, 货币和价格. 点按 "下一步".
苹果会根据您选择的价格点自动为所有地区计算价格, 但您可以为特定地区设定不同的价格. 在此处选择地区价格, 然后点按 "完成".
推介促销价有三种类型, 分别如下
随用随付 用户将按选定时限的每个结算周期支付推介促销价(例如, 订阅的标准价格为 9.99 美元, 推介促销价为前 3 个月每月 1.99 美元). 结算周期可设定以下时限:
1 周订阅, 1 至 12 周
1 个月订阅, 1 至 12 个月
2 个月订阅, 2,4,6,8,10 和 12 个月
3 个月订阅, 3,6,9 和 12 个月
6 个月订阅, 6 和 12 个月
1 年订阅, 1 年
提前支付 用户将一次性支付选定时限的推介促销价(例如, 订阅的标准价格为 9.99 美元, 推介促销价为前 2 个月 1.99 美元). 可设定以下时限: 1 个月, 2 个月, 3 个月, 6 个月或 1 年.
免费试用 用户在选定的时限内免费访问订阅. 时限可以是 3 天, 1 周, 2 周, 1 个月, 2 个月, 3 个月, 6 个月或 1 年.
符合促销定价的用户
促销价的展示依赖于用户的购买状态, 对于未购买的用户, 肯定符合促销价的条件. 如果用户已经付费, 通过用户本地的付费收据, 可以判断用户是否符合促销定价的条件. 如果可以的话, 通过 App 的业务后台进行一次预判断会更好. 如果用户已付费, 具体流程如下
读取用户本地存储的付费票据信息
判断票据信息中
Subscription Trial Period
字段和
Subscription Introductory Price Period
字段
如果这两个字段中有一个为 true, 表示用户已经正在享受促销定价周期, 由于每个订阅群组的新顾客和重新订阅的顾客只可享受一次折扣价或免费试用, 因此这个时候用户并不符合促销定价的条件
总结一下, 符合促销定价的用户, 只有以下两种类型
新的购买者
续期订阅的购买者并且之前没有享受过促销定价
- Show Code
- if SKPaymentQueue.canMakePayments() {
- let request = SKProductsRequest(productIdentifiers:
- self.productIdentifiers as Set<NSObject>)
- request.delegate = self
- request.start()
- }
- public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse)
- {
- let products = Set<SKProduct>(response.products)
- if (products.count != 0) {
- for var i = 0; i <products.count; i++
- {
- let product = products[i] as? SKProduct
- let introductoryPrice: SKProductDiscount = product.introductoryPrice // 这里获取促销定价的对象
- // 假设我们设定了一个付费周期为 3 个月, 头两个月以促销价结算的商品, 并且付费方式后台设定是随用随付, 它的值展开如下所示
- /*
- * introductoryPrice.price = 1.99
- * introductoryPrice.priceLocale.localizedString = "1.99" // 本地化货币价格
- * introductoryPrice.subscriptionPeriod.unit = .month // 以月为周期
- * introductoryPrice.subscriptionPeriod.numberOfUnits = 3 //1 个周期为 3 个月
- * introductoryPrice.numberOfPeriods = 2
- * introductoryPrice.paymentMode = .payAsYouGo // 随用随付
- */
- }
- } else {
- println("No products found")
- }
- let invalidproducts = response.invalidProductIdentifiers
- for product in invalidproducts
- {
- println("Product not found: \(product)")
- }
- }
试用版
我们同样可以为普通 App 设置试用体验, 等用户付费以后再解锁相关功能, 这是 iOS12 发布以后, App Store Review Guidelines 中新增的功能(6 月 4 日更新的版本)
3.1.1 In-App Purchase: Non-subscription apps may offer a free time-based trial period before presenting a full unlock option by setting up a Non-Consumable IAP item at Price Tier 0 that follows the naming convention: "14-day Trial." Prior to the start of the trial, your app must clearly identify its duration, the content or services that will no longer be accessible when the trial ends, and any downstream charges the user would need to pay for full functionality. Learn more about managing content access and the duration of the trial period using Receipts and Device Check.
意思是你可以对你的付费 App 设置一个 14 天的免费试用期(此时用户实际上已发起了付费信息, 安装 App 后在 bundle 里面含有票据), 当试用期满以后, 内容或服务不再允许访问, 用户需要付费才可以继续使用.
注意
如果使用了该特性的 App , 在提交 AppStore 审核的时候, 必须注意以下三点避免被拒风险
App 在醒目的位置声明了试用期的剩余时间
给用户展示解锁功能的费用
明确说明, 当试用期结束时, 有哪些产品特性或本地内容将会丢失
No Code,Manage Your App With App Store Connect
此功能是 App Store Connect 新增的功能, 只需要前端配合展示相关内容信息即可
应用内评价
应用内评价是 iOS10.3 新增的功能, 该功能使用了私有方法来分析当前是否是向用户询问评分的好时机, 所以苹果强烈建议开发者不要在响应用户行为时调用此方法. 例如, 如果你把请求评分放在按钮触摸的回调函数里, 但此时 iOS 可能决定不显示评分, 所以用户就会认为 App 的功能出现了问题. 另一方面, 也不要太早让用户评分, 最好等 App 运行几次之后再询问评分. 尽管我们并不了解苹果的算法, 但我们知道此方法的行为模式, 所以最好在确定用户处于合适的时间时再进行调用.
注意
在本地调试代码的时候, 也就是说每次进行请求调用, 评分对话框都会显示, 但无法提交评分. 在 Testflight 中, 请求都不会被通过, 所以如果 Testflight 测试时评分对话框没有正确显示, 不要慌张. App 上架后, 就会在合适的时间显示对话框了. 本次会议, 苹果的工程师还特别强调了两种限制场景
对于一个应用而言, 该函数的调用次数在同一设备上,
365
天内是有限制的. 根据苹果人机交互文档 https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/ 中的说明,
The system automatically limits the display of the prompt to three occurrences per app within a 365-day period
, 也就是说一年之内最多只会出现三次.
用户可以在设置中关闭应用内弹窗功能
- Show Code
- if(canShowReview()/* 一般是 App 业务的判断, 如是否满足活跃用户等条件 */){
- SKStoreReviewController.requestReview() /* 这个调用将会异步出现对话框, 所以不会阻塞当前流程 */
- recordShowTimes()/* 应该有节操地调用评分弹窗的接口 */
- }
- func canShowReview() -> Bool
- {
- // Local business rules
- }
- One more thing
应用内评价弹窗确实有次数限制, 但是苹果还是给开发者留了一手, 现在只需要给 AppStore 商店链接加上
?action=write-review
即可, 如
https://itunes.apple.com/us/app/itunes-u/id490217893?action=write-review
, 这样用户就可以跳转页面并弹起评价弹框. 所以对于上述的流程, 我们可以再扩展一下
- if(canShowReview()/* 一般是 App 业务的判断, 如是否满足活跃用户等条件 */){
- if(isLimited()/* 应用内评价窗口不能弹起 */){
- // 感谢苹果爸爸的接口
- UIApplication.shared.openURL(myAppStoreLink()+"?action=write-review")/* 跳转并提示用户进行评分 */
- }else{
- SKStoreReviewController.requestReview() /* 这个调用将会异步出现对话框, 所以不会阻塞当前流程 */
- recordShowTimes()/* 应该有节操地调用评分弹窗的接口 */
- }
- }
沙盒测试
由于付费功能如此重要, 一般我们在上线前都希望先在测试环境下进行调试, 苹果对于 IAP 也相应地给开发者配套了沙盒环境.
对于沙盒测试来说, 后台必须指定的账号进行测试, 同时前端也要将 SKMutablePayment 对象的
simulatesAskToBuyInSandbox
设置为 YES 才会连到苹果的沙箱环境. 测试自动更新类 IAP 时, 有一个不同之处: 该购买是有周期的. 订阅会于 5 次更新后作废. 看到这里你肯定会想:"等等, 要是我设置了每月的订阅, 要测试过期得等到 5 个月后?" 实际上, 自动更新的 IAP 在沙箱环境下, 周期是会加速的, 更新是按分钟或小时计算. 对应的时间表如下所示
付费过程的处理
- class AppDelegate: UIResponder, UIApplicationDelegate, SKPaymentTransactionObserver {
- func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
- SKPaymentQueue.default().add(self)
- return true
- }
- public enum SKPaymentTransactionState : Int {
- case purchasing // Transaction is being added to the server queue.
- case purchased // Transaction is in queue, user has been charged. Client should complete the transaction.
- case failed // Transaction was cancelled or failed before being added to the server queue.
- case restored // Transaction was restored from user's purchase history. Client should complete the transaction.
- @available(iOS 8.0, *)
- case deferred // The transaction is in the queue, but its final status is pending external action.
- }
来源: https://juejin.im/post/5b1bfb1f5188257d421543e0