RevenueCatのPaywallは、完全なネイティブビューとしてレンダリングされる高度にカスタマイズ可能なペイウォールを提供します。しかし、エンタイトルメントやオファー、パッケージの管理にはRevenueCatを活用しつつ、独自のカスタムペイウォールを実装したいケースもあるでしょう。本記事では、RevenueCatのPaywallとカスタムペイウォールを効果的に併用する方法について解説します。

なぜ複数のペイウォールを運用するのか?

なぜ複数のペイウォールを同時に管理する必要があるのか、疑問に思うかもしれません。以下は、このアプローチが特に有効なケースです。

  • 移行期間:これまで独自のカスタムペイウォールを構築してきたが、段階的にRevenueCatのペイウォールへ移行したい場合。
  • A/Bテスト:既存のハードコードされたペイウォールと、RevenueCatで新たに構築したペイウォールを比較テストしたい場合。
  • ターゲット別オファー:特定のユーザーセグメント向けに、独自の価格設定やプロモーションオファーを提示する必要があり、高度にカスタマイズされた表示が求められる場合。
  • 高度なロジック:複雑なオンボーディングフローや特定のユーザージャーニーに対応する必要がある場合。

アプリ側でどのペイウォールUIを表示するかを決定しつつ、すべての購入処理は引き続きRevenueCat SDKを通して行います。これにより、ロジックの重複や価格の不一致、エンタイトルメント関連のバグを防ぎながら、必要に応じて表示部分の完全なコントロールを維持できます。

エンドツーエンドのフロー例

全体像として、2種類のペイウォールタイプを持つアプリは次のように動作します。

  1. ユーザーがペイウォールの分岐ポイントに到達する
    • 例:オンボーディングの終了、機能のゲート、プロモーションの入口など
  2. アプリが特定の Placement に対して Offerings をリクエストする
    • Placement は、ユーザージャーニーのどこでペイウォールを表示するかを表します
  3. RevenueCat が適切な Offering を返す
    • 返される Offering は、ターゲティングルールに応じてユーザーごとに異なる場合があります
  4. アプリが Offering のメタデータを確認する
    • メタデータによって、どのペイウォールを表示すべきかが決まります
  5. アプリが表示するペイウォール UI を選択する
    • RevenueCat のペイウォール UI
    • または、完全にカスタム実装したペイウォール UI
  6. ユーザーがプロダクトを選択する
    • プロダクト情報と価格データは常に RevenueCat から取得します
  7. RevenueCat SDK 経由で購入を開始する
    • ペイウォール UI がどちらでも、購入フローは同一です
  8. RevenueCat がエンタイトルメントを更新する
    • アプリが CustomerInfo に基づいてコンテンツを解放/制限する

この仕組みにより、表示は柔軟にしつつ、サブスクリプションシステムは一元化して整合性を保つことができます。さらに RevenueCat の Experiment 機能を使えば、異なる Placement やペイウォール間でコンバージョン成果をテスト・比較することも可能です。

RevenueCat Paywallの理解

RevenueCatのペイウォールは、ユーザーにプロダクトを表示するプロセスを簡素化するよう設計されています。RevenueCat Paywallsのドキュメントにある通り、RevenueCatダッシュボード上で直接、さまざまなペイウォールデザインを作成・管理・テストすることが可能です。プロダクト情報の取得も自動で処理され、ユーザーフレンドリーな形で表示されます。しかも、ペイウォールを変更するたびにアプリの新しいリリースをストアに提出する必要はありません。

ほとんどの新規アプリにとって、RevenueCatでペイウォールを実装するのが最適な選択です。ストアにアップデートを提出することなく、ペイウォールを継続的に改善・更新できるからです。

カスタム実装のペイウォールを実装する

前述のとおり、RevenueCatのPaywallだけでは要件を満たせないケースもあります。たとえば、アプリ内の重要なデザイン要素をペイウォールにも表示したい場合です。ゲーミフィケーションされたアプリであれば、途切れてしまった連続記録(broken streak)の可視化をペイウォール上に出して、「サブスクに登録すればこの途切れた記録を修復できる」ことをユーザーに伝えたい、といったケースが考えられます。

カスタム実装のペイウォールを採用する場合、UI/UXは基本的に自分たちでコントロールすることになります。一方で、バックエンド側の処理(プロダクト情報の取得や購入処理)については、引き続きRevenueCat SDKに依存します。

