Suscripciones Stripe con Claude Code
Aprenda sobre suscripciones Stripe usando Claude Code. Incluye consejos practicos y ejemplos de codigo.
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 gratuito: Hoja de trucos de Claude Code en 5 minutos
Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.
Cuidamos tus datos personales y nunca enviamos spam.
Sobre el autor
Masa
Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.
Artículos relacionados
7 comprobaciones antes de publicar cada día un artículo multilingüe sobre Claude Code
Una lista práctica para publicar artículos multilingües sobre Claude Code todos los días sin olvidar idiomas, romper CTAs ni dejar páginas antiguas en producción.
Que es Codex Automations y como dejar que la IA gestione contenido mientras duermes
Guia practica para usar Codex Automations en analitica, articulos, CTA, despliegue y monetizacion.
Claude Code × GCP Cloud Functions Guía Completa | Desarrollo Serverless Ultrarrápido
Optimiza GCP Cloud Functions con Claude Code. Implementa triggers HTTP/Pub/Sub/Firestore, pruebas locales y automatización de despliegues con ejemplos de código reales de la experiencia de Masa.