WWDC 2018:IAP最佳實踐並增強活動營銷功能

tom510230發表於2019-03-04

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

對於一個標準的 IAP 流程,大致如下圖所示

標準的IAP流程

翻譯成中文就是

  1. 設定商品 ID(跟 ITC 上面的 ID 一致)
  2. 根據商品 ID 去蘋果後臺獲取商品資訊,這時會獲得商品名字、價格等資訊
  3. 把 IAP 的購買介面展示給使用者,使用者可以同意購買並點選購買按鈕。
  4. 使用者授權購買,客戶端向伺服器傳送購買請求。
  5. 此時購買流程狀態變更,客戶端根據蘋果規定的狀態機流程處理回撥
  6. 如果購買請求驗證通過,客戶端此時解鎖內容或者提供金幣。
  7. 至此,整個交易流程結束。

以上是蘋果文件上的流程,應用到 App 的實際操作中,我們還應該包含以下流程

  1. 營銷活動配置
  2. 試用功能控制
  3. 沙箱聯調流程
  4. 安全性校驗

本次 WWDC 議題很好地描述了上述的細節

App 定價策略

一般的 App 營銷活動有如下的型別

App 營銷活動

iOS11.2的時候,蘋果增加了如下類和介面便於開發者實現上述業務流程(只針對自動續期訂閱的場景)

@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 來完成對應的業務流程。

建立推介促銷價

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 個月訂閱,2、4、6、8、10 和 12 個月
  • 3 個月訂閱,3、6、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 專題目錄

相關文章