项目中关于Creem支付相关配置
发给 ChatGPT / Claude 等 AI,可更快消化本页并上手上站。
朋友,可能你看到这篇文章的时候和我写的时候相隔很久,文章中所说可能已经不是对应官网的最新信息,需要你将文章发给AI让其确认是否还可行后再去做,感谢理解
接入 Creem 支付与订阅指南
本指南介绍如何在基于 TanStack 模板的 SaaS 应用中接入与配置 Creem 支付,支持一次性点数购买与周期性自动续费订阅。
写在前面

申请提示:若 Creem 处于「邀请制 + 排队」模式,可在注册时说明自身 SaaS 业务背景,或直接向 Creem 团队发送邮件申请:
We are building a SaaS product targeting global users.
Currently looking for a reliable payment solution to replace Stripe.
Expected monthly volume: $3k-$10k initially
Tech stack: TanStack Start / Next.js + Supabase / PostgreSQL
We are ready to integrate immediately once approved.
出海收单方案:对于尚未办理个人香港银行卡或海外实体的独立开发者,采用 Creem + Wise + 国内借记卡 是目前低门槛实现收付款资金闭环的可靠组合;若已有境外卡,则可直接绑定提现。
未办理港卡看过来,试试 Creem + Wise 组合
1. 开通 Creem 商户与环境准备
登录 Creem 控制台 创建商户账户(无需信用卡即可开启沙盒测试)。
Creem 原生支持 沙盒测试模式(Sandbox) 与 生产环境(Production):
- 沙盒模式(Sandbox):API 交互地址为
https://test-api.creem.io,API 密钥通常以creem_test_开头。 - 生产模式(Production):API 交互地址为
https://api.creem.io,API 密钥以creem_live_开头。



2. 获取 API 密钥与配置管理后台
在系统架构中,所有支付凭据均通过 管理后台(/admin/settings -> Payment 支付设置) 统一持久化管理,环境变量仅作为系统底层兜底。
2.1 获取 API Key
- 进入 Creem 控制台 > Developers > API & Webhooks。
- 点击新建 API Key,复制生成的密钥(测试环境以
creem_test_开头,生产环境以creem_live_开头)。

2.2 填写管理后台配置

进入系统管理后台 http://localhost:3000/admin/settings(生产环境对应 https://你的域名/admin/settings),切换至 Payment(支付) 标签页,找到 Creem 配置卡片:
| 配置项 | 键名 | 说明 |
|---|---|---|
| Enable Creem | creem_enabled |
开关,设为启用(开启后前台支付弹窗才可选该渠道) |
| Environment | creem_environment |
选择 Sandbox(本地开发测试)或 Production(正式上线) |
| API Key | creem_api_key |
填入 Creem 控制台获取的 creem_test_... 或 creem_live_... |
| Signing Secret | creem_signing_secret |
Webhook 验签密钥,格式为 whsec_...(见下文 Webhook 章节) |
| Product IDs Mapping | creem_product_ids_mapping |
本地商品 ID 与 Creem 产品 ID 映射(JSON 对象,必需) |
| Test amount (cents) | creem_test_amount |
可选。填入测试扣费金额(如填 1 则扣款 $0.01,留空按实际价格) |
| Default Provider | default_payment_provider |
设置系统默认首选的支付渠道(如设为 creem) |
3. 在 Creem 控制台创建产品
Creem 支持 一次性商品(One-time) 与 周期性订阅(Subscription)。请在 Creem 控制台 > Commerce > Products 创建与业务对应的产品:

3.1 一次性充值积分包(示例)
- Starter Pack:
- 名称:
Starter Pack - 计费模式:
One-time - 价格:
$9.90(USD),对应本地商品starter_lifetime - 保存并记录产品 ID(以
prod_开头,如prod_starter_xxx)
- 名称:
- Pro Pack:
- 名称:
Pro Pack - 计费模式:
One-time - 价格:
$29.90(USD),对应本地商品pro_lifetime - 保存并记录产品 ID(如
prod_pro_yyy)
- 名称:
- Ultra Pack:
- 名称:
Ultra Pack - 计费模式:
One-time - 价格:
$69.90(USD),对应本地商品enterprise_lifetime - 保存并记录产品 ID(如
prod_ultra_zzz)
- 名称:
3.2 周期订阅计划(示例)
- Lite Monthly:
- 名称:
Lite Monthly - 计费模式:
Recurring(周期:every-month) - 价格:
$9.90(USD/月),对应本地商品starter_monthly - 保存并记录产品 ID(如
prod_sub_lite_monthly_xxx)
- 名称:
- Pro Monthly:
- 名称:
Pro Monthly - 计费模式:
Recurring(周期:every-month) - 价格:
$19.90(USD/月),对应本地商品pro_monthly - 保存并记录产品 ID(如
prod_sub_pro_monthly_yyy)
- 名称:
- Ultra Monthly:
- 名称:
Ultra Monthly - 计费模式:
Recurring(周期:every-month) - 价格:
$39.90(USD/月),对应本地商品enterprise_monthly - 保存并记录产品 ID(如
prod_sub_ultra_monthly_zzz)
- 名称:
4. 定价目录与产品 ID 映射(核心代码配置)
4.1 定价目录:服务端唯一可信源 (src/config/pricing.ts)
客户端提交的金额、积分和计划信息服务端一律忽略。所有结账均以 src/config/pricing.ts 中的 pricingCatalog 为准:
const pricingCatalogRaw: Record<string, PricingProduct> = {
// 周期订阅型(例如 Lite 按月订阅)
starter_monthly: {
productId: 'starter_monthly',
productName: 'Lite',
planName: 'Lite',
description: 'Lite Monthly',
type: PaymentType.SUBSCRIPTION,
priceInCents: 990,
currency: 'usd',
credits: 2000,
creditsValidDays: 30,
monthlyDisplayPrice: '$9.9',
originalPrice: '$14.9',
copyKey: 'lite',
buttonKey: 'subscribe',
creditsKey: 'month',
plan: { name: 'Lite', interval: PaymentInterval.MONTH, intervalCount: 1 },
},
// 热门周期订阅(例如 Pro 按月订阅)
pro_monthly: {
productId: 'pro_monthly',
productName: 'Pro',
planName: 'Pro',
description: 'Pro Monthly',
featured: true,
badgeKey: 'popular',
type: PaymentType.SUBSCRIPTION,
priceInCents: 1990,
currency: 'usd',
credits: 4800,
creditsValidDays: 30,
monthlyDisplayPrice: '$19.9',
originalPrice: '$29.9',
copyKey: 'pro',
buttonKey: 'subscribe',
creditsKey: 'month',
plan: { name: 'Pro', interval: PaymentInterval.MONTH, intervalCount: 1 },
},
// 一次性积分包(例如 Starter Pack)
starter_lifetime: {
productId: 'starter_lifetime',
productName: 'Starter Pack',
planName: 'Starter Pack',
description: 'Starter credit pack',
type: PaymentType.ONE_TIME,
priceInCents: 990,
currency: 'usd',
credits: 1500,
creditsValidDays: 365,
monthlyDisplayPrice: '$9.9',
copyKey: 'starter_pack',
buttonKey: 'buy_once',
creditsKey: 'pack',
},
};
4.2 配置产品映射 (creem_product_ids_mapping)
在系统发起 Creem Checkout 会话时,服务端 src/modules/payment/service.ts 会自动读取管理后台配置的映射字典,将本地的 productId 转化为 Creem 真实的 prod_xxx:
在后台 Product IDs Mapping(creem_product_ids_mapping)字段中填入 JSON 映射字典:
{
"starter_monthly": "prod_sub_lite_monthly_xxx",
"pro_monthly": "prod_sub_pro_monthly_xxx",
"enterprise_monthly": "prod_sub_ultra_monthly_xxx",
"starter_yearly": "prod_sub_lite_yearly_xxx",
"pro_yearly": "prod_sub_pro_yearly_xxx",
"enterprise_yearly": "prod_sub_ultra_yearly_xxx",
"starter_lifetime": "prod_starter_pack_xxx",
"pro_lifetime": "prod_pro_pack_xxx",
"enterprise_lifetime": "prod_ultra_pack_xxx"
}
5. 配置 Webhook 回调(防止掉单与自动续费)
为什么必须配置 Webhook:
- 防止掉单(兜底保障):用户在收银台付款成功后若直接关闭网页或网络异常,前端同步跳回不会触发。服务端通过 Webhook 异步解耦处理,确保订单顺利履约入账。
- 处理订阅自动续费:订阅用户按月/年静默扣款成功时,Creem 会通过 Webhook 通知系统延长订阅有效期并下发新周期额度。
- 订阅状态与计划变更同步:当用户在管理中心取消续订、恢复续订或扣款逾期时,由 Webhook 实时同步本地状态。
- 退款自动冲减:当在 Creem 控制台发起退款时,系统自动识别原交易并按比例安全扣减尚未消耗的权益额度。
- 安全防伪验签:请求头附带
creem-signature,服务端基于 Web Crypto API 使用creem_signing_secret计算 HMAC-SHA256 签名,并配合时序安全比较(timingSafeEqual)杜绝伪造回调。
5.1 系统支持的核心 Webhook 事件清单
系统在 src/core/payment/creem.ts 与 src/modules/payment/service.ts 中针对以下核心事件进行了严格的状态机处理与业务履约:
| Creem 事件类型 | 内部事件映射 | 业务履约逻辑 |
|---|---|---|
checkout.completed |
CHECKOUT_SUCCESS |
初次结账成功:一次性点数包即时发放入账;订阅首期扣款成功并激活用户订阅。 |
subscription.paid |
PAYMENT_SUCCESS |
订阅自动续费成功:延长用户订阅有效期,发放新周期配额额度,记录对账流水。 |
subscription.update |
SUBSCRIBE_UPDATED |
订阅变更:方案升降级或订阅属性更新,同步最新周期与方案。 |
subscription.active |
SUBSCRIBE_UPDATED |
订阅激活/恢复:订阅恢复正常状态时同步本地订阅状态为生效。 |
subscription.paused |
SUBSCRIBE_UPDATED |
订阅暂停:同步订阅暂停状态。 |
subscription.scheduled_cancel |
SUBSCRIBE_UPDATED |
预定到期取消:用户设置了到期不再续费,系统标记 cancel_at_period_end,当前权益保留至期末。 |
subscription.past_due |
SUBSCRIBE_UPDATED |
扣款逾期:续费扣款失败进入宽限期,系统记录逾期告警。 |
subscription.canceled |
SUBSCRIBE_CANCELED |
订阅终止/取消:订阅期满或立即取消后正式停权,将本地订阅置为已取消。 |
refund.created |
PAYMENT_REFUNDED |
退款创建:自动匹配关联订单,按退款比例安全冲减用户积分并记录退款账本流水。 |
💡 提示:未在上述映射中的事件(如
subscription.expired、subscription.trialing、dispute.created)会被系统安全忽略(抛出WebhookIgnoredError),接口自动返回 HTTP 200 确认送达。
5.2 设置 Webhook 步骤
- 登录 Creem 控制台 > Developers > API & Webhooks,点击新建 Webhook。
- Webhook URL 严格填写系统的标准接收地址:(本地测试时使用内网穿透域名,例如
https://你的域名/api/payment/notify/creemhttps://xxxx.ngrok-free.app/api/payment/notify/creem或https://xxxx.hostc.dev/api/payment/notify/creem) - 勾选监听事件:
在事件列表中勾选上述核心事件:checkout.completedsubscription.paidsubscription.updatesubscription.activesubscription.pausedsubscription.scheduled_cancelsubscription.past_duesubscription.canceledrefund.created
- 保存 Webhook,复制生成的签名密钥(格式为
whsec_...)。 - 回到系统管理后台
/admin/settings-> Payment -> Creem,将密钥填入 Signing Secret (creem_signing_secret) 并保存。
(如使用环境变量兜底,对应.env中的CREEM_SIGNING_SECRET=whsec_xxx)
6. 完整支付执行流程
系统与 Creem 的支付链路遵循严密的状态机设计:
[用户客户端]
│
├─ 1. POST /api/payment/checkout ──► [系统服务端]
│ { product_id: 'starter_monthly', │
│ payment_provider: 'creem' } ├─ 校验服务端定价目录
│ ├─ 转换 Creem product_id (从后台映射表读取)
│ ├─ 请求 Creem /v1/checkouts 创建 Session
│ └─ 本地落库订单 (Status: PENDING)
│◄── 2. 返回 { checkout_url } ────────────────┘
│
├─ 3. 跳转至 Creem 托管支付收银台完成付款
│
├─ 4. (前台同步回跳) ──► GET /api/payment/callback?order_no=...&redirect=...
│
[Creem 平台]
│
└─ 5. (后台可靠异步通知) ──► POST /api/payment/notify/creem
│
├─ 严格校验 creem-signature (HMAC-SHA256 + timingSafeEqual)
├─ 将订单更新为 PAID
├─ 发放积分 (增记流水账本)
├─ 创建/更新用户订阅记录
└─ 返回 200 OK 确认送达
7. 客户自助门户(Customer Portal)
系统已集成 Creem 官方的 Customer Billing Portal:
- 用户在个人中心账单页(
/settings/billing)点击管理订阅时,服务端会调用 Creem 接口/v1/customers/billing获取customer_portal_link; - 用户可在 Creem 官方门户中自助查看付款记录、下载收据发票、更新付款卡片或取消订阅;
- 所有在门户中产生的状态变更均会通过 Webhook 实时同步回系统。
8. 本地开发与联调测试
在本地开发阶段,Creem 无法直接请求你的 localhost:3000,需使用内网穿透工具将本地通知端点暴露到公网。
推荐方案一:hostc
# 启动本地端口转发(假设项目端口为 3000)
npx hostc 3000
获得临时公网地址(如 https://your-domain.hostc.dev)。
推荐方案二:ngrok
# 启动 ngrok 隧道
ngrok http 3000
获得临时公网地址(如 https://xxxx.ngrok-free.app)。
联调验证闭环
- 在 Creem 沙盒控制台中添加测试 Webhook:
https://你的穿透域名/api/payment/notify/creem。 - 将对应的 Webhook 签名密钥保存至管理后台。
- 访问
http://localhost:3000/pricing选购商品并完成沙盒结账,检查服务端日志:Payment event [creem]: checkout.success - 验证个人中心
/settings/credits(或/settings/billing)权益已实时到账。
9. 常见问题与排错自检
- Q1:前台点击支付提示 "Payment provider 'creem' is not configured"?
- 排查:进入后台
/admin/settings,确认creem_enabled开关已开启,且creem_api_key已正确填写。
- 排查:进入后台
- Q2:点击结账提示 "Product ID not mapped"?
- 排查:确认在后台
creem_product_ids_mapping中完整配置了当前所选本地productId与 Creem 控制台prod_xxx的映射关系,且 JSON 格式有效。
- 排查:确认在后台
- Q3:付款成功后权益没有到账?
- 排查:
- 检查 Webhook 地址是否精确配置为
https://你的域名/api/payment/notify/creem。 - 检查管理后台的
creem_signing_secret是否与 Creem 控制台生成的whsec_...完全一致。 - 在 Creem 控制台 Webhooks 列表中查看推送记录与 HTTP 响应状态码(正常应为
200 OK)。
- 检查 Webhook 地址是否精确配置为
- 排查:
- Q4:沙盒测试与生产环境区分?
- 排查:沙盒测试需在后台将环境选择为
Sandbox,并使用以creem_test_开头的 API Key 与沙盒环境生成的 Webhook Secret;上线前需切换为Production与生产密钥。
- 排查:沙盒测试需在后台将环境选择为
