支付总览
套餐声明、买家流程与账单页
最近更新 2026 年 11 月 3 日
套餐在 src/config/products.ts 声明一次,provider(Stripe 或 Creem)决定怎么收款。站内不存金额:展示价与扣款价都读 provider 的价格目录。
套餐配置
只改 src/config/products.ts 的 getProducts():
ts
{
id: 'pro',
name: m.pricing_pro_name(),
description: m.pricing_pro_description(),
features: [m.pricing_pro_feature_1()],
recommended: true,
prices: { monthly: 'pro_monthly', yearly: 'pro_yearly' },
}id是本站套餐标识。prices的键是本站购买选项 ID(如monthly),值是销售引用,由 provider 解释:
| provider | 销售引用的含义 |
|---|---|
| Stripe | Price 的 lookup_key |
| Creem | 本站自定义引用,用 CREEM_PRODUCTS 映射到 prod_* 商品 ID |
prices为空表示免费套餐:卡片仍渲染按钮,但走注册或进控制台,不进结账。recommended可选;数组顺序就是展示顺序。- 文案在函数调用时求值,不在模块顶层调用
m.*()。
provider 由 websiteConfig.payment.provider 选,可用环境变量 VITE_PAYMENT_PROVIDER 在部署时切换,不用改代码。
买家流程
- 买家在首页
#pricing区块看到价格与购买按钮;该区块卖的是websiteConfig.landingOffer指向的那个商品与购买方式。想卖多套餐,改src/config/products.ts的套餐列表,并用src/components/sections/Pricing.tsx的Pricing组件渲染卡片(每个套餐内切换月付 / 年付 / 买断),把renderCta接到startCheckout上。 - 未登录会先跳登录页,登录后回到价格区再点一次购买。
- 客户端提交
productId、offerId与回跳地址;服务端鉴权、限流、校验回跳地址同源,再按 provider 建托管结账会话。 - 浏览器跳到 provider 的付款页;付款完成回到
/dashboard/billing?checkout=success,取消回到发起购买的那一页。 - 权益由 provider 的 webhook 确认后写入——回跳页本身不发权益。
一个账号最多保留一份未终止的订阅(active、trialing、past_due、unpaid、paused、incomplete 都算)。已安排期末取消的订阅在到期前仍占名额。一次性商品可以重复买,也能与订阅并存,不会取消或接管已有订阅。
账单页
/dashboard/billing 显示:
- 当前订阅:套餐名、状态、金额与周期、续费或结束日期;
- 历史订阅;
- 支付记录:日期、套餐、金额、状态,部分退款单独标出。
「管理订阅」按钮把用户送到 provider 的自助 Portal(取消续费、换支付方式、发票都在那边)。客户 ID 由服务端按登录用户查,客户端不能指定别人的。