stripe-integration について
stripe-integration は、Stripe の決済やサブスクリプションを実装するための開発スキルです。セキュリティ、Webhook、テスト、顧客ライフサイクル管理の指針を提供します。
主な機能
- 01決済とチェックアウトのフロー
- 02サブスクリプション課金パターン
- 03Webhook の設定とイベント処理
- 04セキュリティと冪等性の指針
- 05Stripe CLI によるテスト手順
活用シーン
- 01アプリへの Stripe 決済の追加
- 02サブスクリプション課金の構築
- 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});
});
対応が必須のイベント:
payment_intent.succeeded- 1 回限りの決済が成功したpayment_intent.payment_failed- 決済が失敗したcustomer.subscription.created- 新しいサブスクリプションが作成されたcustomer.subscription.updated- サブスクリプションが変更されたcustomer.subscription.deleted- サブスクリプションが解約されたinvoice.payment_succeeded- 継続課金が成功した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'
});
リソース
公式ドキュメント:
- https://stripe.com/docs
- https://stripe.com/docs/webhooks
- https://stripe.com/docs/billing/subscriptions/overview
テスト:
セキュリティ:
ヒント
- シンプルに始める:サブスクリプションを導入する前に、基本的な決済を動作させます
- Stripe Checkout を使う:ホスト型ページで複雑なフローを処理できます
- Webhook を徹底的にテストする:CLI を使ってすべてのイベントをシミュレートします
- すべてを記録する:決済インテントとイベントをデータベースで追跡します
- 失敗に適切に対処する:リトライロジックとユーザー通知を用意します
- テスト環境と本番環境を分ける:キーや環境を決して混在させないでください
- Stripe ダッシュボードを監視する:エラーや異議申し立てがないか確認します
- Stripe のドキュメントを読む:優れており、内容も充実しています
コメント
Markdown 対応