mcpField
S
stripe-integration

stripe-integration

github.com/breverdbidder開発40 回インストール
リポジトリ

stripe-integration について

stripe-integration は、Stripe の決済やサブスクリプションを実装するための開発スキルです。セキュリティ、Webhook、テスト、顧客ライフサイクル管理の指針を提供します。

主な機能

  1. 01決済とチェックアウトのフロー
  2. 02サブスクリプション課金パターン
  3. 03Webhook の設定とイベント処理
  4. 04セキュリティと冪等性の指針
  5. 05Stripe CLI によるテスト手順

活用シーン

  1. 01アプリへの Stripe 決済の追加
  2. 02サブスクリプション課金の構築
  3. 03決済 Webhook の設定とテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/breverdbidder/life-os --skill stripe-integration

インストール後、Claude Code で/stripe-integrationを入力して読み込まれたか確認します。

詳細

Stripe 連携

このスキルを使うと、セキュリティ、信頼性、ユーザーエクスペリエンスに関するベストプラクティスに従い、Stripe 決済の連携を最初から正しく実装できます。

このスキルを使用するタイミング

次の場合にこのスキルを使用してください:

  • 新しいアプリケーションに Stripe を追加する
  • サブスクリプション課金を実装する
  • 決済処理を設定する
  • Webhook を設定する
  • 顧客のライフサイクルを管理する
  • 返金や異議申し立てに対応する
  • チェックアウトフローを実装する

基本原則

セキュリティを最優先:

  • シークレットキーをクライアント側に公開しないでください
  • 本番環境では必ず HTTPS を使用してください
  • Webhook の署名を検証してください
  • カード情報の処理には Stripe のクライアントサイドライブラリを使用してください

冪等性:

  • API リクエストには冪等性キーを使用してください
  • Webhook イベントの重複に対応してください
  • リトライしても安全な設計にしてください

ユーザーエクスペリエンス:

  • 明確なエラーメッセージ
  • 決済中の読み込み状態
  • 成功/失敗のフィードバック
  • メールによる確認通知

実装ワークフロー

1. 環境の設定

必要な認証情報:

STRIPE_PUBLISHABLE_KEY_TEST=pk_test_...
STRIPE_SECRET_KEY_TEST=sk_test_...
STRIPE_WEBHOOK_SECRET_TEST=whsec_...

STRIPE_PUBLISHABLE_KEY_PROD=pk_live_...
STRIPE_SECRET_KEY_PROD=sk_live_...
STRIPE_WEBHOOK_SECRET_PROD=whsec_...

絶対にコミットしないでください:

  • .env またはシークレットマネージャーに追加する
  • 環境ファイルには .gitignore を使用する
  • 開発/ステージング/本番環境で異なるキーを使用する

インストール:

# Node.js
npm install stripe @stripe/stripe-js

# Python
pip install stripe
2. 基本的な決済フロー

クライアント側(React の例):

import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);

function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();

  const handleSubmit = async (event) => {
    event.preventDefault();
    
    if (!stripe || !elements) return;

    // Create payment intent on backend first
    const { clientSecret } = await fetch('/api/create-payment-intent', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount: 5000 }) // $50.00
    }).then(r => r.json());

    // Confirm payment on client
    const { error, paymentIntent } = await stripe.confirmCardPayment(
      clientSecret,
      {
        payment_method: {
          card: elements.getElement(CardElement),
          billing_details: {
            name: 'Customer Name',
            email: '[email protected]'
          }
        }
      }
    );

    if (error) {
      console.error(error.message);
    } else if (paymentIntent.status === 'succeeded') {
      // Payment successful
      console.log('Payment succeeded!');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <CardElement />
      <button type="submit" disabled={!stripe}>
        Pay
      </button>
    </form>
  );
}

サーバー側(Node.js の例):

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/api/create-payment-intent', async (req, res) => {
  try {
    const { amount } = req.body;

    const paymentIntent = await stripe.paymentIntents.create({
      amount: amount,
      currency: 'usd',
      automatic_payment_methods: {
        enabled: true,
      },
      metadata: {
        userId: req.user.id, // Track in your DB
        orderId: req.body.orderId
      }
    });

    res.json({ clientSecret: paymentIntent.client_secret });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
3. サブスクリプションの実装

サブスクリプションの作成:

// Server-side
async function createSubscription(customerId, priceId) {
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: priceId }],
    payment_behavior: 'default_incomplete',
    payment_settings: {
      save_default_payment_method: 'on_subscription'
    },
    expand: ['latest_invoice.payment_intent']
  });

  return {
    subscriptionId: subscription.id,
    clientSecret: subscription.latest_invoice.payment_intent.client_secret
  };
}

サブスクリプションのライフサイクルへの対応:

  • 顧客が登録する → 顧客とサブスクリプションを作成する
  • 決済が成功する → 機能を有効にする
  • 決済が失敗する → 顧客に通知し、再試行する
  • 解約がリクエストされる → period_end に処理する
  • 更新 → Webhook で決済を確認する
4. Webhook の設定

重要:Webhook は Stripe がイベントを通知するための仕組みです。正しく実装する必要があります。

エンドポイントの設定:

