はじめに
こんにちは、ひるげです。
iOSアプリに課金機能を追加したいけど、どういう流れで実装していけばいいかわからない...そういったこと、ありませんか?
AIにその場限りの指針を提示してもらってよくわからないまま実装することもできるのだけれど、それでは不安だし、もう少し責任を持って理解した上で進めていきたいですよね。
そこでこの記事では、RevenueCatを使ったiOSサブスクリプション実装の流れをロードマップ形式でまとめます。
個人開発アプリ「Roamble」への課金機能実装で実際に歩んできた道のりをもとに、詰まりやすいポイントも交えながら説明します。
iOSアプリの開発環境構築からApp Store公開までの基礎については、以下の記事を参照してください。
本記事はこの続きとして、課金機能の実装に特化した内容とします。
結論
大きな流れはこうなります。
- App Store ConnectとRevenueCatの事前設定
- StoreKitでのローカルテストとPaywall実装
- Sandboxテスト
- 審査提出
事前知識:RevenueCatとは
RevenueCatは、In-App Purchase(アプリ内課金)の実装を大幅に簡略化するSDKです。Appleの課金APIを直接叩く代わりに、RevenueCatがその複雑さを吸収してくれます。購入・更新・復元といった処理の詳細を自前で書く必要がなくなるうえ、転換率の計測やPaywall A/Bテストのための諸々も提供してくれます。
Webアプリでの課金機能を使ったことがある方は「Stripe経由で課金してもらえばいいのでは?」と思ったかもしれません。しかし残念ながらそうはいかず、App Store規約でデジタルコンテンツへのStripe直払いは禁止されています。
そのため、iOSアプリの課金にはIn-App Purchaseが必須であり、RevenueCatはそのラッパーとして、実質的にデファクトスタンダードになっています。
ではこの前提を踏まえて、実際のサブスク実装ロードマップを見ていきましょう。
1. App Store ConnectとRevenueCatの事前設定
この段階ではコードを一切書きません。
ブラウザ経由でApp Store ConnectとRevenueCatの設定をやっていきましょう。
まずApp Store Connectの「機能 > サブスクリプション」からサブスクリプショングループを作り、月額や年額のプランを登録します。
価格ティアと無料トライアル期間もここで設定します。Product IDは後から変えにくいので、命名は最初に丁寧に決めておきましょう。
続いてRevenueCatのダッシュボードでプロジェクトを作成し、App Store Connectと接続します。次に以下の3つを定義します。
- Entitlement:ユーザーが持つ権限の名前(例:
premium) - Offering:Paywallに表示する商品セット
- Package:Offering内の各商品(
$rc_monthly・$rc_annualなど)
「実際にブラウザのどこから操作するの?」という説明はここでは省きます。
単純に書くのが手間なのと、今後UIが変わっていく可能性もあるので、ここは調べたりAIに聞いたりしながら進めてください。
2. StoreKitでのローカルテストとPaywall実装
SDKを初期化してユーザーを紐付ける
まず以下のコマンドでSDKをインストールします。
npx expo install react-native-purchases
インストールできたら、実際にRevenueCatを使った処理を書いていきましょう。
基本はPurchasesに紐づく処理を呼んでいくだけで課金機能を実装できます。
まずはユーザーとの紐付けです。
課金リクエストが送られてきても、誰が送ってきたのか分からなければ意味がないですからね。
まず、アプリ起動時に Purchases.configure({ apiKey }) を呼びます。これは単純に初期化のようなものだと思ってください。
そしてユーザーがサービスにログインしたタイミングで Purchases.logIn(String(userId)) を用いて自サービスのユーザーIDを紐付け、ログアウト時は Purchases.logOut() を呼びます。RevenueCatでは、大抵の場合、自サービスのユーザーIDをRevenueCat側に送信する形で紐付けを行います。
Stripeとの違い(読み飛ばして構いません)
Stripeでユーザーを紐付ける場合は、Stripe側が発行した customer_id を受け取って自サービスのDBに保存する形です。「外部サービスが作ったIDを自分が保存する」流れです。
RevenueCatは逆で、自サービスのユーザーIDをRevenueCat側に送信する形になります。「自分が持っているIDを外部サービスに渡す」流れです。
ここの違いを把握しておくと、先の記述がわかりやすくなるかもしれません。
これで紐付けは完了です。この紐付けを怠ると、課金情報とユーザー情報が紐づけられず、「課金したのに課金特典が得られない!」的なことが起こります。
決してあってはならないことなので、ログインフローの中で必ず Purchases.logIn を呼ぶようにしましょう。
StoreKitローカルテストを先に設定する
Paywallの実装に入る前に、StoreKitのローカルテスト設定を先に済ませておくことを強くおすすめします。
Xcodeで File > New > File from Template... > StoreKit Configuration File を選び、.storekit ファイルを作成します。Edit Scheme > Run > Options > StoreKit Configuration でそのファイルを選択すると、AppleサーバーにもRevenueCatにも接続せずに購入UIをローカルで動かせるようになります。
後述のSandboxテストはRevenueCat経由でWebhookが飛ぶため、すでに本番環境のバックエンドが稼働している場合は本番DBを書き換えてしまう危険があります。
StoreKitローカルテストはAppleにもRevenueCatにも一切繋がらないので、既存の本番環境に影響を与えず安全に購入フローを試せます。まずここで小さく確認しておきましょう。
Paywallを実装する
ローカルテストの準備ができたらPaywallを実装します。
Purchases.getOfferings() で現在のOfferingを取得してUIを組みます。
購入は Purchases.purchasePackage(pkg)、復元は Purchases.restorePurchases() です。
復元ボタンはApp Storeガイドラインで設置が義務付けられているので省略できません。必ず設置するようにしてください。
なお、ユーザーが購入をキャンセルした場合はエラーとして扱わず、userCancelled: true として正常系で処理しましょう。
復元(Restore Purchases)とは
端末を変えたり、アプリを再インストールしたりしたあとに、過去の購入状態を再度有効化する機能です。App Storeに保存されているレシート情報を再確認することで、購入済みのサブスクリプションを引き継げます。
要は、購入情報を再取得してきて反映させる処理になります。
ただし「復元」という言葉はユーザーに伝わりにくい面があります。
「何を復元するの?」「なんか大ごとになりかねない処理なのか?」と思われてしまいかねないと思ったため、Roambleでは「購入を復元(購入情報を再読み込み)」という表記にしました。
もし迷ったら、こういった補足をつけておくこともオススメします。
Premium状態を確認・管理する
const isActive =
customerInfo.entitlements.active["premium"]?.isActive === true;
getCustomerInfo() を通じて顧客情報を取得し、取得結果の entitlements.active に対象のEntitlement IDが存在し isActive === true であればサブスクが有効である証です。
all ではなく active を参照するのがポイントで、all には期限切れのものも含まれます。boolean だけで管理するより、"loading" | "unknown" | "free" | "premium" の4値にしておくと、「取得中」と「Freeと確定した」をUIが区別できて便利です。取得失敗時は "free" に倒すのが安全側の設計です。
3. Sandboxテスト
In-App Purchaseを含むビルドをASCへアップロードする
Sandboxテストを始めるには、まずIn-App Purchaseを含むビルドをASCへアップロードする必要があります。
App Store Reviewへの提出や承認は不要で、必要なのはビルドをApp Store Connectへアップロードし、Appleが商品IDとビルドを紐付ける処理を完了させることだけです。
アップロード後にASCで処理が完了すると、Sandboxで購入操作ができるようになります。
なおサブスクリプション商品がDraft状態のままではRevenueCatが認識できずエラーになります。商品のメタデータ(価格・期間・説明文など)を埋めてReady to Submit状態にしておきましょう。
もし、いつまで経っても「メタデータが不足」状態になってしまう場合、サブスクリプションページの「ローカリゼーション」が埋まっているかも確認しましょう。
ここは個別サブスク内の設定情報ではないので見落とされがちですが、この項目が埋まっていない場合にも「メタデータが不足」状態になります。
私はここでかなり詰まりました...
WebhookでバックエンドのDB更新を実装する
Sandboxテストの前に、Webhookを先に実装してデプロイしておきます。
Sandboxテスト時にWebhookを通すことで、購入イベント → RevenueCat → バックエンドDB反映までの流れをE2Eで確認できるからです。
Webhookの実装はRevenueCatのダッシュボードでエンドポイントを登録するところから始まります。購入・更新・解約・期限切れのタイミングでPOSTが飛んできます。バックエンドでPremium状態をDBで管理することで、クライアントの申告に依存しないサーバー側判定ができます。
DB側には、is_premiumやpremium_expires_at的なカラムを用意しておきましょう。
イベント種別ごとの処理方針は以下のとおりです。
| イベント | 意味 | 処理 |
|---|---|---|
INITIAL_PURCHASE |
初回購入 | Premium有効・有効期限を保存 |
RENEWAL |
自動更新 | Premium有効・有効期限を延長 |
CANCELLATION |
解約申請 | 何もしない(期限内はまだ有効) |
EXPIRATION |
期限切れ確定 | Premium無効化 |
CANCELLATION で即時無効化してはいけません。「解約申請した」と「アクセス権を失った」は別タイミングで、失効は EXPIRATION が来てから処理します。
なお、Sandboxテストで飛んでくるWebhookのペイロードには event.environment == "SANDBOX" が入っています。
本番DBを汚染したくない場合は、ここを判定基準としてSANDBOX経由のイベントを無視し、200を返すフィルタを先に入れておく選択肢もあります。
ただそうなるとバックエンド含む動作確認はやりにくくなるので、そこも踏まえてこのフィルタを取り入れるべきかどうかを判断してください。
これが済んだら、フロントエンド側に"サブスク状況を確認できるUI"を作っておくことをオススメします。
ユーザーに使ってもらうことを考えればどのみち必要になりますし、次の動作確認時にこれがないと不便ですからね。
では、ここまでできたら次にいきましょう。
Sandboxで実機テストする
Webhook処理のデプロイが済んだら実機でSandboxテストを行います。TestFlight経由でインストールしたビルドは、Appleが自動的にSandboxバックエンドで処理するため、別途Sandboxテスターアカウントを作成しなくても、通常のApple IDのままIAPをテストできます。 実際の課金は発生せず、購入・更新・復元フローを確認できます。
ただし、TestFlight経由の購入は購入履歴のリセットができません。Sandboxテスターアカウントであればデバイス設定から購入履歴をクリアできるため、購入フローのリセットを繰り返してテストしたい場合はSandboxテスターアカウントを用意しておく方が便利です。Sandboxテスターアカウントは、ASCの「ユーザーとアクセス > Sandbox」から作成でき、ASCにログインしているApple IDとは別のメールアドレスである必要があります。
RevenueCatのダッシュボードで顧客情報を確認してEntitlementが正しく付与されており、バックエンドDBにも反映されていれば動作確認完了です。
4. プレミアム機能実装・審査提出
Sandboxで購入フローが動くことを確認したら、プレミアム機能を実装しましょう。
大抵の場合、プレミアム機能を実装して、その前段に「プレミアム権限判定」的な処理をバックエンドと絡めつつ行うだけで良いはずです。
ここに関しては、どういったアプリなのかによるところが大きいので記述を省きます。
この部分の実装もでき、再びSandbox経由で動作確認ができたら審査提出を行いましょう。
もしリジェクトされたら、そこを指摘して直していき、審査が通ったら無事課金機能の実装は完了です!
おわりに
以上がiOS課金機能実装のロードマップです。
実際私はこんなに綺麗な流れで進められたわけではなく、もっとごちゃごちゃといろんなところで詰まりながら課金機能の実装にこぎつけました。
しかし振り返ると、詰まりポイントの多くは「順序を知らなかった」ことに起因しています。
どんな順序で進めればいいかがわかっていれば、個々のステップ自体はそれほど難しくありません。RevenueCatがAppleの課金APIの複雑さを大部分吸収してくれているおかげで、サブスクリプションの実装はかなり楽になっています。
この記事が、課金機能の実装に取り組む方の地図として少しでも役に立てば嬉しいです。
お知らせ
現在私は、RoambleというiOSアプリの公開を目前に控えています。
「気になる店があるのに一人で入る勇気が出ない...」、「新しいお店を開拓したいのに、結局いつものチェーン店に流れてしまう...」
Roambleは、そんな悩みを抱える方に向けて作った新しいお店開拓アプリです。
この記事で書いたRevenueCatの実装は、まさにRoamble iOS版で踏んできた道のりそのものです。
6/19(金)、まさに明日App Storeで公開予定ですので、ぜひ、事前登録・インストールをよろしくお願いします!
2026-06-19追記:Roambleが公開されました
上述の「新しいお店開拓アプリRoamble」が公開されました!
以下のリンクからインストールできますので、ぜひ週末のお出かけの際にお試しください!


