App内課金のプロモーション

iOS 11以降、App Store上でApp内課金のプロモーションが可能になりました。プロモーション対象のApp内課金は、プロダクトページに現れ、検索結果として表示されるほか、App Storeのしかるべきタブ上に大きく取り上げられることもあります。ユーザーは、App Store上でApp内課金を立ち上げた後、Appに切り替えてトランザクションを続行できます。Appがインストール済みでなければ、ダウンロードするよう指示が現れます。

App内課金のプロモーション

App内課金のプロモーションは2段階に分けて行います。

  1. App Store ConnectでApp内課金をセットアップします。まず、対象とするApp内課金のプロモーション画像をアップロードします。次にApp Store ConnectのApp Storeプロモーションツールで、App Storeに現れる順序や表示/非表示を管理します。

  2. SKPaymentTransactionObserverプロトコルに従い、購入手続きに必要なデリゲートメソッドを実装します。

ユーザーごとに見え方をカスタマイズすることも可能です。SKProductStorePromotionControllerメソッドで、表示/非表示や出現順序を変更できるのです。

この機能のマーケティングガイドについては、「App内課金のプロモーション」を参照してください。

購入手続きの進行

App Store上に現れたApp内課金で、ユーザーが「Buy」をタップまたはクリックすると、StoreKitは自動的にAppを開き、SKPaymentTransactionObserverプロトコルのデリゲートメソッドを介してトランザクション情報を渡します。App側では、購入トランザクションおよびこれに関連する、App特有のアクションを実行しなければなりません。

デリゲートメソッドは、トランザクションを続行するならばtrue、延期またはキャンセルするならばfalseを返します。

ユーザーが「Buy」をタップまたはクリックした時点でAppが未インストールであれば、App Storeはこれを自動ダウンロードし、または購入するようユーザーに促します。インストール済みであっても版が古く、App内課金のプロモーションに未対応であれば、App Storeはアップグレードするようユーザーに促します。

トランザクションの続行

App内課金のトランザクションを続行するためには、SKPaymentTransactionObserverプロトコルのデリゲートメソッドを実装し、trueを返す必要があります。するとStoreKitは支払いシートを表示するので、ユーザーはトランザクションを進めることができます。

// App Storeからトランザクションを続行
 
//MARK: - SKPaymentTransactionObserver
 
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment,
        forProduct product: SKProduct) -> Bool {
    // トランザクションを進めることができるか確認。
    // 可能ならばtrueを返す。
    return true
}

トランザクションの延期またはキャンセル

トランザクションを延期またはキャンセルする必要があれば、falseを返すよう、デリゲートメソッドを実装します。延期するべき状況としては、ユーザーが航空機内にいて、着陸後に続行する場合が考えられます。キャンセルするべき状況としては、購入しようとしているプロダクトを、既にユーザーがロック解除してしまった、という場合があります。

トランザクションを延期するには:

  1. 続行するときに備えて、paymentを保存します。paymentにはプロダクトに関する情報が既に格納されています。同じプロダクトについて、改めてSKPaymentを生成しないでください。

  2. falseを返します。

  3. ユーザーが搭乗(その他、延期が必要になるアクション)を終えた後、保存しておいたpaymentを、通常のApp内課金の処理と同じ方法で、キューに送信してください。

トランザクションをキャンセルするには:

  1. falseを返します。

  2. (オプション)ユーザーにフィードバックを返します。ユーザーが「Buy」をタップまたはクリックしたとき、Appが何のアクションもしなければ、App Storeにバグがあると誤認されるかも知れません。

// App Storeからトランザクションを処理
 
//MARK: - SKPaymentTransactionObserver
 
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment,
        forProduct product: SKProduct) -> Bool {
 
    // ... トランザクションを延期する必要があるか、確認するコードを追加。
    let shouldDeferPayment = ...
    // 着陸まで延期する必要があれば、paymentを保存してfalseを返す。
    if shouldDeferPayment {
        self.savedPayment = payment
    return false
    }
 
    // ... トランザクションをキャンセルする必要があるか、確認するコードを追加。
    let shouldCancelPayment = ...
    // トランザクションをキャンセルする必要があれば、falseを返す。
    if shouldCancelPayment {
    return false
    }
}
 
//(トランザクションをキャンセルした場合、ユーザーにフィードバックを返す)
 
// 延期していた支払い処理を続行
SKPaymentQueue.default().add(savedPayment)
 
)

プロモーション対象プロダクトに関する高度な機能

プロモーション対象のApp内課金の提示方法をカスタマイズできます。SKProductStorePromotionControllerで、表示/非表示や出現順序を設定する、という形で行います。デフォルトの出現順序や表示/非表示は、App Store Connectで設定します。この設定のオーバーライドは、デバイスごとにそれぞれ行います。iCloudアカウントには同期しません。なお、この機能はローカルデバイスのストレージを使うので、APIを実行できるのは、当該デバイス上で少なくとも1度、Appを起動した後に限ります。