ステップ1:プロダクト情報を取得する

カスタム実装のペイウォールを作る最初のステップは、RevenueCatからプロダクト情報を取得することです。これは、ユーザーに正しいサブスクリプションの選択肢、価格、導入オファー(初回オファー)を表示するために不可欠です。詳細な手順は、Displaying Products のドキュメントを参照してください。ctions.

Swift では通常、 Purchases.shared.getOfferings() メソッドを使って設定済みの Offerings を取得します。同様に Kotlin では、Purchases.sharedInstance.getOfferingsWith() を使用します。

これにより Offering オブジェクトが取得でき、必要なプロダクト詳細がすべて含まれています。

        Purchases.shared.getOfferings { (offerings, error) in
            if let packages = offerings?.current?.availablePackages {
                self.display(packages)
            }
        }
Purchases.sharedInstance.getOfferingsWith({ error ->
  \/\/ An error occurred
}) { offerings ->
  offerings.current?.availablePackages?.takeUnless { it.isNullOrEmpty() }?.let {
    \/\/ Display packages for sale
  }
}

ステップ2:購入処理を実行する

ユーザーがカスタム実装のペイウォール上でプロダクトを選択したら、RevenueCatのSDKを使って購入を開始します。この手順については、Making Purchases のドキュメントで詳しく解説されています。

iOSでは Purchases.shared.purchase(package:)を使用し、AndroidではPurchases.sharedInstance.purchaseWith()を使用します。購入完了時の処理や、発生し得るエラーのハンドリングを忘れずに実装してください。

