Creem
TanStack Start 模板的 Creem 结账与 Webhook 提供商。
Creem 是默认提供商。它在
src/lib/payment/providers/creem.ts 中实现共享的
PaymentProvider 接口:创建托管结账、校验 webhook
签名,并处理若干生命周期事件。
配置
| 变量 | 位置 | 用途 |
|---|---|---|
VITE_PAYMENT_PROVIDER | 构建时 | 设为 creem(默认)以选择该提供商。 |
CREEM_API_KEY | Worker 密钥 | 创建结账的服务端密钥。 |
CREEM_SERVER_IDX | Worker 密钥 | 选择 Creem 服务器(沙箱 vs. 正式)。默认 0。 |
CREEM_WEBHOOK_SECRET | Worker 密钥 | 校验 webhook 签名的 HMAC 密钥。 |
VITE_CREEM_PRODUCT_PRO_MONTHLY / _PRO_YEARLY / _LIFETIME | 构建时 | 三个方案的公开 Creem 产品 ID。 |
用 wrangler secret put CREEM_API_KEY(及其余项)设置服务端取值。见
环境变量。
价格 vs. 产品 ID
src/config/price-config.ts 保存展示价格。每个方案的 productId 来自某个
VITE_CREEM_PRODUCT_* 变量,那才是 Creem 实际收费所依据的。展示金额与 Creem 产品
彼此独立 —— 需你自行保持一致。
结账
createCheckout 为请求的 productId 构建 Creem 结账并返回其 URL。创建路由将浏览器
导向该地址,成功 URL 为 {origin}/pay-success?orderId={orderNo}。
Webhook
- 路径:
POST /api/payment/notify/creem(由共享的/api/payment/notify/$provider路由提供)。 - 签名: 使用
CREEM_WEBHOOK_SECRET对原始请求体做 HMAC-SHA256,并与十六进制的creem-signature请求头以恒定时间比较。缺少/无效签名时路由返回400。 - 已处理事件:
checkout.completed—— 将匹配的payment行标记为success,并保存 Creem 的 订单/客户 ID。- 订阅生命周期 ——
subscription.active、subscription.paid、subscription.update、subscription.trialing、subscription.paused、subscription.past_due、subscription.scheduled_cancel、subscription.canceled与subscription.expired会将用户的subscription行 upsert 到对应的状态、周期与取消时间戳。 refund.created—— 将匹配的payment行标记为refunded。
本地测试
- 使用 Creem 沙箱服务器(
CREEM_SERVER_IDX)与沙箱产品 ID。 - 用隧道工具(如
cloudflared/ngrok)暴露本地 webhook,并在 Creem 中注册https://<tunnel>/api/payment/notify/creem。 - 确认在
checkout.completed后payment行翻转为success。
生产注意事项
- 切换到正式 Creem 服务器与正式产品 ID(
VITE_*ID 需重新构建才能生效)。 - webhook 可能被重试;处理器不具备幂等性,若你为其增加非终态副作用,请添加事件 ID 去重。
验证
- 定价 CTA 创建结账并跳转到 Creem。
- 在沙箱支付后,成功页显示该订单。
- Webhook 将
payment行标记为success。 - 订阅类产品会创建/更新
subscription行。
