アプリをリリースして最初にぶつかる壁のひとつが、「ペイウォールを少し変えたいだけなのに、また App Store 審査に出さないといけない」という現実です。
ボタンのコピーを「今すぐ試す」から「7日間無料で試す」に変えたい、価格の表示順序を変えたい、無料トライアルの訴求を強くしたい——こういった小さな改善のたびに1〜3日の審査待ちが発生するのは、個人開発者にとって本当に痛いコストです。しかも審査結果によっては、その変更がリジェクトされることもあります。
RevenueCat Paywalls SDK を使うと、この問題が根本から解消されます。ペイウォールのデザイン・コピー・価格・オファーをすべてダッシュボードから変更でき、アプリ審査なしでユーザーに即座に反映できます。さらに組み込みのA/Bテスト機能により、どのペイウォールが最も課金に繋がるかをデータで判断できます。
Rork Max で作ったアプリにこの仕組みを実装する手順を、実際のコード例と落とし穴とともに徹底的に解説します。
なぜ RevenueCat Paywalls SDK が必要なのか
従来のペイウォール実装では、表示するコンテンツはすべてアプリのコードに埋め込まれています。「¥580/月」という文字を変更するだけでも、コード修正 → ビルド → TestFlight → App Store 審査というフローが必要でした。
RevenueCat の調査によると、サブスクリプションアプリのコンバージョン率の中央値は約2〜3%程度とされています。この数字を5%に上げるだけで、売上は約2倍になります。そのためにはペイウォールを継続的に改善し続けることが不可欠なのですが、毎回の審査がボトルネックになって改善速度が上がらない——これが多くの個人開発者が直面している現実です。
RevenueCat Paywalls SDK は、この問題を「ペイウォールをリモートで定義する」というアプローチで解決します。アプリはダッシュボードからペイウォールの設定を取得して表示するだけなので、デザインやコピーの変更はアプリ側のコード変更を一切必要とせず、ほぼリアルタイムで反映されます。
個人的な経験から言うと、この仕組みを導入してから「今週はペイウォールのコピーを3パターン試してみよう」というアジャイルな改善が現実的になりました。審査を気にせずにデータを取れる環境は、思った以上に開発者のメンタルを楽にしてくれます。
RevenueCat Paywalls SDK の全体像
実装に入る前に、Paywalls SDK が何を解決して何を解決しないかを整理しておきます。
できること
RevenueCat ダッシュボード上でペイウォールのUIをビジュアルエディタで作成し、アプリ審査なしでリモート更新できます。複数のペイウォールバリアントを定義してA/Bテストを実行し、どちらが高いコンバージョン率を達成するかをダッシュボード上で確認できます。表示するオファリング(プラン)の切り替えもリモートで行えます。
できないこと
RevenueCat Paywalls SDK が提供するUIコンポーネントは、テンプレートベースの制約があります。完全にカスタムなアニメーションや、ブランドの世界観に合った独特のUIを実現したい場合は、後述する「カスタムペイウォール」の実装が必要になります。ただしその場合もA/Bテスト機能は利用できます。
Paywalls SDK は RevenueCat の In-App Purchase / Subscription 管理機能(旧来の SDK)と組み合わせて使います。RevenueCat 自体の導入が初めての方は、まず基本的な課金フローを実装してから、この記事の内容に進むことをおすすめします。
Rork Max での初期セットアップ
SDK のインストール
Rork Max プロジェクトのターミナルで、以下のコマンドを実行します。
# RevenueCat の基本 SDK(未導入の場合)
npx expo install react-native-purchases
# Paywalls SDK(今回のメイン)
npx expo install react-native-purchases-ui
# 型定義
npx expo install @revenuecat/purchases-typescript-internal
Rork Max で生成したプロジェクトは通常 Expo Managed Workflow ですが、RevenueCat は Expo を公式サポートしているので追加設定は最小限で済みます。
app.json の設定
{
"expo" : {
"plugins" : [
[
"react-native-purchases" ,
{
"apiKey" : {
"ios" : "appl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ,
"android" : "goog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
]
]
}
}
APIキーは RevenueCat ダッシュボードの「API Keys」セクションで取得できます。iOS と Android でキーが分かれていることに注意してください。
Purchases の初期化
アプリのエントリポイント(app/_layout.tsx など)で SDK を初期化します。
import Purchases, { LOG_LEVEL } from 'react-native-purchases' ;
import { useEffect } from 'react' ;
export default function RootLayout () {
useEffect (() => {
// 開発中はログを詳細に出力
if (__DEV__) {
Purchases. setLogLevel ( LOG_LEVEL . DEBUG );
}
Purchases. configure ({
apiKey: process.env. EXPO_PUBLIC_REVENUECAT_API_KEY ?? '' ,
});
// ユーザーIDが存在する場合は連携
// 匿名ユーザーでも動作するため、認証前でも初期化できる
}, []);
return < YourNavigationRoot />;
}
環境変数 EXPO_PUBLIC_REVENUECAT_API_KEY は .env ファイルと Cloudflare / EAS の環境変数に設定します。APIキーをコードに直書きすると GitHub にコミットした際に流出する可能性があるため、必ず環境変数経由で渡してください(この点は特にチームで開発している場合に重要です)。
ダッシュボードでペイウォールを作成する
コードを書く前に、RevenueCat ダッシュボード側でペイウォールを設定します。
Offerings の設定
「Monetization > Offerings」から新しいオファリングを作成します。オファリングとは「ユーザーに提示する課金プランのセット」です。たとえば:
monthly_plan : 月額プランのみ(シンプルな訴求向き)
annual_and_monthly : 月額 + 年額(割引訴求向き)
trial_focused : 無料トライアル付きプランを前面に出す
A/B テストのために複数のオファリングを作成できます。たとえば「割引率を強調するパターン」と「機能を強調するパターン」を別々のオファリングとして定義し、どちらが高いコンバージョン率を達成するかを測定します。
Paywall のビジュアルエディタ
オファリングに「Paywall」を追加すると、ビジュアルエディタが開きます。テンプレートが複数用意されており、以下の要素をGUI上でカスタマイズできます。
ヒーロー画像・アイコン
見出し・サブタイトルのコピー
各プランの表示形式(カード型・リスト型など)
CTA ボタンのラベルと色
フィーチャーリスト(「✓ 広告なし」「✓ 月100件まで利用可能」など)
日本語と英語でコピーを分けたい場合は、Localization 設定から言語ごとのコピーを設定できます。これも審査不要で変更できるのは大きなメリットです。
Rork Max アプリにペイウォールを表示する
シンプルなモーダル表示
最もシンプルな実装は、RevenueCatUI が提供するコンポーネントを使う方法です。
import { useState } from 'react' ;
import { View, Button } from 'react-native' ;
import RevenueCatUI, { PAYWALL_RESULT } from 'react-native-purchases-ui' ;
export function PremiumUpgradeScreen () {
const [ isLoading , setIsLoading ] = useState ( false );
const handleShowPaywall = async () => {
setIsLoading ( true );
try {
const result = await RevenueCatUI. presentPaywall ();
switch (result) {
case PAYWALL_RESULT . PURCHASED :
// 購入完了 — エンタイトルメントを更新して画面を閉じる
console. log ( '購入完了' );
break ;
case PAYWALL_RESULT . RESTORED :
// 購入復元完了
console. log ( '購入復元完了' );
break ;
case PAYWALL_RESULT . NOT_PRESENTED :
// オファリングが設定されていないなど、表示できなかった
console. warn ( 'ペイウォールを表示できませんでした' );
break ;
case PAYWALL_RESULT . CANCELLED :
// ユーザーがキャンセル
break ;
case PAYWALL_RESULT . ERROR :
// エラー発生
console. error ( 'ペイウォール表示エラー' );
break ;
}
} catch (error) {
console. error ( '予期しないエラー:' , error);
} finally {
setIsLoading ( false );
}
};
return (
< View >
< Button
title = "プレミアムにアップグレード"
onPress = {handleShowPaywall}
disabled = {isLoading}
/>
</ View >
);
}
presentPaywall() を呼ぶだけで、ダッシュボードで設定したペイウォールが表示されます。どのオファリングを表示するかもダッシュボードで設定した「Default Offering」が自動的に使われます。
特定のオファリングを指定して表示する
A/B テスト時には、特定のオファリングを明示的に指定することもあります。
import Purchases from 'react-native-purchases' ;
import RevenueCatUI from 'react-native-purchases-ui' ;
const showTargetedPaywall = async ( offeringIdentifier : string ) => {
try {
const offerings = await Purchases. getOfferings ();
const targetOffering = offerings.all[offeringIdentifier];
if ( ! targetOffering) {
// 指定されたオファリングが見つからない場合は current を表示
await RevenueCatUI. presentPaywall ();
return ;
}
const result = await RevenueCatUI. presentPaywallIfNeeded ({
offering: targetOffering,
requiredEntitlementIdentifier: 'premium' ,
});
return result;
} catch (error) {
// エラー時はサイレントに処理(ペイウォールを表示しないだけ)
console. error ( 'ペイウォール取得エラー:' , error);
}
};
presentPaywallIfNeeded() は、ユーザーがすでに premium エンタイトルメントを持っている場合はペイウォールを表示しない、というスマートな挙動をします。無条件で呼び出しても既存ユーザーに課金ダイアログが出ないので、アプリ起動時のチェックにも使えます。
エンタイトルメント確認フック
課金状態をリアクティブに管理するカスタムフックを作っておくと、アプリ全体で使いやすくなります。
import { useState, useEffect, useCallback } from 'react' ;
import Purchases, { CustomerInfo } from 'react-native-purchases' ;
type EntitlementStatus = 'loading' | 'active' | 'inactive' | 'error' ;
export function usePremiumStatus () {
const [ status , setStatus ] = useState < EntitlementStatus >( 'loading' );
const [ customerInfo , setCustomerInfo ] = useState < CustomerInfo | null >( null );
const checkStatus = useCallback ( async () => {
try {
const info = await Purchases. getCustomerInfo ();
setCustomerInfo (info);
const isPremium = info.entitlements.active[ 'premium' ] !== undefined ;
setStatus (isPremium ? 'active' : 'inactive' );
} catch (error) {
console. error ( 'エンタイトルメント確認エラー:' , error);
// エラー時は inactive として扱う(deny by default)
setStatus ( 'inactive' );
}
}, []);
useEffect (() => {
checkStatus ();
// CustomerInfo の更新をリッスン
const subscription = Purchases. addCustomerInfoUpdateListener (( info ) => {
setCustomerInfo (info);
const isPremium = info.entitlements.active[ 'premium' ] !== undefined ;
setStatus (isPremium ? 'active' : 'inactive' );
});
return () => subscription. remove ();
}, [checkStatus]);
return { status, customerInfo, refetch: checkStatus };
}
このフックを使えば、課金完了の瞬間に自動的にステータスが更新されてUIが切り替わります。addCustomerInfoUpdateListener が変更をプッシュしてくれるため、ポーリングは不要です。
A/B テストの設定と分析
Experiments の設定
RevenueCat ダッシュボードの「Experiments」セクションから新しいExperiment(A/B テスト)を作成します。設定するのは以下の3点です。
コントロール群(A) : 現在のオファリング
テスト群(B) : 検証したいオファリング
トラフィック配分 : 50/50 がもっとも統計的に扱いやすい
テスト期間は最低2週間、できれば1ヶ月以上確保することをおすすめします。ユーザー数が少ない段階で判断すると誤った結論を出しやすいためです。
測定すべき指標
Experiments の分析画面では以下の指標が確認できます。
Conversion Rate : ペイウォールを見たユーザーのうち何%が課金したか
Monthly Recurring Revenue (MRR) : 実際の収益への貢献
Initial Conversion : 初回表示での課金率(オファリングの訴求力の純粋な評価)
Retention : 課金後の継続率
コンバージョン率だけを見ると「安い月額プランを前面に出したBの方が高い」という結果が出ることがありますが、MRRを見ると「安いプランへの誘導でむしろ収益が下がった」というケースもあります。意思決定には複数の指標を総合的に見てください。
テスト結果の解釈
実際に数十のアプリを見てきた経験から言うと、以下のパターンがコンバージョン率を改善しやすいです。
年額プランを目立たせる配置 : 月額の上に年額を大きく表示し、「○○% オフ」のバッジを付ける。最初のA/Bテストとして試す価値が高い定番パターンです。
無料トライアルの期間強調 : 「¥580/月」よりも「7日間無料で始める」というCTAの方が高いコンバージョン率を示すことが多いです。コストへの心理的抵抗を「まず体験してもらう」形で下げるわけです。
フィーチャーリストの順序 : 「広告なし」「オフライン利用可」「バックアップ機能」のうち、最もユーザーが価値を感じるものを最上位に持ってくる。これも地味に効きます。
よくある実装ミスと対処法
ミス1: offering の設定漏れによる空白表示
presentPaywall() を呼んでも何も表示されない、または NOT_PRESENTED が返ってくる場合の多くは、RevenueCat ダッシュボードで Default Offering が設定されていないか、オファリングにパッケージが含まれていないことが原因です。
// デバッグ用: 利用可能なオファリングをコンソールに出力
const debugOfferings = async () => {
const offerings = await Purchases. getOfferings ();
console. log ( 'Current offering:' , offerings.current?.identifier);
console. log ( 'All offerings:' , Object. keys (offerings.all));
console. log ( 'Packages in current:' , offerings.current?.availablePackages. map ( p => p.identifier));
};
このデバッグコードで確認してから、ダッシュボード側を修正するのが最速です。
ミス2: Sandbox と本番の混在
Simulator での課金テストは必ず Sandbox 環境で行われますが、RevenueCat ダッシュボードのデフォルトビューは本番データです。「テストしているのに Experiments の結果が反映されない」という問題の多くは、ダッシュボードの右上にある「Sandbox」「Production」の切り替えを見落としていることが原因です。
開発中は Sandbox データで確認し、本番リリース後は Production データで判断する、という明確な切り替えの習慣をつける点が肝心です。
ミス3: エンタイトルメント確認を Pessimistic に行わない
購入完了直後に getCustomerInfo() を呼ぶと、RevenueCat サーバーへのリクエストが必要なため若干の遅延が生じます。この間、UIが更新されないと「課金したのに変化がない」という UX になります。
対処法は addCustomerInfoUpdateListener を使う(前述のフックに含まれています)か、購入完了の PAYWALL_RESULT.PURCHASED を受け取った時点でオプティミスティックに UI を更新することです。
case PAYWALL_RESULT . PURCHASED :
// オプティミスティック更新(サーバー確認前に UI を先に変える)
setIsPremiumOptimistic ( true );
// バックグラウンドで実際のエンタイトルメントを確認
refetchEntitlement ();
break ;
ミス4: iOS と Android で異なるオファリング識別子
StoreKit(iOS)と Google Play Billing(Android)では商品IDの命名規則が異なります。RevenueCat ではこれをオファリングで抽象化できますが、ダッシュボードで iOS/Android 両方のパッケージをオファリングに紐付けていないと、どちらかのプラットフォームでペイウォールが空になります。
特に Android に後から対応する場合、iOS 用のパッケージだけが設定されたままのオファリングが残りやすいので注意してください。
ミス5: A/B テスト中のユーザー体験の一貫性
同じユーザーが「A のペイウォールを見た後に B のペイウォールを見る」という状況は、RevenueCat の Experiments が自動的に防いでくれます。一度テスト群に割り当てられたユーザーは、Experiment が終了するまで同じバリアントを見続けます。
ただし、アプリのアンインストール → 再インストール後は別のユーザーとして扱われる可能性があります。厳密な統計が必要な場合は、ログイン済みユーザーのみを対象にした Experiment の設定を検討してください。
コンバージョン率を上げるペイウォールのデザイン原則
「損失回避」よりも「得られるもの」の提示
「今すぐ登録しないと〜ができません」という損失フレーミングよりも、「登録すると〜ができます」という利得フレーミングの方が、特に日本のユーザーには受け入れられやすい傾向があります。
具体的には:
❌「広告が表示されます」→ ✅「広告なしで快適に使えます」
❌「高度な機能が使えません」→ ✅「全機能をフルに活用できます」
具体的な数字と期間の明示
「月額プレミアム」という表記より「月額580円(コーヒー1杯分)」、「年間プラン」より「年額4,800円(月々400円相当、31% オフ)」のように、金額の感覚を具体的に伝える点が肝心です。
フィーチャーリストも「高度な分析機能」という抽象的な表現より「月間最大500件のデータを分析できます」という具体的な数字が説得力を持ちます。
無料トライアルの位置づけ
無料トライアル期間があるなら、それはCTAの最前面に出すべきです。「7日間無料、いつでもキャンセル可」という一文は、課金への心理的ハードルを大幅に下げます。特にアプリの価値をまだ体験していないユーザーにとって、「先にリスクなく試せる」という保証は購入判断の大きな後押しになります。
ただし、無料トライアル後の自動更新について明示することは、App Store のガイドラインで求められています。「7日後から月額580円」と明記することを忘れないようにしましょう。
モバイル画面での視線の導線
ペイウォールは縦スクロールで見られます。ファーストビュー(スクロールなしで見える範囲)に「この値段でこれだけの価値がある」という情報を凝縮する点が肝心です。スクロールを必要とするフィーチャーリストの末尾は、多くのユーザーには見られません。
RevenueCat のビジュアルエディタでは、要素の表示順序を自由に変更できます。A/B テストで「CTAを上に持ってくるバリアント」vs「フィーチャーリストを先に見せるバリアント」を試してみると、意外な発見があるかもしれません。
プロダクション対応チェックリスト
RevenueCat Paywalls SDK の導入が完了したら、本番環境に出す前に以下を確認してください。
ダッシュボード側
Default Offering が設定されており、iOS/Android 両方のパッケージが含まれている
Paywall に日本語のコピーが設定されている(Localization 設定)
Experiments が意図した配分で設定されている(開始前のプレビューで確認)
アプリ側
Sandbox テストで課金 → エンタイトルメント付与 → UI 更新が正常に動いている
購入復元(Restore Purchases)が実装されており、正常に動作する
エラー時のフォールバック処理が実装されている(ペイウォールが表示できない場合でもクラッシュしない)
プロモーションオファー(割引コード)が利用できるようになっている場合、presentPaywall(offering:) にオファーを含めた呼び出しになっている
リジェクト対策
ペイウォール上に「サブスクリプションは自動更新されます」の一文があるか
App Store のサブスクリプションガイドラインに沿ったコピーになっているか
「プライバシーポリシー」「利用規約」へのリンクがペイウォール内からアクセスできるか
これらを確認してから本番リリースを出すと、審査でのリジェクトリスクが大きく下がります。
全体を振り返ってにかえて — 改善サイクルを回し続けることの価値
RevenueCat Paywalls SDK の最大の価値は、ペイウォールの改善を「いつでも気軽に試せるもの」に変えることです。
以前は「また審査か...」という心理的なハードルがあって後回しにしていた改善が、「今週試してみよう」と思える規模感になります。週次でExperimentsを確認し、勝ったバリアントを Default に昇格させ、次のテストを設計する——このサイクルを3〜6ヶ月続けると、導入前と比べてコンバージョン率が1.5〜2倍になることも珍しくありません。
まず最初のA/Bテストとして「年額プランの表示を目立たせるバリアント」を試してみることをおすすめします。設定に必要なのは30分程度で、データを取り始めるのに審査は要りません。試してみてください。