stripe について
TypeScript、React、Next.jsを使う開発者向けのStripe連携スキルです。サーバー側の決済、サブスクリプション、Webhook、顧客管理、セキュリティ、テストを扱います。
主な機能
- 01サーバー側での決済処理
- 02Stripe Elementsによるクライアントフォーム
- 03サブスクリプションの作成とキャンセル
- 04Webhook署名の検証
- 05顧客管理とエラー処理
- 06テストモードとWebhookのテスト
活用シーン
- 01Next.jsでの決済フロー構築
- 02サブスクリプションのライフサイクル対応
- 03Stripe Webhookイベントの処理
- 04決済とエラーシナリオのテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill stripeインストール後、Claude Code で/stripeを入力して読み込まれたか確認します。
詳細
Stripe 連携
安全な決済ソリューションを構築するための、Stripe 決済連携、TypeScript、React、Next.js のエキスパートです。
基本原則
- 決済は常にサーバー側で処理してください
- Stripe の最新の API バージョンを使用してください
- 適切なエラーハンドリングを実装してください
- PCI 準拠のベストプラクティスに従ってください
- 信頼性の高いイベント処理には Webhook を使用してください
サーバー側のセットアップ
Stripe クライアントの設定
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
typescript: true,
});
Payment Intent の作成
// app/api/create-payment-intent/route.ts
import { NextResponse } from 'next/server';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) {
try {
const { amount, currency = 'usd' } = await request.json();
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
automatic_payment_methods: { enabled: true },
});
return NextResponse.json({
clientSecret: paymentIntent.client_secret,
});
} catch (error) {
return NextResponse.json(
{ error: 'Payment intent creation failed' },
{ status: 500 }
);
}
}
クライアント側の連携
Stripe Elements のセットアップ
'use client';
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export function StripeProvider({ children }: { children: React.ReactNode }) {
return (
<Elements stripe={stripePromise}>
{children}
</Elements>
);
}
決済フォームコンポーネント
'use client';
import { useState } from 'react';
import {
PaymentElement,
useStripe,
useElements,
} from '@stripe/react-stripe-js';
export function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) return;
setIsLoading(true);
setError(null);
const { error: submitError } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${window.location.origin}/payment/success`,
},
});
if (submitError) {
setError(submitError.message ?? 'Payment failed');
}
setIsLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
{error && <div className="error">{error}</div>}
<button type="submit" disabled={!stripe || isLoading}>
{isLoading ? 'Processing...' : 'Pay Now'}
</button>
</form>
);
}
サブスクリプション管理
サブスクリプションの作成
export async function createSubscription(
customerId: string,
priceId: string
): Promise<Stripe.Subscription> {
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 subscription;
}
サブスクリプションのキャンセル
export async function cancelSubscription(
subscriptionId: string
): Promise<Stripe.Subscription> {
return stripe.subscriptions.cancel(subscriptionId);
}
Webhook の処理
// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export async function POST(request: Request) {
const body = await request.text();
const signature = headers().get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
} catch (error) {
return new Response('Webhook signature verification failed', {
status: 400,
});
}
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object as Stripe.PaymentIntent;
await handlePaymentSuccess(paymentIntent);
break;
case 'customer.subscription.created':
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionCreated(subscription);
break;
case 'customer.subscription.deleted':
await handleSubscriptionCanceled(event.data.object);
break;
case 'invoice.payment_failed':
await handlePaymentFailed(event.data.object);
break;
}
return new Response('Webhook processed', { status: 200 });
}
顧客管理
export async function createOrRetrieveCustomer(
email: string,
userId: string
): Promise<Stripe.Customer> {
const existingCustomers = await stripe.customers.list({
email,
limit: 1,
});
if (existingCustomers.data.length > 0) {
return existingCustomers.data[0];
}
return stripe.customers.create({
email,
metadata: { userId },
});
}
セキュリティのベストプラクティス
- シークレットキーをクライアント側に決して公開しないでください
- Webhook の署名を必ず検証してください
- 重要な操作には冪等性キーを使用してください
- 適切なエラーハンドリングを実装してください
- デバッグ用に決済イベントをログに記録してください
- 開発時には Stripe のテストモードを使用してください
エラーハンドリング
try {
const paymentIntent = await stripe.paymentIntents.create({...});
} catch (error) {
if (error instanceof Stripe.errors.StripeCardError) {
// Card was declined
console.error('Card declined:', error.message);
} else if (error instanceof Stripe.errors.StripeInvalidRequestError) {
// Invalid parameters
console.error('Invalid request:', error.message);
} else {
// Other errors
console.error('Stripe error:', error);
}
}
テスト
- Stripe のテストモードとテスト用カード番号を使用してください
- Stripe CLI で Webhook イベントをテストしてください
- 適切なエラーシナリオを実装してください
- サブスクリプションのライフサイクルイベントをテストしてください
コメント
Markdown 対応