支付
Nuxt 模板的多提供商结账与 Webhook —— Creem、Stripe 与 ZPay 演示。
支付是一套可在其上构建的端到端计费流程。一个提供商工厂仅在某提供商的环境变量 齐备时才注册它。Creem 与 Stripe 是可由环境变量选择的提供商; ZPay 是面向中国(支付宝)的演示提供商,拥有自己的 webhook。结账、订单与计费页面、订阅状态,以及 Creem/Stripe 的 webhook(含订阅生命周期 与退款)都基于真实数据运行。
选择一个提供商
提供商工厂
server/payment/index.ts 暴露 getPaymentProviderFactory()(单例)。工厂仅在某
提供商的环境变量齐备时才注册它:
- ZPay:当
NUXT_ZPAY_URL、NUXT_ZPAY_MERCH_ID与NUXT_ZPAY_MERCH_KEY全部设置时。 - Creem:当
NUXT_CREEM_API_KEY设置时。 - Stripe:当
NUXT_STRIPE_API_KEY设置时。
结账流程使用的默认提供商来自 getPaymentProvider() —— 除非
NUXT_PUBLIC_PAYMENT_PROVIDER=stripe,否则为 creem。(ZPay 可通过其自身 webhook
触达,但无法通过 NUXT_PUBLIC_PAYMENT_PROVIDER 选择。)
每个提供商都实现 server/payment/types.ts 中的接口:
interface PaymentProvider {
createPayment(request: CreatePaymentRequest): Promise<CreatePaymentResponse>;
handleCallback(callbackParams: unknown): Promise<CallbackVerificationResult>;
}提供商位于 server/payment/providers/{creem,stripe,zpay}-provider.ts。
配置来源
| 文件 | 驱动 | 说明 |
|---|---|---|
app/config/payment-config.ts | 结账 API 的产品/金额解析 | getPaymentProvider + getPaymentProducts;Creem 或 Stripe ID 来自 runtimeConfig.public |
app/config/price-config.ts | 定价 UI 展示 | Free / Pro(月付 + 年付)/ Lifetime 的文案与金额 |
shared/config/payment.ts | ZPay 演示收费 | 示例:单个硬编码产品 + amountCNY;上线前请替换 |
app/config/payment-config.ts 是创建路由所记录金额/币种的权威来源。
shared/config/payment.ts 只是 ZPay 流程使用的样例 —— 见
ZPay 金额注意事项。
结账流程
创建订单
POST /api/payment/create(server/api/payment/create.post.ts):
- 要求登录(
requireAuth)—— 不支持游客结账(401)。 - 用 Zod 校验
{ productId };邮箱来自会话。 - 通过
getPaymentProduct(productId)解析金额/币种(未知产品返回 400)。 - 生成
orderNo,插入一条payment行,并调用当前提供商的createPayment。 - 返回
{ orderNo, thirdOrderNo, checkoutUrl }。
跳转并轮询
客户端将用户送往提供商的 checkoutUrl,支付后返回
/pay-success?orderId=<orderNo>。该页面可轮询
GET /api/payment/query?orderId=<orderNo>(query.get.ts)查询订单状态。
接收 webhook
提供商调用其对应的 webhook,校验签名并更新 payment(订阅则还更新 subscription)表。
Webhook 路径
| 提供商 | 路径与方法 | 校验 |
|---|---|---|
| Creem | POST /api/payment/notify/creem | 对原始请求体做 HMAC-SHA256,与 creem-signature 请求头比较(NUXT_CREEM_WEBHOOK_SECRET) |
| Stripe | POST /api/payment/notify/stripe | 对原始请求体执行 constructEvent,与 stripe-signature 请求头比较(NUXT_STRIPE_WEBHOOK_SECRET) |
| ZPay | GET /api/payment/notify/zpay | 对排序后的参数 + 商户密钥做 MD5 签名 |
Creem 与 Stripe 处理完整的订阅生命周期与退款(见各自页面)。ZPay 作为演示,只处理其 成功/失败回调。
已接入的部分
- 订单页(
account/order.vue)通过GET /api/payment/orders列出用户真实的payment历史。 - 订阅页(
account/subscription.vue)展示实时的套餐、状态、周期与取消情况。 - 计费页(
setting/billing.vue)通过GET /api/user/billing渲染套餐概要与 计费历史,并将 Stripe 客户链接到 Billing Portal。 - 订单状态查询(
query.get.ts)已认证并限定到当前登录用户。
