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)も必ず확인합시다。
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code 다국어 글을 매일 발행하기 전에 확인할 7가지
누락된 언어, 깨진 CTA, 반영되지 않은 배포를 막기 위해 다국어 Claude Code 글을 매일 발행하기 전에 확인할 체크리스트입니다.
Codex Automations란? 잠자는 동안 AI가 콘텐츠 운영을 처리하게 하는 방법
Codex Automations로 트래픽 분석, 주제 선정, 글 작성, CTA 개선, 배포까지 자동화하는 실전 가이드.
Claude Code × GCP Cloud Functions 완전 가이드 | 서버리스 함수 초고속 개발
Claude Code로 GCP Cloud Functions를 효율화. HTTP/Pub/Sub/Firestore 트리거 구현부터 로컬 테스트·배포 자동화까지, Masa의 실무 경험을 토대로 실제 코드로 해설.