Purchases.shared.purchase(package: package) { (transaction, customerInfo, error, userCancelled) in
  if customerInfo.entitlements["your_entitlement_id"]?.isActive == true {
    \/\/ Unlock "pro" content              
  }
}
Purchases.sharedInstance.purchaseWith(
  PurchaseParams.Builder(this, aPackage).build(),
  onError = { error, userCancelled -> \/* No purchase *\/ },
  onSuccess = { storeTransaction, customerInfo ->
    if (customerInfo.entitlements["my_entitlement_identifier"]?.isActive == true) {
      \/\/ Unlock "pro" content
    }
  }
)

ステップ3:Entitlement(権限)の管理

RevenueCatのペイウォールを使用する場合でも、カスタム実装のペイウォールを使用する場合でも、entitlement(権限)の管理はRevenueCatが処理します。購入が成功すると、RevenueCatはユーザーの CustomerInfoを更新します。これを利用して、プレミアム機能へのアクセスを付与または制限できます。Swiftでは、 Purchases.shared.customerInfo()を使用して CustomerInfo を取得できます。

Purchases.shared.getCustomerInfo { (customerInfo, error) in
    \/\/ access latest customerInfo
}

Kotlinで

Purchases.sharedInstance.getCustomerInfoWith(
    onError = { error -> \/* Optional error handling *\/ },
    onSuccess = { customerInfo -> \/* Access latest customerInfo *\/ },

ペイウォールの条件付き表示ロジック

異なるペイウォールやOfferingを表示するロジックは、Placement または Offering metadata を使って制御できます。Placement を使うと、各Offeringをアプリ内のどこで表示するかを定義できます。一方、Offering metadata を使うと、Offeringに追加情報を付与でき、それをアプリ側で参照することで「カスタム実装のペイウォールを表示するのか」「RevenueCatのペイウォールを表示するのか」を切り替えることができます。

Placementによるターゲティング

Targeting by Placement を使うことで、アプリ内のペイウォール表示位置ごとに異なるOfferingを配信できます。たとえば、以下のような場所ごとに異なるペイウォールを表示できます。

  • オンボーディング終了時(例:onboarding_end
  • ユーザーが有料機能を使おうとしたとき(例:feature_gate
  • セール実施中(例:sale_offer

Placementを設計する際の基本は、理想的なカスタマージャーニーをどう補完するかを理解することです。たとえばセールを実施している場合、初回利用ユーザーと既存ユーザーでは、表示すべきオファーやコピーが異なる可能性があります。そのような場合は sale_offer というPlacementを定義します。

アプリがPlacementを指定してOfferingsを取得すると、RevenueCatはそのPlacementとユーザーに応じたOfferingを返します。これにより、カスタマージャーニーに応じたユニークなペイウォールを表示できます。Swiftでは、次のように実装できます。

Purchases.shared.getOfferings { offerings, error in
    if let offering = offerings?.currentOffering(forPlacement: "your_placement_identifier") {
        \/\/ Show paywall
    } else {
        \/\/ Do nothing or continue on to next view
    }
}

同様にKotlinでは:

Purchases.sharedInstance.getOfferingsWith({ error ->
    \/\/ An error occurred
}) { offerings ->
    offerings.getCurrentOfferingForPlacement("your-placement-identifier")?.let {
        \/\/ show paywall
    } ?: run {
        \/\/ Do nothing or continue on to next view
    }
}

Offering metadataを使ったターゲティング

異なる種類のペイウォールを運用し、カスタム実装のペイウォールに表示する内容を柔軟に制御するための鍵となるのが Offering metadata です。RevenueCatダッシュボードでは、対象のOfferingに移動し、「Edit」または「Configure metadata」(まだ設定していない場合)をクリックすることで、Metadataフィールドに有効なJSONを追加できます。

ダッシュボードで設定するMetadataは、たとえば次のような形式になります。

{
  "custom_paywall": true,
}

このmetadataは、SDKを呼び出して利用可能なOfferingsを取得する際に、Offeringデータに紐づけられます。そのため、このmetadataを使って、アプリ内でのペイウォール表示方法を動的に変更できます。

SDKでOffering metadataを使用する

Swiftでは、RevenueCat SDKの offerings メソッドを使って取得した Offering オブジェクトから、metadata に直接アクセスできます。

do {
    let offerings = try await Purchases.shared.offerings()

    if let offering = offerings.current {
        let useCustomPaywall = offering.metadata["custom_paywall"] as? Bool

        if useCustomPaywall == true {
            \/\/ Show custom paywall UI
        } else {
            \/\/ Show RevenueCat Paywalls UI
        }
    }
} catch {
    \/\/ An error occurred
}

そしてKotlinでは同じ方法で:

Purchases.sharedInstance.getOfferingsWith({ error ->
    \/\/ An error occurred
}) { offerings ->
    offerings.current?.let { offering ->
        val useCustomPaywall = offering.metadata["custom_paywall"] as? Boolean

        if (useCustomPaywall == true) {
            \/\/ Show custom built paywall UI
        } else {
            \/\/ Show RevenueCat Paywalls UI
        }
    }


metadataの値は本質的にオプションです。そのため、ダッシュボードで値が定義されていない場合でも、アプリやペイウォールが正常に動作するように実装する必要があります。

条件分岐ロジックの例

最後に残っているステップは、カスタム実装のペイウォールを表示するか、RevenueCat ペイウォールを表示するかを判定するロジックを適用することです。

func shouldShowCustomPaywall(offering: Offering) -> Bool {
    offering.getMetadataValue(for: "custom_paywall", default: false)
}

func showPaywall(for offering: Offering) {
    if shouldShowCustomPaywall(offering: offering) {
        \/\/ Show your custom paywall UI
        displayCustomPaywall(offering: offering)
    } else {
        \/\/ Show RevenueCat's default paywall or a paywall built with RevenueCat's templates
        displayRevenueCatPaywall(offering: offering)
    }
}

そしてKotlinでは:


fun shouldShowCustomPaywall(offering: Offering): Boolean {
    return offering.metadata["custom_paywall"] as? Boolean ?: false
}

fun showPaywall(offering: Offering) {
    if (shouldShowCustomPaywall(offering)) {
        \/\/ Show your custom paywall UI
        displayCustomPaywall(offering)
    } else {
        \/\/ Show RevenueCat's default paywall or a paywall built with RevenueCat's templates
        displayRevenueCatPaywall(offering)
    }
}

まとめ

独自に実装したカスタムペイウォールを、RevenueCatのペイウォールと併用することで、実験やターゲット別オファーにおける柔軟性を確保できます。プロダクト情報の取得や購入管理はRevenueCatに任せつつ、UI部分は自社のエンジニアリングで最適化することで、iOSおよびAndroidユーザーに対して高度に最適化された、魅力的なサブスクリプション体験を構築できます。このハイブリッドアプローチにより、フロントエンドの表現は自分たちでコントロールしながら、サブスクリプション基盤については堅牢で実績のあるバックエンドに依存することが可能になります。