この高度な機能の実装はオプションです。App StoreにApp内課金を表示するだけならば、未実装でも構いません。

表示/非表示の設定の読み取り

あるプロダクトについて、表示/非表示の設定を読み取るためには、当該プロダクトの情報を渡してfetchStorePromotionVisibility(for:completionHandler:)メソッドを呼び出します。

// プロモーション対象のApp内課金について、オーバーライドした設定を読み取る。
 
// 「Hidden Beaches」パックのプロダクト情報を取得
 
let storePromotionController = SKProductStorePromotionController.default()
storePromotionController.fetchStorePromotionVisibility(forProduct: hiddenBeaches,
    completionHandler: { visibility: SKProductSTorePromotionVisiblity, error: Error?)in
        // visibility == .default

表示/非表示の設定のオーバーライド

あるApp内課金を表示するか否か、デバイスごとに指定できます。たとえば、購入済みのApp内課金は非表示にし、未購入の場合のみ表示する、といったことが考えられます。

例として、プロダクト「Pro Subscription」を、購入後は非表示になるよう制御してみましょう。プロダクト情報を取得し、デフォルトのストアプロモーションコントローラを、.hideという設定に変更するコード例を示します。以降、「Pro Subscription」というApp内課金が、このデバイスに現れることはありません。

// プロモーション対象のApp内課金の表示/非表示設定をオーバーライド
// 「Pro Subscription」のプロダクト情報を取得
 
let storePromotionController = SKProductStorePromotionController.default()
storePromotionController.update(storePromotionVisibility: .hide, forProduct: proSubscription,
    completionHandler: { (error: Error?) in
        // 完了
    })

オーバーライドする前の時点では、表示/非表示の設定は.defaultになります。App Store Connectで設定したデフォルト値に応じて、表示か非表示かが決まります。

出現順序のオーバーライド

プロモーション対象のApp内課金がApp Storeのプロダクトページに現れる順序を、デバイスごとに設定できます。たとえばあるゲームで、特定レベルの直前のレベルに達したとき、当該特定レベルのロックを解除するApp内課金プロダクトをプロモートする、といったことが可能です。

App内課金のデフォルトの出現順序をオーバーライドするためには、まず、順序を変更しようとするプロダクト群の情報を、画面に現れて欲しい順序に、配列に収容します。この配列を引数として、updateStorePromotionOrder:completionHandler:メソッドを呼び出してください。配列に収容されているプロダクトがリストの先頭に現れ、他のApp内課金はその後に、App Store Connectに設定された順序で並びます。

// プロモーション対象のApp内課金の出現順序を変更
 
// 「Pro Subscription」、「Fishing Hot Spots」、「Hidden Beaches」という3つのプロダクトの情報を取得
 
let storePromotionController = SKProductStorePromotionController.default()
let newProductsOrder = [hiddenBeaches, proSubscription, fishingHotSpots]
storePromotionController.updateStorePromotionOrder(newProductsOrder,
    completionHandler: { (error: Error?) in
        // 完了
    })

オーバーライドした出現順序のキャンセル

出現順序を元に戻すためには、空の配列を渡してupdateStorePromotionOrder:completionHandler:メソッドを呼び出します。以後、デフォルトの順序で表示されるようになります。

オーバーライドした順序設定の読み取り

あるデバイスに適用される、オーバーライドしたプロダクト出現順序設定を読み取るためには、fetchStorePromotionOrder(completionHandler:)メソッドを使います。プロダクトを出現順序に並べた配列が返されます。これが空の配列であれば、オーバーライドしていないので、デフォルトの順序で現れることになります。

// プロモーション対象のApp内課金の、オーバーライドされた出現順序を読み取り
 
let storePromotionController = SKProductStorePromotionController.default()
storePromotionController.fetchStorePromotionOrder(completionHander: {
    (products: [SKProduct], error: Error?)in
        // products == [hiddenBeaches, proSubscription, fishingHotSpots
    })

プロモーション対象のApp内課金のテスト

プロモーション対象のApp内課金は、App Storeに公開する前にテストできます。Appleは、「itms-services://」プロトコルでAppを起動できるよう、システムURLを提供しています。

表 4-1 プロモーション対象のApp内課金のテストに用いるシステムURL

プロトコル

itms-services://

パラメータ action

purchaseIntent

パラメータ bundleId

AppのバンドルID。例:

com.example.app

パラメータ productIdentifier

テストしようとするApp内課金のプロダクト名。例:

product_name

最終的なURLは次のようになります。

itms-services://?action=purchaseIntent&bundleId=com.example.app&productIdentifier=product_name

このURLを電子メールやiMessageで自分自身に送信し、これをデバイスから開いてください。Appが自動的に開けば、プロモーション対象のApp内課金のテストを開始できる状態です。