如何Integrate Payment Systems (Stripe):Claude Code 实战指南
学习如何integrate payment systems (stripe):Claude Code 实战. 包含实用代码示例和分步指导。
決済システムの实现にClaude Code使用メリット
決済システムは安全と正確性が求められる重要な機能です。Claude Code Stripeの最佳实践に沿った实现を生成し、Webhook処理や错误处理の見落としを防ぎます。
Stripe Checkout通过決済
> Stripe Checkout使用...的サブスクリプション決済实现。
> フリー・プロ・企业级の3プランで。
> 成功・取消時のリダイレクトも配置して。
チェックアウト会话の创建
// src/services/payment-service.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-12-18.acacia',
});
const PLANS = {
pro: {
priceId: process.env.STRIPE_PRO_PRICE_ID!,
name: 'Pro',
},
enterprise: {
priceId: process.env.STRIPE_ENTERPRISE_PRICE_ID!,
name: 'Enterprise',
},
} as const;
type PlanKey = keyof typeof PLANS;
export class PaymentService {
async createCheckoutSession(userId: string, plan: PlanKey) {
// 既存のStripe顧客を搜索、如果不创建
let customer = await this.findOrCreateCustomer(userId);
const session = await stripe.checkout.sessions.create({
customer: customer.id,
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: PLANS[plan].priceId,
quantity: 1,
},
],
success_url: `${process.env.APP_URL}/payment/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/pricing`,
metadata: { userId, plan },
subscription_data: {
trial_period_days: 14,
},
});
return { url: session.url };
}
async createPortalSession(userId: string) {
const customer = await this.getCustomerByUserId(userId);
const session = await stripe.billingPortal.sessions.create({
customer: customer.id,
return_url: `${process.env.APP_URL}/settings/billing`,
});
return { url: session.url };
}
private async findOrCreateCustomer(userId: string) {
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user) throw new Error('User not found');
if (user.stripeCustomerId) {
return stripe.customers.retrieve(user.stripeCustomerId) as Promise<Stripe.Customer>;
}
const customer = await stripe.customers.create({
email: user.email,
metadata: { userId },
});
await prisma.user.update({
where: { id: userId },
data: { stripeCustomerId: customer.id },
});
return customer;
}
private async getCustomerByUserId(userId: string) {
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user?.stripeCustomerId) throw new Error('No Stripe customer found');
return stripe.customers.retrieve(user.stripeCustomerId) as Promise<Stripe.Customer>;
}
}
APIルートの配置
// src/app/api/checkout/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { PaymentService } from '@/services/payment-service';
import { getSession } from '@/lib/auth';
const paymentService = new PaymentService();
export async function POST(req: NextRequest) {
const session = await getSession();
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { plan } = await req.json();
if (!['pro', 'enterprise'].includes(plan)) {
return NextResponse.json({ error: 'Invalid plan' }, { status: 400 });
}
const checkout = await paymentService.createCheckoutSession(session.userId, plan);
return NextResponse.json(checkout);
}
Webhook処理
> Stripeのwebhook处理程序ー实现。
> サブスクリプションの创建・更新・取消を処理して。
> 署名验证も实现して。
// src/app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from 'next/server';
import Stripe from 'stripe';
import { prisma } from '@/lib/db';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: NextRequest) {
const body = await req.text();
const signature = req.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
console.error('Webhook signature verification failed');
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object as Stripe.Checkout.Session;
await handleCheckoutCompleted(session);
break;
}
case 'customer.subscription.updated': {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionUpdated(subscription);
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionCanceled(subscription);
break;
}
case 'invoice.payment_failed': {
const invoice = event.data.object as Stripe.Invoice;
await handlePaymentFailed(invoice);
break;
}
}
return NextResponse.json({ received: true });
}
async function handleCheckoutCompleted(session: Stripe.Checkout.Session) {
const userId = session.metadata?.userId;
if (!userId) return;
await prisma.user.update({
where: { id: userId },
data: {
plan: session.metadata?.plan || 'pro',
stripeSubscriptionId: session.subscription as string,
subscriptionStatus: 'active',
},
});
}
async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
const user = await prisma.user.findFirst({
where: { stripeSubscriptionId: subscription.id },
});
if (!user) return;
await prisma.user.update({
where: { id: user.id },
data: { subscriptionStatus: subscription.status },
});
}
async function handleSubscriptionCanceled(subscription: Stripe.Subscription) {
const user = await prisma.user.findFirst({
where: { stripeSubscriptionId: subscription.id },
});
if (!user) return;
await prisma.user.update({
where: { id: user.id },
data: {
plan: 'free',
subscriptionStatus: 'canceled',
stripeSubscriptionId: null,
},
});
}
async function handlePaymentFailed(invoice: Stripe.Invoice) {
const customerId = invoice.customer as string;
const user = await prisma.user.findFirst({
where: { stripeCustomerId: customerId },
});
if (!user) return;
// 支払い失败の通知メールを发送
await sendPaymentFailedEmail(user.email);
}
料金表组件
function PricingTable() {
const handleCheckout = async (plan: string) => {
const res = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan }),
});
const { url } = await res.json();
window.location.href = url;
};
return (
<div className="grid gap-6 md:grid-cols-3">
<PricingCard
name="Free"
price="$0"
features={['基本機能', '月5プロジェクト']}
buttonText="現在のプラン"
disabled
/>
<PricingCard
name="Pro"
price="$1,980/月"
features={['全機能', '無制限プロジェクト', '優先サポート']}
buttonText="Proにアップグレード"
onSelect={() => handleCheckout('pro')}
highlighted
/>
<PricingCard
name="Enterprise"
price="$9,800/月"
features={['全機能', 'SSO', '専任サポート', 'SLA保証']}
buttonText="お問い合わせ"
onSelect={() => handleCheckout('enterprise')}
/>
</div>
);
}
总结
借助 Claude Code,Stripe決済の集成をチェックアウトからWebhook処理まで安全かつ高效地实现可以。決済は安全に直結するため、コードレビューは必ず行いましょう。项目の決済方針はCLAUDE.mdに記述しておくことをお勧めします。コード品質の維持には重构の自动化も活用して请。
Claude Code 的详细信息请参阅Anthropic官方文档。Stripeの实现指南はStripe官方文档也请参阅。
#Claude Code
#Stripe
#payments
#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 实战. 包含实用技巧和代码示例。