WWDC 2018:IAP最佳实践并加强活动营销功能

Session: WWDC2018 Best Practices and What’s New with In-App Purchaseshtml

对于一个标准的 IAP 流程,大体以下图所示前端

标准的IAP流程

翻译成中文就是ios

  1. 设置商品 ID(跟 ITC 上面的 ID 一致)
  2. 根据商品 ID 去苹果后台获取商品信息,这时会得到商品名字、价格等信息
  3. 把 IAP 的购买界面展现给用户,用户能够赞成购买并点击购买按钮。
  4. 用户受权购买,客户端向服务器发送购买请求。
  5. 此时购买流程状态变动,客户端根据苹果规定的状态机流程处理回调
  6. 若是购买请求验证经过,客户端此时解锁内容或者提供金币。
  7. 至此,整个交易流程结束。

以上是苹果文档上的流程,应用到 App 的实际操做中,咱们还应该包含如下流程git

  1. 营销活动配置
  2. 试用功能控制
  3. 沙箱联调流程
  4. 安全性校验

本次 WWDC 议题很好地描述了上述的细节github

App 订价策略

通常的 App 营销活动有以下的类型算法

App 营销活动

iOS11.2的时候,苹果增长了以下类和接口便于开发者实现上述业务流程(只针对自动续期订阅的场景)swift

@available(iOS 11.2, *)
open class SKProductDiscount : NSObject {

    
    @available(iOS 11.2, *)
    open var price: NSDecimalNumber { get }

    
    @available(iOS 11.2, *)
    open var priceLocale: Locale { get }

    
    @available(iOS 11.2, *)
    open var subscriptionPeriod: SKProductSubscriptionPeriod { get }

    
    @available(iOS 11.2, *)
    open var numberOfPeriods: Int { get }

    
    @available(iOS 11.2, *)
    open var paymentMode: SKProductDiscount.PaymentMode { get }
}
复制代码

以上的 api 对应于App Store Connect后台中设定的推介促销价,能够设置为折扣价格或免费试用,根据适当的条件,App 能够为符合条件的用户显示促销价。接下来咱们先了解一些基本概念,而后再看看如何经过以上的 api 来完成对应的业务流程。api

建立推介促销价

App Store Connect中能够为 App 内购买项目的每一个订阅设置和管理推介促销价,并能够针对每一个地区设置一个当前价格和一个将来推介促销价。若是建立的新价格出现重叠日期,则最新一次操做将覆盖现有的推介促销价。操做流程以下安全

  1. 在首页上,点按“个人 App ”,而后选择与该 App 内购买项目相关联的 App。
  2. 在工具栏中,点按“功能”,而后在左列中点按“App 内购买项目”。
  3. 点按自动续期订阅,前往“订阅价格”部分,而后点按“添加”按钮(+)。
  4. 选择“设置推介促销价”。
    Set_an_introductory_price_for_an_auto-renewable_subscription_iOS-tvOS-macOS_Create_an_introductory_price_Step4
  5. 选择想要提供折扣价格的地区,而后再点按“下一步”。
    Set_an_introductory_price_for_an_auto-renewable_subscription_iOS-tvOS-macOS_Create_an_introductory_price_Step5
  6. 选择开始和结束日期。若是想让推介促销价无限期使用,能够选择“无结束日期”。而后点按“下一步”。
  7. 选择“随用随付”、“提早支付”或者“免费试用”,而后选择合适的时限、货币和价格。点按“下一步”。
    Set_an_introductory_price_for_an_auto-renewable_subscription_iOS-tvOS-macOS_Create_an_introductory_price_Step7
  8. 苹果会根据您选择的价格点自动为全部地区计算价格,但您能够为特定地区设定不一样的价格。在此处选择地区价格,而后点按“完成”。
    Set_an_introductory_price_for_an_auto-renewable_subscription_iOS-tvOS-macOS_Create_an_introductory_price_Step8

