上手实践

Stripe 集成与配置

使用 Stripe Checkout 完成支付,并通过 Webhook 最终写入积分。讲解环境变量配置、会话创建、回调处理以及签名 Webhook 的验证与处理。

总览

本模板使用 Stripe Checkout 完成支付,并在成功后将积分写入账本。

  • 页面:/[locale]/pricing 从 TypeScript 配置加载套餐。
  • 创建会话:客户端 POST /api/checkout,随后跳转到 Stripe。
  • 返回站点:成功后跳转到 /api/pay/callback/stripe (GET)。
  • 最终入账:Stripe 通过 Webhook 调用 /api/pay/webhook/stripe (POST),将订单记为已支付并增加积分。

注意:Webhook 才是最终可信来源;回调仅用于页面跳转。


快速开始资源


支付网关 101

什么是支付网关?

支付网关是用于处理和授权电子支付(银行卡、数字钱包、银行转账等)的在线中介服务,相当于线下读卡器的“在线版本”。它负责在互联网上安全地校验、批准或拒绝交易。

到 2025 年,全球电商销售额预计接近 7.4 万亿美元。要抓住增长红利,商家需要提供顺畅、安全的结账体验,以提升转化并建立客户信任。

工作原理(概览)

  1. 客户下单:在结账页输入支付信息。
  2. 加密与传输:站点加密信息并发送至支付网关。
  3. 转发交易:网关将加密信息转给支付处理方。
  4. 与发卡行通讯:处理方将交易明细发给发卡行/银行进行授权。
  5. 结果返回:银行基于资金与风控审核,批准或拒绝。
  6. 回传链路:结果经由处理方返回到支付网关。
  7. 完成交易:若批准,用户收到成功确认;若拒绝,提示失败原因。
  8. 资金结算:通过收单行进行清算后,资金入账到商户银行账户(具体时间取决于合作方设置)。

整个过程自动化完成,通常只需数秒。

如何将支付网关集成到网站

  1. 选择网关:考虑手续费、支持的支付方式、安全性与平台兼容性。
  2. 商户账户:部分网关(如 Stripe)已内置商户功能;也有需要单独申请的。
  3. 获取 API Key:用于让你的网站/应用与网关安全通信。
  4. 完成集成:可用插件(Shopify/WooCommerce)或自定义代码。Stripe 的 API 对开发者十分友好。
  5. 沙箱测试:在测试环境全面验证支付流程。
  6. 上线:切换到正式密钥并开始监控。

接口说明

  • /api/checkout (POST):校验 src/data/pricing.ts 的计划,创建 Checkout 会话,保存订单 created
  • /api/pay/callback/stripe (GET):Stripe 成功页的跳转目标;不再做入账逻辑。
  • /api/pay/webhook/stripe (POST):校验签名,处理 checkout.session.completed,完成入账与积分增加。

积分流程

  1. 从 Session 的 metadata 读取 order_no
  2. 更新订单状态为 paid 并记录支付详情。
  3. credits 表插入正数记录(order_pay)。

必要环境变量

STRIPE_PRIVATE_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_WEB_URL=http://localhost:3000
NEXT_PUBLIC_PAY_CANCEL_URL=/pricing
NEXT_PUBLIC_PAY_SUCCESS_URL=/pricing
NEXT_PUBLIC_PAY_FAIL_URL=/pricing

本地 Webhook 调试

stripe login
stripe listen --forward-to localhost:3000/api/pay/webhook/stripe
# 复制输出的 whsec 到 .env
stripe trigger checkout_session_completed

提示:触发 payment_intent.succeeded 不会被当前实现处理,建议使用 checkout_session_completed


试运行

  1. 注册并登录。
  2. 访问 /[locale]/pricing 选择任一套餐。
  3. 使用测试卡 4242 4242 4242 4242 完成支付。
  4. Webhook 会完成订单入账并增加积分。
  5. /[locale]/credits-test 验证余额变化。

Stripe 集成指南(高阶概览)

遵循以下步骤即可快速把 Stripe 集成到你的网站:

  1. 创建 Stripe 账户:填写企业信息与结算银行账户。
  2. 获取 API 密钥:在 Dashboard → Developers 查看 publishable/secret key。
  3. 安装 SDK:使用 Stripe 官方库(JavaScript/TypeScript、Ruby、Python 等)。
  4. 前端集成:使用 Stripe.js 与内置 UI(Elements/Payment Element)或平台插件,安全采集卡片信息。
  5. 服务端端点:用 secret key 初始化 Stripe,提供创建 PaymentIntent 或 Checkout Session 的接口。
  6. 采集支付信息:尽量不在你的服务器保存原始卡数据,交给 Stripe 组件处理。
  7. 提交到服务端:通过 HTTPS 安全传输支付意图与必要参数。
  8. 服务端处理:使用 SDK 创建 PaymentIntent/Checkout Session。
  9. 处理结果:更新数据库并将成功/失败反馈给前端。
  10. 错误与边界:考虑余额不足、卡片过期、3DS 验证、重试等情况。
  11. 全面测试:使用测试模式、Webhook 与测试卡模拟各种场景。
  12. 上线:替换为正式密钥,开启所需支付方式并持续监控。