const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    // Verify webhook signature
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    console.log(`Webhook signature verification failed: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      await handlePaymentSuccess(paymentIntent);
      break;

    case 'payment_intent.payment_failed':
      const failedPayment = event.data.object;
      await handlePaymentFailure(failedPayment);
      break;

    case 'customer.subscription.created':
      const subscription = event.data.object;
      await handleSubscriptionCreated(subscription);
      break;

    case 'customer.subscription.updated':
      const updatedSub = event.data.object;
      await handleSubscriptionUpdated(updatedSub);
      break;

    case 'customer.subscription.deleted':
      const deletedSub = event.data.object;
      await handleSubscriptionCanceled(deletedSub);
      break;

    case 'invoice.payment_succeeded':
      const invoice = event.data.object;
      await handleInvoicePaymentSucceeded(invoice);
      break;

    case 'invoice.payment_failed':
      const failedInvoice = event.data.object;
      await handleInvoicePaymentFailed(failedInvoice);
      break;

    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  res.json({received: true});
});

対応が必須のイベント:

  1. payment_intent.succeeded - 1 回限りの決済が成功した
  2. payment_intent.payment_failed - 決済が失敗した
  3. customer.subscription.created - 新しいサブスクリプションが作成された
  4. customer.subscription.updated - サブスクリプションが変更された
  5. customer.subscription.deleted - サブスクリプションが解約された
  6. invoice.payment_succeeded - 継続課金が成功した
  7. invoice.payment_failed - 継続課金が失敗した
5. 顧客管理

顧客の作成:

const customer = await stripe.customers.create({
  email: user.email,
  name: user.name,
  metadata: {
    userId: user.id
  }
});

// Store customer.id in your database
await db.users.update(user.id, {
  stripeCustomerId: customer.id
});

支払い方法の紐付け:

const paymentMethod = await stripe.paymentMethods.attach(
  pm_id,
  { customer: customerId }
);

await stripe.customers.update(customerId, {
  invoice_settings: {
    default_payment_method: pm_id
  }
});
6. テスト

テストモード:

  • テストキー(pk_test_、sk_test_)を使用する
  • テスト用カード番号:
    • 成功:4242 4242 4242 4242
    • 拒否:4000 0000 0000 0002
    • 3D セキュア:4000 0027 6000 3184
  • Webhook のテストには stripe CLI を使用する:
    stripe listen --forward-to localhost:3000/webhook
    stripe trigger payment_intent.succeeded
    

Webhook のテスト:

# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login
stripe login

# Forward webhooks to local server
stripe listen --forward-to localhost:3000/webhook

# Trigger events manually
stripe trigger customer.subscription.created
7. エラー処理

対応すべき一般的なエラー:

try {
  const charge = await stripe.charges.create({...});
} catch (error) {
  switch (error.type) {
    case 'StripeCardError':
      // Card was declined
      console.log('Card declined:', error.message);
      break;
    case 'StripeInvalidRequestError':
      // Invalid parameters
      console.log('Invalid request:', error.message);
      break;
    case 'StripeAPIError':
      // Stripe API error
      console.log('API error:', error.message);
      break;
    case 'StripeConnectionError':
      // Network error
      console.log('Network error');
      break;
    case 'StripeAuthenticationError':
      // Authentication error
      console.log('Auth error');
      break;
    default:
      console.log('Unknown error:', error);
  }
}

本番環境向けチェックリスト

公開前に確認してください:

  • 本番用 API キー(pk_live_、sk_live_)を使用している
  • すべての箇所で HTTPS が有効になっている
  • Stripe Dashboard で Webhook エンドポイントが設定されている
  • Webhook 署名の検証が実装されている
  • 重要なイベントすべてに対応している
  • API 呼び出しに冪等性キーを使用している
  • 顧客の作成がユーザーアカウントに紐付いている
  • すべての決済フローでエラー処理を行っている
  • メール通知が設定されている
  • 返金手順が文書化されている
  • 実際のテストカードでテスト済みである
  • CLI を使って Webhook イベントをテスト済みである
  • サブスクリプションの更新フローをテスト済みである
  • 解約フローをテスト済みである
  • 決済失敗時の再試行ロジックがある
  • セキュリティレビューが完了している

よくあるパターン

サブスクリプションのプラン
const plans = {
  basic: 'price_basic123',
  pro: 'price_pro456',
  enterprise: 'price_enterprise789'
};

function subscribeToPlan(customerId, tier) {
  return stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: plans[tier] }]
  });
}
従量課金
// Record usage
await stripe.subscriptionItems.createUsageRecord(
  subscriptionItemId,
  {
    quantity: 100,
    timestamp: Math.floor(Date.now() / 1000),
    action: 'increment'
  }
);
日割り計算によるプラン変更
await stripe.subscriptions.update(subscriptionId, {
  items: [{
    id: subscriptionItemId,
    price: newPriceId
  }],
  proration_behavior: 'create_prorations'
});

リソース

公式ドキュメント:

テスト:

セキュリティ:

ヒント

  1. シンプルに始める:サブスクリプションを導入する前に、基本的な決済を動作させます
  2. Stripe Checkout を使う:ホスト型ページで複雑なフローを処理できます
  3. Webhook を徹底的にテストする:CLI を使ってすべてのイベントをシミュレートします
  4. すべてを記録する:決済インテントとイベントをデータベースで追跡します
  5. 失敗に適切に対処する:リトライロジックとユーザー通知を用意します
  6. テスト環境と本番環境を分ける:キーや環境を決して混在させないでください
  7. Stripe ダッシュボードを監視する:エラーや異議申し立てがないか確認します
  8. Stripe のドキュメントを読む:優れており、内容も充実しています

コメント