推介促销价有三种类型,分别以下服务器

  1. 随用随付
    用户将按选定时限的每一个结算周期支付推介促销价(例如,订阅的标准价格为 9.99 美圆,推介促销价为前 3 个月每个月 1.99 美圆)。
    结算周期可设定如下时限:
  • 1 周订阅,1 至 12 周
  • 1 个月订阅,1 至 12 个月
  • 2 个月订阅,二、四、六、八、10 和 12 个月
  • 3 个月订阅,三、六、9 和 12 个月
  • 6 个月订阅,6 和 12 个月
  • 1 年订阅,1 年
  1. 提早支付
    用户将一次性支付选定时限的推介促销价(例如,订阅的标准价格为 9.99 美圆,推介促销价为前 2 个月 1.99 美圆)。可设定如下时限:1 个月、2 个月、3 个月、6 个月或 1 年。

  2. 免费试用
    用户在选定的时限内免费访问订阅。时限能够是 3 天、1 周、2 周、1 个月、2 个月、3 个月、6 个月或 1 年。

符合促销订价的用户

促销价的展现依赖于用户的购买状态,对于未购买的用户,确定符合促销价的条件。若是用户已经付费,经过用户本地的付费收据,能够判断用户是否符合促销订价的条件。若是能够的话,经过App的业务后台进行一次预判断会更好。若是用户已付费,具体流程以下

  1. 读取用户本地存储的付费票据信息
  2. 判断票据信息中Subscription Trial Period字段和Subscription Introductory Price Period字段
  3. 若是这两个字段中有一个为true,表示用户已经正在享受促销订价周期,因为每一个订阅群组的新顾客和从新订阅的顾客只可享受一次折扣价或免费试用,所以这个时候用户并不符合促销订价的条件

总结一下,符合促销订价的用户,只有如下两种类型

  1. 新的购买者
  2. 续期订阅的购买者而且以前没有享受过促销订价

相关代码

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审核的时候,必须注意如下三点避免被拒风险

  1. App 在醒目的位置声明了试用期的剩余时间
  2. 给用户展现解锁功能的费用
  3. 明确说明,当试用期结束时,有哪些产品特性或本地内容将会丢失

如何使用

此功能是App Store Connect新增的功能,只须要前端配合展现相关内容信息便可

应用内评价

应用内评价是iOS10.3新增的功能,该功能使用了私有方法来分析当前是不是向用户询问评分的好时机,因此苹果强烈建议开发者不要在响应用户行为时调用此方法。例如,若是你把请求评分放在按钮触摸的回调函数里,但此时 iOS 可能决定不显示评分,因此用户就会认为 App 的功能出现了问题。另外一方面,也不要太早让用户评分,最好等 App 运行几回以后再询问评分。尽管咱们并不了解苹果的算法,但咱们知道此方法的行为模式,因此最好在肯定用户处于合适的时间时再进行调用。

注意

在本地调试代码的时候,也就是说每次进行请求调用,评分对话框都会显示,但没法提交评分。在Testflight中,请求都不会被经过,因此若是Testflight测试时评分对话框没有正确显示,不要慌张。App 上架后,就会在合适的时间显示对话框了。本次会议,苹果的工程师还特别强调了两种限制场景

  1. 对于一个应用而言,该函数的调用次数在同一设备上,365天内是有限制的。根据苹果人机交互文档中的说明,The system automatically limits the display of the prompt to three occurrences per app within a 365-day period,也就是说一年以内最多只会出现三次
  2. 用户能够在设置中关闭应用内弹窗功能

相关代码

if(canShowReview()/*通常是App业务的判断,如是否知足活跃用户等条件*/){
	SKStoreReviewController.requestReview() /*这个调用将会异步出现对话框,因此不会阻塞当前流程*/
	recordShowTimes()/*应该有节操地调用评分弹窗的接口*/
	/* * 为何这里须要增长showtime这个维度呢?考虑一下这个场景 * 已经判断是活跃分子,调用苹果api,这个时候苹果的算法判断须要出现弹窗 * 用户点取消 * 第二次App启动后,用户又触发了弹窗的知足条件,这个时候依然知足苹果的算法 * 弹窗再次出现 * 此时已构成骚扰,用户给差评的几率大大上升,所以showtime的这个维度仍是很是值得加入到产品逻辑中去的 */
}

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 在沙箱环境下,周期是会加速的,更新是按分钟或小时计算。对应的时间表以下所示

