【Stripe】Next.js 15とPrismaで実装するサブスクリプション機能の開発
Next.js 15
このcheckout.session.completed など)をlayout.tsx(サーバーコンポーネント)でstripe listen --forward-to で
1. はじめに
現在開発中の
2. 実装要件とデータベース設計
今回の
システム要件
- プランの
多段階化 : ライト(980円)と スタンダード (2,980円)の 提供。 - 即時利用制限: 決済完了まで
機能への アクセスを 遮断。 - Webhookに
よる : 決済成功・解約時の自動更新 ステータス同期。 - セルフプラン変更: 管理画面からの
アップグレード対応。
Prismaによるスキーマ設計
ユーザー
// schema.prisma
model Restaurant {
id String @id @default(cuid())
// ...基本情報
// セットアップ状況(決済完了でtrue)
isSetupCompleted Boolean @default(false)
// Stripe連携用フィールド
stripeCustomerId String? @unique
subscriptionId String? @unique
subscriptionStatus String? @default("inactive")
subscriptionPlanId String?
}3. 実装のハイライト
3.1 「仮登録」と「決済」を分離するUX
ユーザー登録と
- 新規登録後:
自動的に /admin/subscriptionへリダイレクト。 - Stripe Checkout: サーバーサイドで
支払いリンクを 生成し、 Stripeの 堅牢な 決済画面へ 遷移。
3.2 Webhookによるステータス同期(最重要)
クライアント側の
api/webhooks/stripe/route.ts の
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
await prisma.restaurant.update({
where: { id: restaurantId },
data: {
stripeCustomerId: session.customer as string,
subscriptionId: session.subscription as string,
subscriptionStatus: "active",
isSetupCompleted: true
}
});
}3.3 未払いユーザーの徹底的なアクセス制御
Next.jsのlayout.tsx(サーバーコンポーネント)を
4. 開発・デバッグ:Stripe CLIの活用
ローカル環境では
# ローカルへのWebhook転送コマンド
stripe listen --forward-to localhost:3000/api/webhooks/stripeこのwhsec_...)を
株式会社bundlyzeでは、
5. まとめ
Next.js 15 と
- 確実な
収益化 : 決済なしでの利用を システムレベルで 排除。 - 運用の
自動化 : 入金確認作業のゼロ化。 - スケール性: 容易な
プラン追加や 変更が 可能な 柔軟な 設計。
SaaS開発に
よくある質問
決済が完了したかどうかは、どこで判定すればいい?
Stripeからのcheckout.session.completed を
ローカル環境でStripeのWebhookを試すには?
Stripe CLIのstripe listen --forward-to localhost:3000/api/webhooks/stripe で、whsec_...)を
未払いのユーザーに管理画面を見せないようにするには?
Next.jsのlayout.tsx(サーバーコンポーネント)で