ハンズオン

Stripe セットアップ

Stripe Checkout を使った決済と、Webhook によるクレジット付与の最終確定。環境変数、セッション作成、コールバック処理、署名付き Webhook の扱いを解説します。

概要

このテンプレートは Stripe Checkout で決済し、支払い後にクレジット残高を付与します。

  • UI: /[locale]/pricing は TypeScript の設定からプランを表示。
  • セッション作成: POST /api/checkout → Stripe にリダイレクト。
  • リダイレクト: 成功後に /api/pay/callback/stripe (GET)。
  • 最終反映: Stripe の Webhook が /api/pay/webhook/stripe (POST) に届き、注文を paid にしてクレジットを付与。

ソース・オブ・トゥルースは Webhook です。コールバックは画面遷移のみ。


クイックスタート資料


決済ゲートウェイ 101

決済ゲートウェイとは?

決済ゲートウェイは、クレジット/デビットカード、デジタルウォレット、銀行振込などの電子決済を処理・認可するオンラインの仲介サービスです。実店舗のカードリーダーのオンライン版に相当し、取引を安全に検証して承認/拒否します。

2025 年には世界の EC 売上が約 7.4 兆ドルに達すると予測されています。滑らかで安全なチェックアウトは、顧客体験やコンバージョン、セキュリティと信頼の向上に直結します。

どのように機能するか

  1. 購入 : ユーザーがチェックアウトで支払い情報を入力。
  2. 暗号化と送信 : サイトがデータを暗号化してゲートウェイへ送付。
  3. 転送 : ゲートウェイが暗号化データを決済プロセッサに転送。
  4. 連携 : プロセッサが発行銀行に認可を依頼。
  5. 承認/拒否 : 資金状況やセキュリティ検査に基づき銀行が判断。
  6. 応答返送 : 判断がプロセッサ→ゲートウェイへ戻る。
  7. 完了 : 承認なら確認、拒否ならエラーをユーザーに表示。
  8. 資金決済 : 承認取引はアクワイアラ経由で貴社口座に入金。

これらは自動化され、通常は数秒で完了します。

決済ゲートウェイを統合する手順

  1. プロバイダ選定 : 手数料、対応決済、セキュリティ、プラットフォーム互換性。
  2. マーチャント口座 : Stripe など一体型もあれば、別途必要な場合も。
  3. API キー取得 : サイト/アプリとゲートウェイを認証する鍵。
  4. 実装 : プラグイン(Shopify/WooCommerce)またはカスタム実装。Stripe の API は特に扱いやすいです。
  5. サンドボックスで検証 : テスト環境・テストカードで動作確認。
  6. 本番化 : ライブキーに切替え、稼働を監視。

エンドポイント

  • /api/checkout (POST): src/data/pricing.ts を元に検証し、Checkout セッションを作成、注文を created で保存。
  • /api/pay/callback/stripe (GET): Stripe からのリダイレクト用。最終処理は行いません。
  • /api/pay/webhook/stripe (POST): 署名検証後、checkout.session.completed を処理して付与。

クレジットの流れ

  1. セッションのメタデータから order_no を取得。
  2. 注文を paid に更新し、決済情報を保存。
  3. credits に正の行を追加(order_pay)。

必要な環境変数

STRIPE_PRIVATE_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_WEB_URL=http://localhost:3000
NEXT_PUBLIC_PAY_CANCEL_URL=/pricing
NEXT_PUBLIC_PAY_SUCCESS_URL=/pricing
NEXT_PUBLIC_PAY_FAIL_URL=/pricing

ローカル Webhook 設定

stripe login
stripe listen --forward-to localhost:3000/api/pay/webhook/stripe
# 表示された whsec を .env に設定
stripe trigger checkout_session_completed

動作確認

  1. サインアップ/ログイン。
  2. /[locale]/pricing でプランを選択。
  3. テストカード 4242 4242 4242 4242 で決済。
  4. Webhook が注文確定とクレジット付与を実行。
  5. /[locale]/credits-test で残高を確認。

Stripe 統合ガイド(ハイレベル)

以下の手順で素早く Stripe を統合できます:

  1. アカウント作成:Stripe に登録し、事業情報と入金口座を設定。
  2. API キー取得:ダッシュボード → Developers で公開鍵/秘密鍵を取得。
  3. ライブラリ導入:公式 SDK(JavaScript/TypeScript、Ruby、Python など)。
  4. フロント実装:Stripe.js と UI コンポーネント(Elements/Payment Element)やプラグインで安全にカード情報を収集。
  5. サーバ実装:秘密鍵で Stripe を初期化し、PaymentIntent/Checkout Session を作成するエンドポイントを用意。
  6. 情報収集:生カードを保持せず、Stripe のコンポーネントで取得。
  7. サーバ送信:HTTPS 経由で意図情報を安全に送信。
  8. サーバ処理:SDK で PaymentIntent/Checkout Session を作成。
  9. 応答処理:DB を更新し、成功/失敗をクライアントへ返却。
  10. エラー/例外:残高不足、期限切れ、3DS、リトライ等を考慮。
  11. 徹底テスト:テストモード、Webhook、テストカードで各パターン検証。
  12. 本番移行:テスト鍵→本番鍵、必要な支払い方法を有効化し監視。

