Androidのサブスクリプションコンバージョン率は、iOSと比較するとプラットフォームの問題のように見えます。RevenueCatが「State of Subscription Apps 2026」レポートのために11万5,000以上のアプリと160億ドル以上の収益を分析したところ、数値は明確でした。AndroidではDay 35時点のダウンロードから課金へのコンバージョン率の中央値は0.9%であるのに対し、iOSでは2.6%です。これはほぼ3倍の差です。この差を見ると、トライアルやユーザー層、あるいはAndroidユーザーの行動そのものに根本的な違いがあるのではないかと考えがちです。しかし、データはそうではないことを示しています。
同じデータセットをもう一段深く見ると、Androidのトライアルから課金へのコンバージョン率は32.5%、iOSは32.6%となっています。つまり、どちらのプラットフォームでも、一度トライアルを開始したユーザーは統計的にほぼ同じ割合で課金に至っています。ただし重要な注意点があります。Androidではトライアルを開始するユーザー層がより絞り込まれている可能性があります。Androidではそもそもトライアルが表示される機会が少ないため、トライアルを開始するユーザーはより購買意欲が高い傾向にあります。この選択バイアスが、両者のコンバージョン率が近づく理由の一部となっています。それでもなお、主要なレバーは明確です。Androidアプリは、この最初のステージにユーザーを十分に送り込めていません。このギャップを埋めるには、まずより多くのユーザーにトライアルを開始してもらうことから始まります。
本記事では、Androidのペイウォールファネルが2段階で構成されてい ることと、そのどこでユーザーが離脱しているのか、Google Playのオファーおよびタグシステムがどのサブスクリプションオプションをユーザーに表示するかをどのように制御しているのか、RevenueCatのSubscriptionOptionsの選択ロジックがどのように機能し、どこでサイレントな設定ミスが発生するのか、ハードペイウォールとフリーミアムモデルに関するデータが何を示しているのか、そしてRevenueCat Experimentsを使ってこのギャップを体系的に解消する方法について解説します。
根本的な問題:2つのステージ、そのうち1つが壊れている
ダウンロードから課金に至るまでのユーザージャーニーは、2つの明確なステージに分かれており、それぞれ異なる挙動を示します。
最 初のステージは「ダウンロードからトライアル開始まで」です。ユーザーはアプリをインストールし、ペイウォールに到達し、無料トライアルを開始するかどうかを判断します。2つ目のステージは「トライアル開始から課金まで」です。トライアルが終了し、ユーザーがサブスクライバーとして継続するかどうかを決定します。
RevenueCatのデータは、Androidにおいてこの2つのステージの挙動が大きく異なることを示しています。トライアルを開始したユーザーは32.5%の確率で課金に至り、iOSの32.6%とほぼ同じです。しかし、Androidアプリはこの最初のステージに送り込めているユーザー数が大幅に少ないのです。コンバージョンギャップの大部分は、このステージ1に存在しています。
これを具体的に示すデータがあります。トライアル開始の89.4%はインストール当日に発生しています。購買意欲の高いユーザーは、ダウンロード直後に行動します。一方で、インストール当日にトライアルを開始しなかったユーザーは、後から戻って開始することはほとんどありません。つまり、Androidにおける最初のペイウォール表示は、サブスクリプション収益の大半を決定づける瞬間なのです。その後のトライアル体験、オンボーディング、プロダクト自体のパフォーマンスは、AndroidでもiOSでもほぼ同等です。問題は、ユーザーがそもそもその瞬間に到達しているかどうかなのです。
ステージ1:ユーザーがトライアルを見るかどうかを決める要因
Androidでユーザーに無料トライアルが提示されるかどうかは、2つの要素によって決まります。それは「何を表示するか(ペイウォールの種類)」と「いつ表示するか(タイミング)」です。どちらも完全にコントロール可能です。
ペイウォールタイプの差
RevenueCatのデータでは、ペイウォールモデルごとにD35(35日間)のダウンロードから課金へのコンバージョン(最大1ヶ月のトライアル期間をカバーする測定ウィンドウ)が分析されています。コア機能にアクセスする前にサブスクリプションオファーとのインタラクションを必須とするハードペイウォールは、D35コンバージョンの中央値が10.7%です。さらに、上位10%のハードペイウォールアプリでは38.7%に達します。一方で、無料で一部機能を利用できるフリーミアムモデルの中央値は2.1%です。
これはコンバージョンにおいて約5倍の差でありながら、年間リテンションはほぼ同じです。ハードペイウォールは12ヶ月後に27%のサブスクライバーを維持し、フリーミアムは28%です。ほとんどのアプリカテゴリにおいて、ハードペイウォールの数値は大幅に優れています。もしあなたのプロダクトが1回のセッションで明確かつ即時に価値を提供できるのであれば、ハードペイウォールはほぼ確実に適したモデルです。一方で、フリーミアムが適しているのは、ネットワーク効果や価値の発見に時間がかかるカテゴリ――たとえばソーシャルアプリやコミュニティツールのように、収益化の前に広いユーザーベースの獲得が重要な場合です。
| ペイウォールモデル | D35コンバージョン中央値 | 上位10%のD35コンバージョン | 12ヶ月後のリテンション |
|---|---|---|---|
| ハードペイウォール | 10.7% | 38.7% | 27% |
| フリーミアム | 2.1% | — | 28% |
ただし、フリーミアムが後半のコンバージョンで優位性を示すケースが1つあります。6週目時点では、フリーミアムアプリはそのコホートの22.9%をコンバージョンさせるのに対し、ハードペイウォールは15.3%にとどまります。もしプロダクトが数週間かけて徐々に価値を発揮するような長い発見サイクルを持つ場合、フリーミアムはハードペイウォールでは取りこぼしてしまうユーザーを獲得できるのです。
ペイウォール表示タイミングの差
トライアル開始の89.4%がDay 0(初日)に集中しているという事実は、タイミングに対して明確な示唆を与えます。ペイウォールは最初のセッションで表示すべきです。最初のセッション以降は、効果が急速に低下していきます。
ただし、これはオンボーディングの前にペイウォールを表示すべきという意味ではありません。ユーザーがプロダクトの価値を理解する前にペイウォールを表示するアプリは、一般的にオプトイン率が低くなります。効果的なパターンはこうです:まず1つの強い価値体験(1つのタスクの完了、主要機能の提示、具体的なアウトプット)を提供し、その後にペイウォールを表示する。しかも、それをDay 0に行うことです。
見えない失敗:オファー設定ミスによってトライアルがサイレントに抑制される場合
ペイウォールの種類とタイミングが正しくても、Androidにはもう一つ見落とされがちなトライアル失敗の原因があります。それは、サブスクリプションオファー自体がユーザーに表示されていない可能性です。これはGoogle Playの設定に関する問題であり、エラーが表示されないまま発生することがあります。
Google Playにおけるサブスクリプションオファーの構造
Google Playのすべてのサブスクリプションは、ベースプランと、必要に応じて1つ以上のオファーで構成されます。オファーは、ベースプランの価格に先立つプロモーション価格のフェーズ(無料トライアル、導入価格、またはその両方)を定義します。オファーはBilling Library内では ProductDetails.SubscriptionOfferDetails として表現されます。
各 SubscriptionOfferDetails オブジェクトは、価格フェーズのリスト、オファータグのセット、そして購入を開始するためのオファートークンを持っています。価格フェーズは各ステージの価格と期間を示します。オファータグは、Play Console上でベースプランまたはオファー単位で設定する文字列です。Billing Libraryでは getOfferTags() によって両方のタグの集合(ユニオン)が返されるため、ベースプランに設定したタグは、その下のすべてのオファーにも自動的に適用されます。
RevenueCatがプロダクト情報を取得すると、各 SubscriptionOfferDetails は GoogleSubscriptionOption に変換されます。 subscriptionOptionConversions.kt における変換処理を見ると、以下のようになります:
1internal fun ProductDetails.SubscriptionOfferDetails.toSubscriptionOption(
2 productId: String,
3 productDetails: ProductDetails,
4): GoogleSubscriptionOption {
5 val pricingPhases = pricingPhases.pricingPhaseList.map { it.toRevenueCatPricingPhase() }
6 return GoogleSubscriptionOption(
7 productId,
8 basePlanId,
9 offerId,
10 pricingPhases,
11 offerTags,
12 productDetails,
13 offerToken,
14 presentedOfferingContext = null,
15 installmentPlanDetails?.installmentsInfo,
16 )
17}
offerId はベースプランでは null であり、オファー付きのオプションでは値が設定されます。 The offerToken はユーザーが「無料トライアルを開始」をタップした際に課金フローへ渡されるものであり、The offerTags にはPlay Consoleで割り当てたラベルが含まれます。
RevenueCatがデフォルトオファーを選択する仕組み
プロダクトに GoogleSubscriptionOption オブジェクトのリストが含まれると、RevenueCatはそれらを SubscriptionOptions コレクションにまとめ、 defaultOffer プロパティとして公開します。これは、明示的に別のオプションを選択しない限り、ペイウォール上に表示されるオプションです。
SubscriptionOptions.kt における選択アルゴリズムは次のように動作します:
1public val defaultOffer: SubscriptionOption?
2 get() {
3 val basePlan = this.firstOrNull { it.isBasePlan } ?: return null
4
5 val validOffers = this
6 .filter { !it.isBasePlan }
7 .filter { !it.tags.contains(RC_IGNORE_OFFER_TAG) }
8 .filter { !it.tags.contains(SharedConstants.RC_CUSTOMER_CENTER_TAG) }
9
10 return findLongestFreeTrial(validOffers) ?: findLowestNonFreeOffer(validOffers) ?: basePlan
11 }
このアルゴリズムでは、まず rc-ignore-offer または rc-customer-center のタグが付いたオファーを除外し、そのうえで最も長い無料トライアルを持つオファーを選択します。もし無料トライアルが存在しない場合は、最も低い導入価格のオファーが選択されます。これらの条件を満たすオファーが存在しない場合、プロモーションフェーズを持たないベースプランにフォールバックします。
このフォールバックこそが、サイレントに発生する失敗です。トライアルオファーに rc-ignore-offer のタグが付いている場合、正しいベースプランに紐づいていない場合、あるいはオファータグがまったく設定されておらずアプリ側がタグベースのフィルタリングに依存している場合、 defaultOffer はベースプランを返します。ペイウォール自体は正常に表示され、見た目も問題ありません。エラーも発生しません。しかし、トライアルは表示されていないのです。
ペイウォールが実際に何を表示しているかを確認する
他の最適化に着手する前に、まずプロダクトの defaultOffer が無料トライアル付きのオファーに解決されていることを確認してください。 SubscriptionOption インターフェースには、まさにこの確認のために freePhase プロパティが用意されています:
1val freePhase: PricingPhase?
2 get() = pricingPhases.dropLast(1).firstOrNull {
3 it.price.amountMicros == 0L
4 }
freePhase が null でない場合、そのオプションには無料トライアルのフェーズが含まれていることを意味します。同様に、introPhase は有料の導入フェーズの有無を確認します。もし defaultOffer の freePhase と introPhase の両方が null であれば、プロモーションフェーズは一切表示されません。また、 defaultOffer?.isBasePlan を直接確認することもできます。これが true の場合、SDKは有効なオファーを見つけられず、ベースプランにフォールバックしたことを意味します。いずれにしても、Play Console上のオファー設定を確認してください。
この確認は、offeringsを取得した後に コード上で行うことができます:
1Purchases.sharedInstance.getOfferingsWith(
2 onError = { error -> /* handle */ },
3 onSuccess = { offerings ->
4 val currentOffering = offerings.current ?: return@getOfferingsWith
5 val monthlyPackage = currentOffering.monthly ?: return@getOfferingsWith
6 val subscriptionOptions = monthlyPackage.product.subscriptionOptions
7
8 val defaultOffer = subscriptionOptions?.defaultOffer
9 val hasFreeTrialOption = defaultOffer?.freePhase != null
10 val hasIntroductoryOffer = defaultOffer?.introPhase != null
11
12 Log.d("Paywall", "Default offer: ${defaultOffer?.id}")
13 Log.d("Paywall", "Has free trial: $hasFreeTrialOption")
14 Log.d("Paywall", "Has intro offer: $hasIntroductoryOffer")
15 }
16)
開発中にこれを実行し、出力結果がPlay Consoleで設定したオファー構成と一致していることを確認してください。もし hasFreeTrialOption が false で、トライアルが表示される想定だった場合、そのオファーは選択されていません。オファータグを確認し、オファーが正しいベースプランに紐づいていることを検証し、さらにそのオファーがPlay Console上で有効になっていることを確認してください。
注意すべき構造的な違い
トラッキングや実験に進む前に、AndroidとiOSの比較では完全には捉えきれない、プラットフォームレベルの違いを1つ押さえておく価値があります。
iOSでは、トライアル終了前にAppleがシステムレベルのプッシュ通知を送信し、ユーザーに有料へ移行することを知らせます。一方で、Google Playにはこれに相当するシステム通知は存在しません。つまり、iOSではトライアルから課金への重要なタイミングで、再エンゲージメントの仕組みがプラットフォーム側に組み込まれているのに対し、Androidにはそれがありません。Androidでは、このリマインドは完全にアプリ側の責任となります。アプリ内バナー、自前のバックエンドからのプッシュ通知、あるいはトライアル終了間際にユーザーが戻ってきた際に発動する再エンゲージメントフローなどで対応する必要があります。
この構造的な違いは、トライアル開始ユーザー数が大きく異なるにもかかわらず、トライアルから課金へのコンバージョン率が似て見える理由の一部を説明しています。iOSではコンバージョンのタイミングでプラットフォームによる後押しがあるのに対し、Androidでは同じ結果を得るためには明示的な実装が必要です。もしAndroidのトライアルから課金へのコンバージョン率がiOSより低い場合、その要因の一つとして、トライアル終了リマインダーがアプリ内に存在しないことが考えられます。

