支付

Nuxt 模板的多提供商结账与 Webhook —— Creem、Stripe 与 ZPay 演示。

支付是一套可在其上构建的端到端计费流程。一个提供商工厂仅在某提供商的环境变量 齐备时才注册它。CreemStripe 是可由环境变量选择的提供商; ZPay 是面向中国(支付宝)的演示提供商,拥有自己的 webhook。结账、订单与计费页面、订阅状态,以及 Creem/Stripe 的 webhook(含订阅生命周期 与退款)都基于真实数据运行。

选择一个提供商

提供商工厂

server/payment/index.ts 暴露 getPaymentProviderFactory()(单例)。工厂仅在某 提供商的环境变量齐备时才注册它:

  • ZPay:当 NUXT_ZPAY_URLNUXT_ZPAY_MERCH_IDNUXT_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.tsZPay 演示收费示例:单个硬编码产品 + amountCNY;上线前请替换

app/config/payment-config.ts 是创建路由所记录金额/币种的权威来源。 shared/config/payment.ts 只是 ZPay 流程使用的样例 —— 见 ZPay 金额注意事项

结账流程

创建订单

POST /api/payment/createserver/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 路径

提供商路径与方法校验
CreemPOST /api/payment/notify/creem对原始请求体做 HMAC-SHA256,与 creem-signature 请求头比较(NUXT_CREEM_WEBHOOK_SECRET
StripePOST /api/payment/notify/stripe对原始请求体执行 constructEvent,与 stripe-signature 请求头比较(NUXT_STRIPE_WEBHOOK_SECRET
ZPayGET /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)已认证并限定到当前登录用户。

相关页面

On this page