ヒント:公式ガイド — https://docs.stripe.com/get-started


実装メモ

  • プラン: src/data/pricing.ts に定義(型安全)。
  • handleCheckoutSession は一括・サブスク両対応(サブスクは最新請求書から PaymentIntent を取得)。

請求とサブスクリプション

カスタマーポータル

Stripe の Customer Portal を有効化し、顧客が行える操作(解約/再開、カード更新、請求書閲覧)を設定します。

本テンプレートには以下を用意しています:

  • API: GET /api/billing/portal — ポータルセッションを作成して Stripe にリダイレクト
  • UI: /:locale/account/billing — 「Manage billing」ボタンでポータルを開く

実装箇所:

  • API ルート: src/app/api/billing/portal/route.ts
    • ログイン中ユーザーのメールで Stripe Customer を検索/作成
    • return_url/:locale/account/billing に設定

UI(Server Component):

  • src/app/[locale]/account/billing/page.tsx から /api/billing/portal?locale=<locale> へリンク

督促(支払い失敗)

Webhook invoice.payment_failed で、支払い方法の更新を促すメール(Resend)を送信します。

  • テンプレート: src/services/email/templates/payment-failed.tsx
  • 送信関数: sendPaymentFailedEmail()src/services/email/send.ts
  • Webhook 実装: src/app/api/pay/webhook/stripe/route.ts

Stripe CLI でのテスト:

stripe trigger invoice_payment_failed

備考:メールに /:locale/account/billing へのリンクを含め、アプリからポータルを開けます。


製品と価格(Stripe)

Stripe は「販売するもの(製品)」と「価格設定(価格)」を分離しています:

  • 製品(prod_...):名称・説明、カタログ用メタデータ。1つの製品に複数の価格が紐づきます。
  • 価格(price_...):通貨、金額、課金間隔(毎月/毎年)、トライアル、税設定、ティアなど。サブスクは価格に紐づきます。

Checkout で価格 ID を使う理由

  • サブスクには正確な課金条件が必要です。price_... に紐づけることで、更新課金、按分、トライアル、Stripe Tax が正しく機能します。
  • 更新時の請求書行にも同じ price_... が入り、Webhook がプランを特定して各サイクルでクレジットを付与できます。
  • 一時的なインライン価格より安全で運用しやすい:金額やルールは Stripe 側で管理し、コードは安定します。

Stripe で価格 ID を見つける手順

  1. ダッシュボード → 製品 → 対象の製品を選択。
  2. 価格テーブルの行末「︙」(三点) をクリック → 「価格 ID をコピー」。
  3. price_... で始まります。prod_...(製品 ID)と混同しないでください。

環境変数(任意だが推奨)

# プランと Stripe 価格をマッピング。空ならインライン price_data にフォールバック
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_MONTHLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_MONTHLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_YEARLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_YEARLY=price_...
# CNY 価格(任意)
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_MONTHLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_MONTHLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_YEARLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_YEARLY_CNY=price_...

価格 ID の有無による動作

  • サブスクプランに Price ID があれば、Checkout はそれを直接使用します。
  • 無い場合は、src/data/pricing.ts の金額でインライン price_data を使ってセッションを作成します(安全なフォールバック)。

更新(リニューアル)とクレジット

本テンプレートは更新時のクレジット自動付与に対応しています:

  • Webhook invoice.payment_succeededbilling_reason=subscription_cycle の場合)で、当該期間の更新注文を作成し、プランのクレジットを付与します。
  • 冪等性:同一 (subscription_id, period_start) の注文が既にあればスキップします。
  • 初回購入は引き続き checkout.session.completed で処理します。二重付与を避けるため、サイクル外の請求書は無視します。

リマインダー:テスト時は Webhook 転送を設定

ローカル(または本番以外)での検証では、Stripe のイベントを必ずバックエンドに転送してください。そうしないと支払いは成功しても、注文/クレジットが確定しません。

stripe listen --forward-to localhost:3000/api/pay/webhook/stripe

トンネル/リモート URL を利用する場合は、localhost の代わりにそのドメインを指定してください。


その他の決済オプション

Stripe は数ある選択肢のひとつです。暗号資産(クリプト)決済や PayPal など他の手段が必要な場合はご連絡ください。ユースケースと市場に合ったプロバイダの選定・実装をお手伝いします。