提示:从官方“入门”文档开始 — https://docs.stripe.com/get-started


实现细节

  • 套餐定义:src/data/pricing.ts(强类型、前后端共用)。
  • handleCheckoutSession 同时支持一次性与订阅(订阅场景从最新账单获取 PaymentIntent)。
  • 可扩展处理 invoice.payment_succeeded 实现订阅续费时自动充值积分。

产品与价格(Stripe)

Stripe 将“售卖的内容”和“定价方式”分离:

  • 产品(prod_...):名称/描述、目录与会计元数据;一个产品可以对应多条价格。
  • 价格(price_...):币种、金额、是否按月/按年循环、试用、税务行为、分级等。订阅绑定到价格。

为何在 Checkout 使用价格 ID(Price ID)

  • 订阅需要明确的计费条款。绑定到 price_... 才能启用续费、按比例结算(proration)、试用和 Stripe Tax。
  • 续费账单行会引用同一 price_...,Webhook 能据此映射到计划并在每个周期自动发放积分。
  • 比“临时内联价格”更安全:把金额与规则放在 Stripe 管理,代码保持稳定。

如何在 Stripe 找到价格 ID

  1. 仪表盘 → 产品 → 选择你的产品。
  2. 在“价格(Prices)”表中,点击该行末尾的省略号(三点) → 复制价格 ID。
  3. 价格 ID 以 price_... 开头;不要与产品 ID(prod_...)混淆。

环境变量(可选但推荐)

# 把计划配置映射到 Stripe 的价格;留空则回退为内联 price_data
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_MONTHLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_MONTHLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_YEARLY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_YEARLY=price_...
# 可选人民币价格
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_MONTHLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_MONTHLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_LAUNCH_YEARLY_CNY=price_...
NEXT_PUBLIC_STRIPE_PRICE_SCALE_YEARLY_CNY=price_...

有/无价格 ID 的行为

  • 若订阅计划配置了 Price ID,Checkout 将直接引用该 price_...
  • 若未配置,Checkout 会使用 src/data/pricing.ts 的金额通过内联 price_data 创建会话(安全回退)。

续费与积分

模板已支持订阅自动续费时的积分发放:

  • Webhook 处理 invoice.payment_succeeded,当且仅当 billing_reason=subscription_cycle 时:
    • 创建当期的续费订单(Order)。
    • 根据计划发放当期积分,过期时间对齐账单周期(+24h 宽限)。
  • 幂等:若同一 (subscription_id, period_start) 已有订单,则跳过。
  • 首次购买仍由 checkout.session.completed 处理;为避免重复发放,非续费账单会被忽略。

提醒:本地测试记得转发 Webhook

本地或非生产环境测试时,务必将 Stripe 事件转发到你的后端,否则支付虽然成功,但订单与积分不会最终入账:

stripe listen --forward-to localhost:3000/api/pay/webhook/stripe

如果使用外网域名或隧道,请将 forward-to 换成对应地址。

计费与订阅

客户门户(Customer Portal)

在 Stripe 后台启用 Customer Portal,并配置可用功能(取消/恢复、更新卡片、查看发票等)。

本模板提供:

  • API:GET /api/billing/portal — 创建门户会话并 302 跳转到 Stripe
  • UI:/:locale/account/billing — “Manage billing” 按钮打开门户

实现位置:

  • API 路由:src/app/api/billing/portal/route.ts
    • 通过登录用户的邮箱查找或创建 Stripe Customer
    • 使用 return_url 返回到 /:locale/account/billing

页面(Server Component):

  • src/app/[locale]/account/billing/page.tsx 链接到 /api/billing/portal?locale=<locale>

催款(支付失败)

Webhook invoice.payment_failed:通过 Resend 发送“更新付款方式”的提示邮件。

  • 模板:src/services/email/templates/payment-failed.tsx
  • 发送函数:sendPaymentFailedEmail()src/services/email/send.ts
  • Webhook:src/app/api/pay/webhook/stripe/route.ts

本地测试(Stripe CLI):

stripe trigger invoice_payment_failed

说明:邮件中包含 /:locale/account/billing 链接,用户可从你的应用打开门户。


其他支付选项

Stripe 只是一个选择。如果你需要加密货币支付或 PayPal 等替代方案,欢迎联系我——我可以根据你的技术栈与目标市场,协助评估并完成对接集成。