AIのあとしまつ

実務・技術解説

マイクロSaaSのStripe決済実装ガイド——「Stripe入れるだけ」では済まない理由

マイクロSaaSにStripe決済を組み込む手順と注意点。Checkout、Webhook、サブスク管理、失敗時のリトライまで、AIコードでは抜け落ちやすい実装ポイントを解説。

Stripe 決済 実装マイクロSaaS Stripe個人開発 決済SaaS 課金 実装Stripe Checkout 実装Stripe Webhookサブスクリプション 実装Next.js StripeStripe サブスク 実装個人開発 サブスクリプション

「Stripeを入れれば課金できるようになる」——これは正しい。ただし「Stripeを入れるだけ」で本番運用に耐える課金システムになるかというと、まったく別の話だ。

AIツールに「Stripe連携を追加して」と指示すると、基本的なCheckoutへの遷移コードは生成してくれる。でもそこから先——Webhookの処理、カード期限切れの対応、サブスク解約時のアクセス制御、本番環境とテスト環境の切り替え——は、AIが正しく生成してくれることを期待してはいけない。

この記事では、マイクロSaaSにStripe決済を組み込む際に必要な実装ポイントを、AIコードで特に抜け落ちやすい部分を中心に解説する。

決済実装の全体像

マイクロSaaSの決済フローは、以下の要素で構成される。

ユーザー → プラン選択 → Stripe Checkout → 支払い完了
                                              ↓
                               Stripe → Webhook → アプリ
                                              ↓
                               ユーザーのプラン情報を更新
                                              ↓
                               有料機能にアクセス可能に

見た目はシンプルだが、実装すべきポイントは意外と多い。

1. Stripe Checkoutを使う(自前のフォームは作らない)

カード番号を自前のフォームで受け取るのは絶対にやめる。PCI DSSの準拠が必要になり、個人開発者にはハードルが高すぎる。

Stripe Checkoutを使えば、決済フォームはStripe側がホストしてくれる。カード情報はあなたのサーバーを一切経由しない。

AI生成コードで注意すべき点

AIが生成するStripe Checkoutのコードでよくある問題:

  • テストキーがハードコードされている — APIキーは環境変数で管理する(クラウド破産を防ぐAPIキー管理術
  • success_urlとcancel_urlが固定 — 環境に応じて動的に変える必要がある
  • metadataが空 — Webhookで「どのユーザーの支払いか」を特定するために、user_idをmetadataに入れる

2. Webhookを正しく実装する

ここが決済実装の核心部分であり、AIコードで最も抜け落ちやすい部分だ。

Stripe Checkoutで支払いが完了しても、あなたのアプリは「支払いが完了した」ことを知らない。Stripeからの通知(Webhook)を受け取って、初めてアプリ側でユーザーのプランを更新できる。

最低限処理すべきイベント

イベント意味やるべきこと
checkout.session.completed支払い完了ユーザーのプランを有料に更新
invoice.payment_succeeded月次課金成功サブスク継続を確認
invoice.payment_failed月次課金失敗ユーザーに通知、猶予期間設定
customer.subscription.deletedサブスク解約ユーザーのプランを無料に戻す

Webhook実装の落とし穴

冪等性(べきとうせい)を担保する

Stripeは同じイベントを複数回送信することがある(ネットワークエラー時のリトライ)。同じイベントを2回処理すると、ユーザーに二重課金したりプランが二重に適用されたりする。

対策:Webhook受信時にevent.idを記録し、既に処理済みなら何もしない。

署名検証を行う

Webhookのエンドポイントは公開URLなので、誰でもリクエストを送れてしまう。Stripeの署名ヘッダー(stripe-signature)を検証して、Stripeからの正規のリクエストであることを確認する。

AIが生成するコードでは、この署名検証が省略されていることが非常に多い。省略すると、偽のWebhookを送りつけることでユーザーを不正に有料プランにすることが可能になる。

レスポンスを200で返す

Webhook処理中にエラーが発生しても、StripeにはHTTP 200を返す。500を返すとStripeがリトライを繰り返し、同じイベントが何度も処理される可能性がある。エラーは内部でログに記録して別途対応する。

3. サブスクリプション管理

マイクロSaaSで最も一般的なのはサブスク(月額課金)モデルだ。

→ 各課金モデルの比較は収益化モデル5選

実装が必要な機能

プラン変更(アップグレード/ダウングレード)

ユーザーがプランを変更したとき、日割り計算をどうするか。Stripeのproration設定で制御できるが、デフォルト設定のままだと意図しない挙動になることがある。

解約フロー

即座に解約するか、現在の請求期間の終わりに解約するか。多くのSaaSは「期間終了時に解約」を採用している(cancel_at_period_end)。

カード期限切れ・支払い失敗

カードの有効期限が切れた場合、自動的にサブスクが停止する。ユーザーにカード更新を促すメールを送る仕組みが必要。StripeのSmart Retriesと組み合わせて、自動リトライ+通知メールの設計をする。

4. 有料/無料ユーザーの出し分け

決済を入れたら、アプリ側で「有料ユーザーかどうか」を判定する仕組みが必要になる。

データ設計

Supabaseを使っている場合、最もシンプルな方法はユーザーテーブルにplanカラムを追加すること。

users テーブル
├── id (UUID)
├── email
├── plan ('free' | 'pro' | 'enterprise')
├── stripe_customer_id
└── subscription_status ('active' | 'past_due' | 'canceled')

Webhookでサブスクの状態が変わるたびに、このテーブルを更新する。

アクセス制御

有料機能へのアクセス制御は、フロントエンド(UI表示)とバックエンド(API/RLS)の両方で行う。

フロントエンドだけでの制御は「UIを隠しているだけ」であり、直接APIを叩けば有料機能にアクセスできてしまう。必ずSupabase RLSまたはRBACでバックエンド側でも制御する。

5. テスト環境と本番環境の切り分け

Stripeにはテストモードと本番モードがある。開発中はテストモードのAPIキーを使い、本番ではライブモードのAPIキーに切り替える。

よくある事故

  • テストキーのまま本番にデプロイ(課金できない)
  • 本番キーで開発環境をテスト(実際に課金が発生する)
  • Webhookのエンドポイントがテスト環境を指したまま(本番の決済通知がテスト環境に行く)

環境変数でSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETを環境ごとに切り替える。Vercelならプレビュー環境とプロダクション環境で別々の値を設定できる。

環境変数の管理方法

6. 法的対応

日本でサブスクリプションサービスを提供する場合、特定商取引法に基づく表記が必要。最低限以下を記載する。

  • 事業者名
  • 連絡先
  • 料金と支払い方法
  • 解約方法
  • 返金ポリシー

これはコードの問題ではないが、ページとして用意しないと法的リスクがある。

まとめ:決済は「入れるだけ」では動かない

実装ポイントAIが生成するか自分で実装が必要か
Checkout遷移△(キー管理、metadata)
Webhook受信△(基本形のみ)◎(署名検証、冪等性)
サブスク管理×
プラン出し分け×
環境切り替え×
法的対応×

決済周りのバグは直接的に金銭トラブルに繋がる。プロに任せるべきタイミングの見極めが特に重要な領域だ。