时间表

付费过程的处理

关于付费票据的处理过程,去年的WWDC2017已经有详细讨论过了,推荐阅读这篇文章,此次会议讨论了一些代码上的使用细节

Transaction Observer

对于SKPaymentTransactionObserver的回调处理,应该注册在 App 启动的时候,这样咱们能第一时间知道用户是否完成购买流程

class AppDelegate: UIResponder, UIApplicationDelegate, SKPaymentTransactionObserver {
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    SKPaymentQueue.default().add(self)
    return true
}
复制代码

这是由于用户的购买流程跟 App 生命周期不挂钩致使的,典型的有如下几个场景

  • 用户杀死了 App
  • 用户须要更新账号中的付费信息(此时已跳出 App )
  • App 闪退
  • 用户进行了订阅续期
  • 用户进入了推介促销价的流程
  • 用户跳出 App 输入推广码

Transaction State

正确理解购买流程中的各状态表明的意义,是很是关键的

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.
}
复制代码

结合上述代码,咱们看一下下面的这个对应表

Transaction State Action Needed
.purchasing 不须要作什么,继续等待SKPaymentTransactionState的状态流转
.purchased 用户已完成付费,处理付费后的流程并调用finishTransaction方法
.failed 用户付费失败,处理付费失败的流程并调用finishTransaction方法
.restored 用户已完成付费,处理付费后的流程并调用finishTransaction方法
.deferred 不须要作什么,继续等待SKPaymentTransactionState的状态流转

deferred这个状态比较特殊,通常是家长控制致使的,即该购买请求会发送到孩子的家长账号上,若是家长赞成付费,该付费流程才能完成。可是deferred不会永远是deferred,有一个对应的过时时间以下图所示

过时时间

Finishing Transactions

对于购买完成的这个流程咱们须要当心处理,否则一不当心就成为一个 App 漏洞形成较大损失(对于游戏项目而言确实是很可怕的事情)

  • 确保付费内容都已从苹果后台下载,而后才调用finishTransaction方法,否则该方法会阻止全部的下载流程,而且没法从新下载
  • 必定要经过业务后台去校验票据,不要在前端直接校验

用户票据的处理

这已是一个很老的话题了,此次会议从新再整理几个要点

  • 票据验证必定是经过Server-to-Server的方式去验证,前端环境是不可信任的
  • 票据的失效日期比对,不要经过用户的本地时间(能够人为变动),而应该根据票据中的购买时间(已固化没法变动)去比对
  • 本地票据由一个通过签名的 PKCS #7 容器组成,开发者能够实现本身独有的的收据验证代码
  • 经过SKReceiptRefreshRequest的接口,能够向苹果后台从新请求用户的付费票据,这个流程是异步的
  • 经过遍历票据内容,能够判断用户是否继续处于试用版的流程中,默认认为用户处于试用版
  • 若是在App Store Connect后台中更新了收费类型,理应对用户已付费过的功能进行保持而不是从新收费

总结

苹果此次会议中介绍的新功能,有很多是针对自动订阅付费场景,自动订阅是指用户能够购买指定时间期限内的更新和动态内容,除非用户取消选择,不然订阅(例如杂志订阅等)会自动续订。而对于中国区的开发者而言,更常见的场景是一次性付费,如王者荣耀中的充值功能,微信读书中的买书功能等。所以暂时而言,苹果的一些关于 IAP 的新功能,在中国开发者的眼里并非太有用。对比一下国内的一些 IAP 管理后台,如腾讯的米大师等,在功能的便利性和普适性上还有很大的差距,但愿苹果后面能对IAP的相关功能投入更大的精力去开发和升级,为开发者提供更好用的应用管理平台。


参考资料

苹果文档 In-App Purchase Best Practices

Offering Introductory Pricing in Your App

为自动续期订阅设定推介促销价

Validating Receipts With the App Store

App Store Guidelines最新版

基于Swift最流行的StoreKit封装库

腾讯米大师文档

查看更多 WWDC 18 相关文章请前往老司机x知识小集xSwiftGG WWDC 18 专题目录