Stripe: Claude Code 활용 가이드
stripe: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.
Stripeサブスクリプション구현をClaude Code로 효율화하기
サブスクリプション課金はSaaSビジネスの根幹です。プラン변경、トライアル、請求관리、Webhook処理など考慮すべき点が多いですが、Claude Code를 활용하면Stripeの모범 사례に沿った구현を효율적으로進められます。
プラン定義とCheckout세션
> Stripeサブスクリプション를 구현해줘。
> Free / Pro / Enterprise の3プランで、
> トライアル期間14日間、プラン변경・취소대응、
> Webhookでの状態동기も含めて。
// src/lib/subscription.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export const PLANS = {
free: { name: 'Free', priceId: null, features: ['基本機能', '3プロジェクト'] },
pro: { name: 'Pro', priceId: process.env.STRIPE_PRO_PRICE_ID!, features: ['全機能', '無制限プロジェクト', '優先サポート'] },
enterprise: { name: 'Enterprise', priceId: process.env.STRIPE_ENT_PRICE_ID!, features: ['全機能', 'SSO', '専任担当', 'SLA'] },
} as const;
export type PlanKey = keyof typeof PLANS;
export async function createSubscription(userId: string, plan: PlanKey) {
if (plan === 'free') throw new Error('Freeプランにサブスクリプションは不要です');
const customer = await findOrCreateCustomer(userId);
const priceId = PLANS[plan].priceId;
const session = await stripe.checkout.sessions.create({
customer: customer.id,
mode: 'subscription',
line_items: [{ price: priceId!, quantity: 1 }],
subscription_data: { trial_period_days: 14 },
success_url: `${process.env.APP_URL}/dashboard?upgraded=true`,
cancel_url: `${process.env.APP_URL}/pricing`,
metadata: { userId, plan },
});
return session.url;
}
Webhook処理
// src/app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from 'next/server';
import Stripe from 'stripe';
import { prisma } from '@/lib/prisma';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: NextRequest) {
const body = await request.text();
const signature = request.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated': {
const subscription = event.data.object as Stripe.Subscription;
await prisma.subscription.upsert({
where: { stripeSubscriptionId: subscription.id },
create: {
stripeSubscriptionId: subscription.id,
stripeCustomerId: subscription.customer as string,
status: subscription.status,
plan: subscription.metadata.plan || 'pro',
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
update: {
status: subscription.status,
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
});
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
await prisma.subscription.update({
where: { stripeSubscriptionId: subscription.id },
data: { status: 'canceled' },
});
break;
}
case 'invoice.payment_failed': {
const invoice = event.data.object as Stripe.Invoice;
// 支払い실패の알림メールを전송
await sendPaymentFailedEmail(invoice.customer as string);
break;
}
}
return NextResponse.json({ received: true });
}
プラン변경処理
// src/lib/subscription.ts(続き)
export async function changePlan(userId: string, newPlan: PlanKey) {
const sub = await prisma.subscription.findFirst({
where: { userId, status: 'active' },
});
if (!sub) throw new Error('アクティブなサブスクリプションがありません');
const stripeSubscription = await stripe.subscriptions.retrieve(
sub.stripeSubscriptionId
);
await stripe.subscriptions.update(sub.stripeSubscriptionId, {
items: [
{
id: stripeSubscription.items.data[0].id,
price: PLANS[newPlan].priceId!,
},
],
proration_behavior: 'create_prorations', // 日割り計算
metadata: { plan: newPlan },
});
}
export async function cancelSubscription(userId: string) {
const sub = await prisma.subscription.findFirst({
where: { userId, status: 'active' },
});
if (!sub) throw new Error('アクティブなサブスクリプションがありません');
// 期間終了時に취소(即時ではない)
await stripe.subscriptions.update(sub.stripeSubscriptionId, {
cancel_at_period_end: true,
});
}
顧客ポータル
Stripeの顧客ポータルを使えば、プラン변경・支払い方法の업데이트・請求書の閲覧をStripe側のUIに任せられます。
export async function createPortalSession(userId: string) {
const customer = await getCustomerByUserId(userId);
const session = await stripe.billingPortal.sessions.create({
customer: customer.id,
return_url: `${process.env.APP_URL}/settings/billing`,
});
return session.url;
}
関連글
決済全般에 대해서는Stripe決済の통합가이드、인증周りは인증機能の구현를 확인하세요.
Stripeの公式가이드(stripe.com/docs/billing)も必ず확인합시다。
#Claude Code
#Stripe
#subscription
#課金
#TypeScript
Related Posts
Use Cases
Use Cases
Claude Code로 리팩토링을 자동화하는 방법
Claude Code를 활용해 코드 리팩토링을 효율적으로 자동화하는 방법을 알아봅니다. 실전 프롬프트와 구체적인 리팩토링 패턴을 소개합니다.
Use Cases
Use Cases
Claude Code로 사이드 프로젝트 개발 속도를 극대화하는 방법 [예제 포함]
Claude Code를 활용해 개인 프로젝트 개발 속도를 획기적으로 높이는 방법을 알아봅니다. 실전 예제와 아이디어부터 배포까지의 워크플로를 포함합니다.
Use Cases
Use Cases
Complete CORS Configuration Guide: Claude Code 활용 가이드
complete cors configuration guide: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.