stripe-stack について
stripe-stackは、Next.jsとSupabaseのプロジェクトにStripe決済を組み込むスキルです。Webhook、サブスクリプション、クレジット制の手順とテンプレートを備え、冪等性と安全な本番移行を重視します。
主な機能
- 01Next.jsとSupabaseの連携手順
- 02データベースによるWebhook冪等処理
- 03Webhook署名の検証
- 04サブスクリプションとクレジット制のパターン
- 05テスト環境から本番環境への移行チェックリスト
活用シーン
- 01新規Next.jsプロジェクトへのStripe導入
- 02Stripe Webhookの確実な処理
- 03定期課金の実装
- 04従量課金クレジットの構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ scientiacapital/ skills --skill stripe-stackインストール後、Claude Code で/stripe-stackを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
config.jsonreference/common-errors.mdenvironment-vars.mdpricing-models.mdwebhook-patterns.mdtemplates/env-example.txtidempotency-migration.sqlplans-config.tsstripe-client.tswebhook-handler-nextjs.tswebhook-handler.test.tsworkflows/add-credit-system.mdadd-webhook-handler.mdgo-live-checklist.mdimplement-subscriptions.mdsetup-new-project.md詳細
<quick_start> Next.js + Supabase プロジェクトに決済を追加する:
- Stripe をインストールします:
npm install stripe @stripe/stripe-js - 環境変数を追加します(以下の quick_reference を参照)
- 冪等性テーブルを作成します(以下のスキーマを参照)
- ワークフローを選択します:
setup-new-project.mdまたはadd-webhook-handler.md
// Lazy-loaded Stripe client
import Stripe from 'stripe';
let _stripe: Stripe | null = null;
export function getStripe(): Stripe {
if (!_stripe) {
_stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2025-12-15.clover' });
}
return _stripe;
}
</quick_start>
<success_criteria> 以下の条件を満たしたとき、統合は成功です:
- Webhook ハンドラーがデータベースベースの冪等性を使用している(インメモリではない)
- すべてのキーが環境変数に格納されている(ハードコードされていない)
- ライブモードでデプロイする前にテストモードが完全に動作している
- すべての Webhook エンドポイントで署名検証が行われている
- 処理の前にイベントがログに記録されている(insert-before-process パターン)
- 本番環境へのデプロイ前に Go-live チェックリストが完了している </success_criteria>
<essential_principles>
基本原則
-
冪等性は譲れない
- すべての Webhook ハンドラーはデータベースベースの冪等性を使用しなければならない
- インメモリの Set を決して使用しない(サーバーレスのコールドスタートで失われる)
- イベントレコードは処理の後にではなく、前に挿入する
-
テストモード/ライブモードの分離
- すべてのキーに環境変数を使用する(決してハードコードしない)
- テストキー:
sk_test_、pk_test_、whsec_test_ - ライブキー:
sk_live_、pk_live_、whsec_live_ - 製品/価格はライブモードで再作成する必要がある
-
共有 Stripe アカウント
- すべての NetZero Suite プロジェクトは 1 つの Stripe アカウントを共有する
- 同じ Webhook シークレットをプロジェクト間で使用できる
- 各プロジェクトは独自の Webhook エンドポイント URL を持つ
-
クライアントの遅延初期化
- モジュールレベルで Stripe を初期化しない(ビルドエラーの原因になる)
- サーバーサイドクライアントにはファクトリー関数パターンを使用する
- インスタンスを作成する前に API キーの存在を確認する
</essential_principles>
何を構築していますか?
進める前に、ユースケースを特定してください:
| ユースケース | ワークフロー | 説明 |
|---|---|---|
| 新規プロジェクト | setup-new-project.md | ゼロからの Stripe 統合 |
| Webhook の追加 | add-webhook-handler.md | 既存プロジェクトへの Webhook ハンドラーの追加 |
| サブスクリプション | implement-subscriptions.md | プランによる定期課金 |
| クレジットシステム | add-credit-system.md | 従量課金制のクレジット |
| Go live | go-live-checklist.md | テスト → 本番環境への移行 |
ワークフローのルーティング
新規プロジェクトで Stripe をセットアップする場合:
→ workflows/setup-new-project.md を読む
→ 次に reference/environment-vars.md を読む
→ templates/stripe-client.ts と templates/env-example.txt を使用する
Webhook 処理を追加する場合:
→ workflows/add-webhook-handler.md を読む
→ 次に reference/webhook-patterns.md を読む
→ templates/webhook-handler-nextjs.ts と templates/idempotency-migration.sql を使用する
サブスクリプション課金を実装する場合:
→ workflows/implement-subscriptions.md を読む
→ 次に reference/pricing-models.md を読む
→ templates/plans-config.ts を使用する
クレジット/使用量ベースのシステムを追加する場合:
→ workflows/add-credit-system.md を読む
→ 次に reference/pricing-models.md を読む
テスト → 本番環境へ移行する場合:
→ workflows/go-live-checklist.md を読む
<quick_reference>
クイックリファレンス
環境変数(標準)
# Server-side (never expose to client)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Client-side (safe to expose)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
# Optional: Price IDs (for test→live switching)
STRIPE_PRICE_STARTER_MONTHLY=price_...
STRIPE_PRICE_PRO_MONTHLY=price_...
一般的な Webhook イベント
| イベント | 発火するタイミング | アクション |
|---|---|---|
checkout.session.completed | 顧客がチェックアウトを完了したとき | サブスクリプションレコードを作成する |
customer.subscription.created | 新しいサブスクリプションが開始したとき | ユーザーの制限を初期化する |
customer.subscription.updated | プラン変更、更新 | プラン/制限を更新する |
customer.subscription.deleted | キャンセル | 無料プランにダウングレードする |
invoice.paid | 月次更新の成功 | 使用量カウンターをリセットする |
invoice.payment_failed | 支払いの失敗 | past_due としてマークする |
Stripe クライアントパターン
let _stripe: Stripe | null = null;
export function getStripe(): Stripe {
if (!_stripe) {
const key = process.env.STRIPE_SECRET_KEY;
if (!key) throw new Error('STRIPE_SECRET_KEY not configured');
_stripe = new Stripe(key, {
apiVersion: '2025-12-15.clover',
typescript: true
});
}
return _stripe;
}
冪等性テーブルのスキーマ
CREATE TABLE stripe_webhook_events (
id TEXT PRIMARY KEY, -- Use Stripe event ID directly
type TEXT NOT NULL, -- Event type
data JSONB NOT NULL, -- Full event payload
processed_at TIMESTAMPTZ DEFAULT NOW()
);
Webhook ハンドラーの構成
export async function POST(request: NextRequest) {
const body = await request.text();
const signature = request.headers.get('stripe-signature');
// 1. Verify signature
const event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
// 2. Check idempotency (BEFORE processing)
const { data: existing } = await supabase
.from('stripe_webhook_events')
.select('id')
.eq('id', event.id)
.single();
if (existing) return NextResponse.json({ duplicate: true });
// 3. Log event (INSERT before processing)
await supabase.from('stripe_webhook_events').insert({
id: event.id,
type: event.type,
data: event,
});
// 4. Process event
switch (event.type) {
case 'checkout.session.completed':
await handleCheckout(event.data.object);
break;
// ... other handlers
}
return NextResponse.json({ received: true });
}
</quick_reference>
<integration_notes>
統合に関するメモ
対応するサービス
- Supabase:Webhook ハンドラーにはサービスロールクライアントを使用します(RLS をバイパスします)。
- Prisma:冪等性テーブルに使用できる Supabase の代替手段です。
- Vercel:Webhook ルートにランタイム/maxDuration の設定を追加します。
- Next.js App Router:生の本文を取得するには
request.text()を使用します。
関連スキル
supabase-sql-skill- データベースのマイグレーション用create-hooks-skill- デプロイ後の通知用
GitHub リポジトリ
プライベートテンプレートとサンプルはこちらから利用できます:
github.com/ScientiaCapital/stripe-stack
</integration_notes>
<reference_index>
参考ファイル
| ファイル | 目的 |
|---|---|
reference/webhook-patterns.md | 冪等性、イベント処理、エラー復旧 |
reference/pricing-models.md | プラン、クレジット、使用量ベースの課金 |
reference/environment-vars.md | 標準的な環境変数の規約 |
reference/common-errors.md | トラブルシューティングガイド |
テンプレートファイル
| ファイル | 目的 |
|---|---|
templates/webhook-handler-nextjs.ts | Webhook ルート全体(コピー&ペースト用) |
templates/stripe-client.ts | 遅延読み込みクライアントのファクトリー |
templates/plans-config.ts | サブスクリプションプランの定義 |
templates/idempotency-migration.sql | Supabase マイグレーション |
templates/webhook-handler.test.ts | テストテンプレート |
templates/env-example.txt | 標準的な .env テンプレート |
ワークフローファイル
| ファイル | 目的 |
|---|---|
workflows/setup-new-project.md | Stripe を新規に統合 |
workflows/add-webhook-handler.md | 既存プロジェクトに Webhook を追加 |
workflows/implement-subscriptions.md | サブスクリプション課金 |
workflows/add-credit-system.md | 従量課金型のクレジット |
workflows/go-live-checklist.md | テストから本番環境への移行 |
</reference_index>
実行結果のサイドカーを出力
最後の手順として、~/.claude/skill-analytics/last-outcome-stripe-stack.json に書き込みます:
{"ts":"[UTC ISO8601]","skill":"stripe-stack","version":"1.0.0","variant":"default",
"status":"[success|partial|error]","runtime_ms":[estimated ms from start],
"metrics":{"webhooks_configured":[n],"products_created":[n],"checkout_flows_built":[n]},
"error":null,"session_id":"[YYYY-MM-DD]"}
一部のステージが失敗したものの結果が生成された場合は、ステータスに "partial" を使用します。出力が生成されなかった場合に限り、"error" を使用します。