Waffo 支付
配置 Waffo Pancake 托管结账、消费者门户和 Webhook
Waffo Pancake 支付集成
EasyStarter 的 Web 端内置了 Waffo Pancake 支付支持,仅适用于 Web 端,包含:
- 订阅制(月付 / 年付)托管结账
- 一次性买断(Lifetime)托管结账
- Waffo 产品试用期
- Waffo 消费者门户
- 到期取消订阅
- Webhook 事件处理(订单、订阅、续费与退款同步)
支付配置分为两部分:
- 环境变量:Merchant ID、私钥与运行环境,填入服务端
.dev.vars/.env.production - 定价计划:在
packages/app-config/src/app-config.ts中配置 Waffo Product ID 与价格信息
Waffo SDK 只能在服务端使用。切勿把
WAFFO_PRIVATE_KEY写入 Web 环境变量、前端代码或提交到 Git。
所需环境变量
WAFFO_MERCHANT_ID=
WAFFO_PRIVATE_KEY=
WAFFO_ENVIRONMENT=test| 变量 | 说明 |
|---|---|
WAFFO_MERCHANT_ID | 商户 ID,格式为 MER_xxx;不要填写 Store ID |
WAFFO_PRIVATE_KEY | 当前环境的 RSA 私钥,仅供服务端 SDK 签名请求 |
WAFFO_ENVIRONMENT | Webhook 验签环境:开发填 test,生产填 prod |
获取 Merchant ID 与测试私钥
- 登录 Waffo Pancake 商户后台
- 进入 集成 页面,先选择 测试模式
- 复制页面顶部的 商户 ID(格式为
MER_xxx) - 在 创建 API 密钥 区域创建测试密钥,复制私钥或直接使用 复制 .env 配置
- 将凭证写入本地服务端环境文件:
WAFFO_MERCHANT_ID=MER_xxxxxxxxxxxxxxxxxxxxxxxx
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
WAFFO_ENVIRONMENT=testWaffo 的测试密钥与生产密钥相互独立。切换后台环境时,必须同步更换私钥与
WAFFO_ENVIRONMENT,否则 API 调用或 Webhook 验签会失败。
私钥可以保留后台复制出的 PEM 格式。若 .env 使用单行值,请保留引号与 \n 转义换行;Waffo SDK 会自动规范化 PEM 内容。
在 Waffo 创建产品
EasyStarter 默认包含三个计划:Free、Pro(月付 + 年付)与 Lifetime(一次性买断)。Free 计划无需创建 Waffo 产品。
在商户后台进入 产品 页面,创建以下三个产品:
| EasyStarter 价格 | Waffo 产品类型 | Waffo 计费周期 |
|---|---|---|
| Pro Monthly | Subscription | Monthly |
| Pro Yearly | Subscription | Yearly |
| Lifetime | One-time | 不适用 |
保存后复制每个产品的 Product ID(格式为 PROD_xxx)。EasyStarter 将 Waffo Product ID 存放在通用的 providerPriceId 字段中。
如需试用期,请在 Waffo 订阅产品或产品组中设置试用天数,并让下一步的 trialDays 与后台配置保持一致。
配置定价计划
将 Waffo Product ID 填入 packages/app-config/src/app-config.ts 的 web.payments.plans:
web: {
payments: {
provider: "waffo",
plans: [
{
id: "free",
},
{
id: "pro",
prices: [
{
id: "monthly",
provider: "waffo",
test: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test monthly Product ID
},
prod: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production monthly Product ID
},
currency: "usd",
amountCents: 1000,
priceType: "subscription",
interval: "month",
trialDays: 7,
status: "active",
},
{
id: "yearly",
provider: "waffo",
test: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test yearly Product ID
},
prod: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production yearly Product ID
},
currency: "usd",
amountCents: 10000,
priceType: "subscription",
interval: "year",
trialDays: 7,
status: "active",
},
],
},
{
id: "lifetime",
prices: [
{
id: "lifetime",
provider: "waffo",
test: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test one-time Product ID
},
prod: {
providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production one-time Product ID
},
currency: "usd",
amountCents: 20000,
priceType: "lifetime",
status: "active",
},
],
},
],
},
},字段说明:
| 字段 | 说明 |
|---|---|
provider | Web 默认支付服务商与每个价格的服务商都设为 "waffo" |
test.providerPriceId | 测试环境可用的 Waffo Product ID,格式为 PROD_xxx |
prod.providerPriceId | 生产环境已发布的 Waffo Product ID,格式为 PROD_xxx |
amountCents | EasyStarter 前端展示的金额(分),必须与 Waffo 产品价格一致 |
priceType | "subscription" 订阅 / "lifetime" 一次性买断 |
interval | 订阅周期:"month" / "year",Lifetime 不填 |
trialDays | 非空时结账会请求启用试用;具体天数以 Waffo 产品或产品组配置为准 |
status | "active" 启用 / "archived" 归档 |
Waffo SDK 创建产品时使用
"10.00"这样的展示金额;EasyStarter 的本地配置仍使用分,因此$10.00应填写amountCents: 1000。
配置 Waffo Webhook
Webhook 是订单完成后授予权限以及同步订阅状态的依据,必须配置。
-
在 Waffo 商户后台进入 设置 → Webhooks
-
添加 HTTP Webhook,并选择与当前凭证一致的测试或生产环境
-
填写 Endpoint URL:
- 本地开发:
https://your-ngrok-url/api/webhooks/waffo - 生产环境:
https://your-server.workers.dev/api/webhooks/waffo
- 本地开发:
-
订阅 EasyStarter 已处理的事件:
事件 EasyStarter 行为 order.completed完成一次性购买或积分包订单 subscription.activated激活订阅 subscription.payment_succeeded同步续费成功 subscription.canceling标记到期取消,当前周期内保留权限 subscription.uncanceled恢复已安排取消的订阅状态 subscription.updated同步订阅变更 subscription.canceled标记订阅已终止 subscription.past_due标记续费逾期 refund.succeeded撤销对应买断或积分权益 refund.failed记录事件,不改变权益状态
Waffo 使用 RSA-SHA256 为原始请求体签名,并通过 x-waffo-signature 请求头发送签名。EasyStarter 会读取原始文本并使用 Waffo SDK 自动验签,不需要单独配置 Webhook Secret。
本地调试请使用 ngrok 并转发 Server 的
3001端口。不要使用会移除自定义请求头的隧道,否则x-waffo-signature丢失后将无法验签。
ngrok http 3001启动并完成沙盒结账
启动 Web 与 Server:
pnpm dev:web+server打开定价页完成一次测试结账。Waffo 结账会在新标签页打开,以保留 EasyStarter 当前页面状态。
| 场景 | 测试卡号 |
|---|---|
| 支付成功 | 4576 7500 0000 0110 |
| 支付失败 | 4576 7500 0000 0220 |
有效期可填写任意未来日期,CVC 可填写任意值。支付后确认 Server 收到 POST /api/webhooks/waffo 且返回 200,再到 /settings/billing 检查订阅或买断权益。
生产环境上线
上线前完成以下检查:
- 在 Waffo 后台切换到 生产模式,创建并复制独立的生产私钥
- 将所需产品发布到生产环境,并确认
prod.providerPriceId对应可用的生产 Product ID - 在生产环境注册
https://your-server.workers.dev/api/webhooks/waffo - 写入生产服务端环境变量:
WAFFO_MERCHANT_ID=MER_xxxxxxxxxxxxxxxxxxxxxxxx
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
WAFFO_ENVIRONMENT=prod- 按部署 Server文档上传生产 Secrets 并部署
不要在生产环境复用测试私钥,也不要在产品尚未发布时填写生产 Product ID。
消费者门户与当前限制
用户可从 /settings/billing 前往 Waffo 消费者门户管理订阅。当前 EasyStarter 集成的能力边界如下:
- 支持取消订阅,并在当前计费周期结束时失效
- 取消后的恢复操作需由买家在 Waffo 门户完成
- Waffo 的更换订阅产品接口目前尚未实现,调用固定返回
501 Not Implemented,因此 EasyStarter 暂时无法在应用内提供订阅升级或降级;这是 Waffo 平台当前的能力限制 - Waffo Webhook 仍会同步买家在门户完成的恢复和订阅变更
更多平台细节可参考 Waffo 官方 SDK 集成指南与 Webhook 指南。