stripe-integration について
stripe-integrationは、Stripe決済を実装する開発者向けのスキルで、APIの選択、注文処理、サブスクリプション、キーの保護、テストを扱います。Webhookと冪等性を重視し、詳細の確認にはStripeのドキュメントを参照するよう案内します。
主な機能
- 01決済場面に応じたStripe APIの選択
- 02Webhookによる注文処理とサブスクリプション同期
- 03署名検証と重複イベントへの対応
- 04重複作成を防ぐ冪等性キーの利用
- 05キー、サンドボックス、決済テストの管理
活用シーン
- 01Webアプリでの単発決済やサブスクリプションの実装
- 02遅延決済、Webhook再試行、イベント順序への対応
- 03Stripeキーの保護と連携のテスト
MCP 対応
Stripeの詳細を確認するため、Stripe MCP serverのsearch_stripe_documentationツールを利用するよう案内しています。Stripe MCP serverはAgentにアカウントデータとドキュメント検索を提供し、書き込み操作には確認を求めます。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davila7/ claude-code-templates --skill stripe-integrationインストール後、Claude Code で/stripe-integrationを入力して読み込まれたか確認します。
詳細
Stripe 連携
注文の消失や二重請求、キーの漏えいを防ぐ決済コードを構築するためのガイダンスです。2026 年 10 月時点の Stripe 独自の推奨事項に従っています(パラメーター、イベント名、API バージョンなどの)詳細が重要な場合は、記憶に頼らず、docs.stripe.com または Stripe MCP サーバーの search_stripe_documentation ツールで確認してください。
バージョン
- 最新の API バージョン:
2026-09-30.endive。アカウントのデフォルト設定が変更されてもレスポンス形式が変わらないよう、クライアントで明示的に固定してください。 - 最新のサーバー SDK:Node.js 23、Python 16、Ruby 20、PHP 22、Java 34、Go 87、.NET 53。クライアントインスタンスを作成してください。グローバルキーのパターン(
stripe.api_key = …、Stripe.setApiKey)は、すべての SDK で非推奨です。 - 既存の連携を新しいバージョンに移行するには、まずAPI バージョンのアップグレードをお読みください。
// lib/stripe.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_API_KEY!, {
apiVersion: '2026-09-30.endive',
});
API を選ぶ
| 構築するもの | 使用するもの |
|---|---|
| 一回限りの決済、シンプルな商品 | Payment Links(コード不要)または Checkout Sessions |
| ウェブアプリでの一回限りの決済 | Checkout Sessions(ホスト型または埋め込み型) |
| カスタム決済フォーム | Checkout Sessions と ui_mode: 'custom' + Payment Element |
| 後で使うためにカードを保存 | Setup Intents |
| オフセッションでの請求(顧客がその場にいない場合) | 保存済みの支払い方法を使用する Payment Intents |
| サブスクリプション、シート、トライアル | Billing APIs + Checkout mode: 'subscription' + Customer Portal |
| 新しい従量課金 | Metronome(Stripe の使用量課金プラットフォーム)。生の Billing Meters は使用しないでください |
| マーケットプレイスまたはプラットフォーム | Connect、Accounts v2(/v2/core/accounts)、Stripe ホスト型オンボーディング |
Charges API、Sources API、Tokens API、Card Element、plan オブジェクトは使用しないでください。これらは非推奨またはレガシーです。上の表と Prices を使用してください。
ルール
成功ページではなく、ウェブフックから処理を実行する
顧客は決済後、成功ページが読み込まれる前にタブを閉じることがあります。アクセス権の付与、メールの送信、注文の記録は、ウェブフックハンドラー内でのみ行ってください。
checkout.session.completedとcheckout.session.async_payment_succeededを処理してください。session.payment_status !== 'unpaid'の場合にのみ、注文を処理してください。銀行口座からの引き落としなど、決済に時間がかかる方法では、未払いのままcompletedが送信されます。- これらの決済失敗には
checkout.session.async_payment_failedを処理してください。 - サブスクリプションでは、
customer.subscription.created|updated|deleted、invoice.paid、invoice.payment_failedが必要です。更新、督促、キャンセルは、この方法でのみ通知されます。 - ウェブフックはリリース前に必須です。後回しにしないでください。
すべてのウェブフック署名を未加工のボディで検証する
署名の検証には、Stripe が送信した正確なバイト列が必要です。ボディ解析ミドルウェアを使うと検証できなくなります。
// Next.js App Router: app/api/stripe/webhook/route.ts
import Stripe from 'stripe';
import { stripe } from '@/lib/stripe';
export async function POST(req: Request) {
const body = await req.text(); // raw body, do not use req.json()
const signature = req.headers.get('stripe-signature');
if (!signature) return new Response('missing signature', { status: 400 });
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!,
);
} catch {
return new Response('invalid signature', { status: 400 });
}
switch (event.type) {
case 'checkout.session.completed':
case 'checkout.session.async_payment_succeeded': {
// Re-fetch instead of trusting the payload snapshot.
const session = await stripe.checkout.sessions.retrieve(event.data.object.id);
// fulfillOrder must be idempotent: the same event can arrive twice.
if (session.payment_status !== 'unpaid') await fulfillOrder(session.id);
break;
}
case 'checkout.session.async_payment_failed':
await markPaymentFailed(event.data.object.id);
break;
}
return new Response(null, { status: 200 });
}
- Cloudflare Workers、Deno、Bun では
await stripe.webhooks.constructEventAsync(...)を使用してください。 - Express では、ウェブフックルートのみに
express.raw({ type: 'application/json' })をマウントし、グローバルなexpress.json()より前に配置してください。 - ライブモードでは、Stripe は最大 3 日間再試行し、同じイベントを複数回、または順不同で配信することがあります。
fulfillOrderを冪等にしてください(処理済みのイベント ID またはセッション ID を一意制約付きで保存します)。イベントの順序ではなく、オブジェクトの現在の状態に基づいて判断してください。 - 速やかに 2xx を返し、時間のかかる処理はキューに移してください。
すべての作成処理で冪等性キーを使用する
ネットワークの再試行時に冪等性キーがないと、請求やサブスクリプションが二重に作成される可能性があります。
await stripe.paymentIntents.create(
{ amount: 2000, currency: 'usd', customer: customerId },
{ idempotencyKey: `order-${orderId}-charge` },
);
キーは試行ごとのランダム値ではなく、自分のレコード(注文 ID、ユーザー ID + アクション)から導出してください。そうしないと、再試行時に新しいキーが使われます。
支払い方法の選択は Stripe に任せる
payment_method_types(['card'] も含む)を渡さないでください。省略すると、ダッシュボードで設定した動的な支払い方法が有効になります。リストを絞るには、Payment Intents と Setup Intents で payment_method_configuration、excluded_payment_method_types、または allowed_payment_method_types を使用してください。例外は Terminal のみです。payment_method_types: ['card_present'] を使用してください。
Stripe オブジェクトを ID で自分のレコードに紐付ける
Stripe Customer、Subscription、Checkout Session の ID を独自の行に保存し、それらの ID を使って Webhook イベントを照合してください。metadata(たとえば、セッションの client_reference_id や metadata.orderId)はフォールバックとして使用し、主な紐付けには使わないでください。
サブスクリプションの状態を同期する
上記のサブスクリプション Webhook で更新されるデータベースからアクセス権を読み取り、判断にサブスクリプションの状態が関わる場合は、Stripe からサブスクリプションを再取得してください。アップグレード、ダウングレード、解約、カード情報の更新には、それらの画面を自作せず、Customer Portal を使用してください。プランごとに 1 つの Product を作成し、複数の Price を関連付けるのは、同一プランのバリエーション(月次、年次、通貨)の場合に限ってください。Payment Intents のみを使った更新処理を構築しないでください。リトライと督促は Billing が処理します(Dashboard で Smart Retries とメールを設定してください)。
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
customer: stripeCustomerId,
line_items: [{ price: priceId, quantity: 1 }],
subscription_data: { trial_period_days: 14 },
success_url: `${baseUrl}/billing/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${baseUrl}/pricing`,
});
税金
automatic_tax: { enabled: true } を設定しても、顧客の管轄区域に対するアカウントの税務登録が有効になるまでは、税金は徴収されず、エラーも返されません。税金が有効だと伝える前に、登録状況を確認してください。
キーとセキュリティ
- 完全なシークレットキー(
sk_)よりも、各サービスに必要な権限のみを付与した制限付き API キー(rk_)を優先してください。環境ごとに別のキーを使用してください。 - キーはプラットフォームのシークレットストア(Cloudflare と Vercel のシークレット、AWS Secrets Manager、GCP Secret Manager、Azure Key Vault)に保管してください。ソースコード、クライアントコード、モバイルアプリ、ログ、エラーメッセージには絶対に含めないでください。リポジトリ内で
[sr]k_live_に一致する文字列は本番キーです。直ちにローテーションしてください。 - Webhook 署名シークレット(
whsec_)は API キーと同様に扱ってください。 sk_およびrk_の文字列を対象とした、コミット前のシークレットスキャンを追加してください。script-src、frame-src、connect-srcでhttps://*.stripe.comを許可する Content-Security-Policy を送信してください。- Connect OAuth フローでは、ランダムな
state値を送信して検証してください。
テスト
- アカウントで共有されるテストモードではなく、開発者または CI ジョブごとに 1 つの sandbox を使って開発してください。
stripe sandbox createを実行すると、サインアップせずに作成できます。 - イベントをローカルに転送します:
stripe listen --forward-to localhost:3000/api/stripe/webhook(使用するwhsec_が表示されます)。続けてstripe trigger checkout.session.completedを実行します。 - テストカード:
4242 4242 4242 4242は成功します。4000 0025 0000 3155は 3D Secure が必要です。4000 0000 0000 9995は拒否されます(残高不足)。 - テストクロックを使って、サブスクリプションの更新や支払い失敗をシミュレートしてください。
- リリース前に go-live checklist を確認し、処理するイベントのみを設定した本番 Webhook エンドポイントを登録してください。
ツール
- Stripe MCP サーバー(
https://mcp.stripe.com、OAuth)を使うと、エージェントがアカウントデータやドキュメント検索を利用できます:npx claude-code-templates@latest --mcp devtools/stripe。 - MCP 経由で書き込みや資金移動を行う前に確認するよう設定してください:
npx claude-code-templates@latest --setting permissions/stripe-mcp-confirm-writes。 - 会社の設立:Stripe Atlas(Delaware C corp または LLC)は、MCP(
atlas_company_formation)または CLI(stripe plugin install toolsを実行し、続けてstripe tools create_atlas_applicationを実行)から開始できます。Atlas はすべての入力値を検証します。本人確認、支払い、署名、申請は Stripe Dashboard で行います。法人形態、株式、ベスティングに関する創業者の決定を記録してください。ユーザーに代わって決定しないでください。 - Stripe は、自社で保守するエージェントスキル(
npx skills add https://docs.stripe.comまたはstripe agent setup)も公開しています。これにはstripe-best-practicesとupgrade-stripeが含まれます。
関連スキル
相性の良いスキル:nextjs-supabase-auth、nextjs-app-router-patterns、supabase-postgres-best-practices、api-security-best-practices