关于 stripe
面向使用 TypeScript、React 和 Next.js 的开发者的 Stripe 集成技能,涵盖服务端支付、订阅、Webhook、客户管理、安全与测试。
主要功能
- 01服务端处理支付
- 02使用 Stripe Elements 构建客户端表单
- 03创建和取消订阅
- 04验证 Webhook 签名
- 05客户管理与错误处理
- 06测试模式和 Webhook 测试
适用场景
- 01在 Next.js 中构建支付流程
- 02添加订阅生命周期处理
- 03处理 Stripe 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,
});
创建支付意向
// 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