支付
如何使用支付
学习如何在 NEXTDEVKIT 中使用 Stripe 和 Creem 的支付 API
🔧 支付提供商架构
NEXTDEVKIT 使用提供商模式来支持多个支付系统。Stripe 和 Creem 都实现相同的接口,使在提供商之间切换变得容易。
提供商接口
export interface PaymentProvider {
createCheckoutLink(params: CreateCheckoutLinkParams): Promise<string>;
createCustomerPortalLink(params: CreatePortalLinkParams): Promise<string>;
handleWebhook(payload: string, signature: string): Promise<void>;
}
Stripe 提供商实现
import Stripe from 'stripe';
import { PaymentProvider, CreateCheckoutLinkParams } from '@/payment/types';
export class StripeProvider implements PaymentProvider {
private stripe: Stripe;
constructor() {
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
});
}
async createCheckoutLink(params: CreateCheckoutLinkParams): Promise<string> {
//...代码的其余部分
return session.url!;
}
async createCustomerPortalLink(params: CreatePortalLinkParams): Promise<string> {
//...代码的其余部分
return session.url!;
}
async handleWebhook(payload: string, signature: string): Promise<void> {
const event = this.stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
switch (event.type) {
case 'checkout.session.completed':
await this.handleCheckoutCompleted(event.data.object);
break;
case 'customer.subscription.created':
await this.handleSubscriptionCreated(event.data.object);
break;
case 'customer.subscription.updated':
await this.handleSubscriptionUpdated(event.data.object);
break;
case 'customer.subscription.deleted':
await this.handleSubscriptionDeleted(event.data.object);
break;
default:
console.log(`未处理的事件类型:${event.type}`);
}
}
}
Creem 提供商实现
import { createHmac } from "node:crypto";
import { PaymentProvider, CreateCheckoutLinkParams } from '@/payment/types';
export class CreemProvider implements PaymentProvider {
async createCheckoutLink(params: CreateCheckoutLinkParams): Promise<string> {
//...代码的其余部分
return checkout_url;
}
async createCustomerPortalLink(params: CreatePortalLinkParams): Promise<string> {
//...代码的其余部分
return customer_portal_link;
}
async handleWebhook(payload: string, signature: string): Promise<void> {
// 验证 webhook 签名
//...代码的其余部分
const event = JSON.parse(payload);
switch (event.eventType) {
case "checkout.completed":
await this.handleOneTimePayment(event);
break;
case "subscription.active":
await this.handleSubscriptionActive(event);
break;
case "subscription.trialing":
await this.handleSubscriptionTrialing(event);
break;
case "subscription.canceled":
case "subscription.expired":
await this.handleSubscriptionCanceled(event);
break;
default:
console.log(`未处理的 Creem 事件:${event.eventType}`);
}
}
}
🔄 Webhooks
Stripe Webhook 处理器
import { getPaymentProvider } from '@/payment/providers';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const payload = await request.text();
const signature = request.headers.get('stripe-signature')!;
try {
const paymentProvider = getPaymentProvider();
await paymentProvider.handleWebhook(payload, signature);
return NextResponse.json({ received: true });
} catch (error) {
console.error('Stripe webhook 错误:', error);
return NextResponse.json(
{ error: 'Webhook 处理程序失败' },
{ status: 400 }
);
}
}
Creem Webhook 处理器
import { getPaymentProvider } from '@/payment/providers';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const payload = await request.text();
const signature = request.headers.get('creem-signature')!;
try {
const paymentProvider = getPaymentProvider();
await paymentProvider.handleWebhook(payload, signature);
return NextResponse.json({ success: true });
} catch (error) {
console.error('Creem webhook 错误:', error);
return NextResponse.json(
{ error: 'Webhook 处理程序失败' },
{ status: 400 }
);
}
}
💳 前端使用
创建结账链接
import { createCheckoutLink } from '@/payment/actions';
import { PaymentType } from '@/payment/types';
// 在您的 React 组件中
const handleSubscribe = async (priceId: string) => {
const result = await createCheckoutLink({
type: PaymentType.SUBSCRIPTION,
priceId: priceId,
redirectUrl: window.location.origin + "/app/dashboard",
});
if (result.data?.checkoutUrl) {
window.location.href = result.data.checkoutUrl;
}
};
// 与不同提供商的使用
// 相同的代码对 Stripe 和 Creem 都有效!
<button onClick={() => handleSubscribe("price_monthly")}>
月付订阅
</button>
客户门户访问
import { createCustomerPortal } from '@/payment/actions';
const handleManageBilling = async () => {
const result = await createCustomerPortal({
customerId: user.customerId,
redirectUrl: window.location.href,
});
if (result.data?.portalUrl) {
window.location.href = result.data.portalUrl;
}
};
<button onClick={handleManageBilling}>
管理账单
</button>
🎯 下一步
现在您了解了如何使用支付系统:
- 📘 设置 Stripe 集成
- 📗 设置 Creem 集成
- ⚙️ 配置您的定价计划
- 🔐 了解身份验证