第2章:Next.js と Stripe Checkout の統合

この章では、Stripeの強力な機能であるStripe Checkoutの使用方法を学びます。

もし台湾の決済サービス(例:緑界)を統合したことがあれば、自分で決済ページを作成し、データをパッケージ化、暗号化、リダイレクトする必要があったでしょう。Stripe Checkoutはこのプロセスを完全に革新します。 Stripe CheckoutはStripeがホストする決済ページで、数十億回の取引を通じたA/Bテストが行われ、非常に高いコンバージョン率を誇ります。多言語自動切り替え、通貨のローカライズをサポートするだけでなく、Apple PayGoogle Payのワンクリック決済機能も標準装備しています!

私たちがこれから行うのは、Next.jsのバックエンドAPIを通じてStripeに専用のCheckout URLを「申請」し、ユーザーをそこに誘導することです。


💻 実装:Checkout Session APIの作成

App Routerアーキテクチャの下で、APIルートを作成します:src/app/api/stripe/checkout/route.ts

import { NextResponse } from 'next/server';
import Stripe from 'stripe';
import { createClient } from '@/utils/supabase/server'; // Supabase使用を想定

// Stripeインスタンスの初期化
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16', // APIバージョンを固定することを推奨
});

export async function POST(request: Request) {
  try {
    // 1. ユーザー認証 (ログインユーザーのみが購入可能)
    const supabase = await createClient();
    const { data: { user } } = await supabase.auth.getUser();

    if (!user) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }

    // 2. フロントエンドから購入するPrice IDを取得
    const body = await request.json();
    const { priceId } = body;

    // 3. Stripe Checkout Sessionの作成
    // これが最も重要なAPI呼び出しです!
    const session = await stripe.checkout.sessions.create({
      // Stripeに今回の取引に含まれる商品を伝える
      line_items: [
        {
          price: priceId, // 前章でDashboardに作成したPrice IDを渡す
          quantity: 1,    // 数量は1
        },
      ],
      // modeは'payment'(単回支払い)または'subscription'(サブスクリプション)が可能
      mode: 'subscription', 
      
      // 成功時と失敗時のリダイレクトURL (ReturnURLのフロントエンド版)
      // 注意:デプロイ後にlocalhostに戻らないよう、環境変数で動的にURLを決定する必要があります!
      success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/payment-success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing`,
      
      // データベースのUser IDをメタデータ(metadata)に埋め込む
      // これにより、支払い成功時にStripe WebhookからこのIDが返されます
      metadata: {
        userId: user.id,
      },
      
      // ユーザーのEmailを事前入力。入力時間を節約しコンバージョン率向上!
      customer_email: user.email,
    });

    // 4. 作成したCheckout URLをフロントエンドに返す
    return NextResponse.json({ url: session.url });

  } catch (error: any) {
    console.error('Stripe Checkout Error:', error);
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

💡 コードのハイライト解説

  1. mode: 'subscription':この1行だけで、Stripeが毎月の定期課金ロジックを自動処理します。自分でCron Job(スケジューラ)を書いて定期課金する必要はありません!買い切りのコースを販売する場合は、mode: 'payment'に変更するだけです。
  2. metadata: { userId: user.id }:これは非常に重要なテクニックです!緑界で使うCustomFieldのようなものです。ユーザーがStripeページで支払いを完了すると、StripeはWebhookを送信します。このmetadata.userIdを使って、データベースのどのアカウントに権限を付与するかを判断します。
  3. success_url:ユーザーが支払いに成功した後に表示されるページです。URLに{CHECKOUT_SESSION_ID}変数を含めることができ、Stripeが自動的に実際のSession IDに置き換えます。これによりフロントエンドページはこのIDを使ってデータを取得できます。

🖥️ フロントエンド統合:決済リクエストの開始

バックエンドAPIが完成したら、フロントエンドのボタンは非常にシンプルです。Pricingページに以下を追加します:

'use client';
import { useState } from 'react';

export default function PricingButton({ priceId }: { priceId: string }) {
  const [loading, setLoading] = useState(false);

  const handleCheckout = async () => {
    setLoading(true);
    try {
      const response = await fetch('/api/stripe/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ priceId }),
      });
      
      const data = await response.json();
      
      if (data.url) {
        // ブラウザをStripeの高コンバージョン決済ページに直接リダイレクト
        window.location.href = data.url;
      }
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <button 
      onClick={handleCheckout} 
      disabled={loading}
      className="bg-indigo-600 text-white font-bold py-3 px-6 rounded-xl hover:bg-indigo-700 transition"
    >
      {loading ? '決済準備中...' : 'Apple Pay / クレジットカードで支払う'}
    </button>
  );
}

✅ 本章のまとめ

これで、ユーザーがボタンをクリックすると、シームレスにStripeの公式決済ページに誘導され、Apple PayやGoogle Payのオプションが表示されます! しかし、ユーザーが支払いに成功してsuccess_urlにリダイレクトされた後、どのように権限を付与すればよいでしょうか? 次の章では、決済システムの核心であるWebhookサーバー間通信について探求します。これにより、ユーザーのブラウザが支払い時にクラッシュした場合でも、データベースに正確に取引記録が残るようになります!

完全なチュートリアルをロック解除

このチャプターは有料コンテンツです。プロジェクトに参加して、10以上の神レベルのPromptや実際のソースコード例を含む、5000字以上の深い分析をロック解除してください!