TanStarter Docs

Waffo Pancake

如何设置和使用 Waffo Pancake 处理支付和订阅

TanStarter 使用 Waffo Pancake 进行支付管理,支持一次性支付和订阅支付。Waffo Pancake 是一家 Merchant of Record(MoR,记录商) 平台,它以法定卖家身份处理全球税务计算、代收、合规和结算。

该集成使用官方 @waffo/pancake-ts SDK,并采用 Waffo 托管的结账页面和消费者门户,因此卡信息不会经过您的服务器。

设置

TanStarter 模板默认提供三种价格计划:免费计划、专业版订阅计划(月度/年度)和终身计划(一次性支付),按照以下步骤设置:

创建 Waffo 账户

Waffo Pancake 注册 Waffo Pancake 账户,并在引导过程中创建一个商店。更多基础信息请参考 Waffo 快速入门

获取 API 密钥

从 Waffo 控制台获取您的 Merchant ID 和私钥:

  • 进入到 Waffo 控制台 > API & Development,点击 Create API Key 生成新的密钥对
  • 复制 Merchant ID(以 MER_ 开头)和私钥
  • 将其保存到环境变量文件中作为 WAFFO_MERCHANT_IDWAFFO_PRIVATE_KEY

WAFFO_MERCHANT_ID 指的是 Merchant ID——不是 storeId,也不是 URL 中的商店标识。API 密钥在创建时就绑定到测试或生产环境,因此请为每个环境创建独立的密钥。

私钥只能存放在服务端。将 PEM 私钥写入环境变量时,需要用转义的 \n 保留换行:

WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"

更多关于 API 密钥认证 的信息请参考 Waffo 官方文档。

设置 Webhook

设置 Webhook 并订阅支付事件:

  • 进入到 Waffo 控制台 > Settings > Webhooks,点击 Add Webhook
  • 选择 Raw 负载格式(只有 Raw 格式会携带用于验签的 X-Waffo-Signature 请求头)
  • 输入 Webhook URL:https://YOUR-DOMAIN.com/api/webhooks/waffo
  • 所有事件默认都会订阅,请至少保留以下事件:
    • order.completed
    • subscription.activated
    • subscription.payment_succeeded
    • subscription.updated
    • subscription.canceling
    • subscription.uncanceled
    • subscription.canceled
    • subscription.past_due
    • refund.succeeded
    • refund.failed

与 Stripe 和 Creem 不同,Waffo 没有 Webhook 签名密钥这样的环境变量@waffo/pancake-ts SDK 内置了验证公钥并自动识别环境,verifyWebhook() 会为您完成签名验证。

更多关于 WebhookWebhook 签名验证 的信息请参考 Waffo 官方文档。

创建产品和价格计划

在 Waffo 中创建产品并设置价格计划。Waffo 使用 Product ID(以 PROD_ 开头)进行结账,而不是价格 ID:

  • 进入到 Waffo 控制台 > Products,点击 Create Product
  • 创建专业版订阅计划的产品
    • 产品类型:Subscription
    • 名称:专业版计划
    • 计费周期:Monthly——保存并复制产品 ID,用于 VITE_WAFFO_PRODUCT_PRO_MONTHLY
    • 再创建一个计费周期为 Yearly 的订阅产品——保存并复制产品 ID,用于 VITE_WAFFO_PRODUCT_PRO_YEARLY
  • 创建终身计划的产品
    • 产品类型:One-time
    • 名称:终身计划
    • 保存并复制产品 ID,用于 VITE_WAFFO_PRODUCT_LIFETIME

建议为每个计费周期(月度/年度)分别创建订阅产品,详见 Waffo 订阅指南。新创建的产品处于测试模式;在接收真实支付前需要先发布到生产环境,参见 发布产品

添加环境变量

添加以下环境变量:

.env
# 支付提供商
VITE_PAYMENT_PROVIDER=waffo

# Waffo API 凭据(仅服务端使用)
WAFFO_MERCHANT_ID=MER_...
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"

# 可选:在生产构建中接受测试模式 Webhook(仅用于冒烟测试)
# WAFFO_DEBUG=true

# Product IDs
VITE_WAFFO_PRODUCT_PRO_MONTHLY=PROD_...
VITE_WAFFO_PRODUCT_PRO_YEARLY=PROD_...
VITE_WAFFO_PRODUCT_LIFETIME=PROD_...

更新网站配置

更新 src/config/website.ts 中的 payment 部分,配置价格计划 — 金额、货币、周期和计划元数据。enableproviderpriceId 字段会根据环境变量(VITE_PAYMENT_PROVIDERVITE_WAFFO_PRODUCT_*)自动解析,无需硬编码。

必须配置此部分,使其与您在 Waffo 中创建的产品一致:

src/config/website.ts
payment: {
  enable: isPaymentEnabled,              // ← 自动:当 VITE_PAYMENT_PROVIDER 设置时为 true
  provider: isPaymentEnabled ? paymentProvider : undefined, // ← 自动:'waffo'
  price: {
    plans: {
      free: {
        id: 'free',
        prices: [],
        isFree: true,
        isLifetime: false,
      },
      pro: {
        id: 'pro',
        prices: [
          {
            type: 'subscription',
            priceId: priceIds.proMonthly,  // ← 自动:来自 VITE_WAFFO_PRODUCT_PRO_MONTHLY
            amount: 990,                   // 金额,单位为分($9.90)
            currency: 'USD',
            interval: 'month',
          },
          {
            type: 'subscription',
            priceId: priceIds.proYearly,   // ← 自动:来自 VITE_WAFFO_PRODUCT_PRO_YEARLY
            amount: 9900,                  // 金额,单位为分($99.00)
            currency: 'USD',
            interval: 'year',
          },
        ],
        isFree: false,
        isLifetime: false,
        popular: true,
      },
      lifetime: {
        id: 'lifetime',
        prices: [
          {
            type: 'one_time',
            priceId: priceIds.lifetime,     // ← 自动:来自 VITE_WAFFO_PRODUCT_LIFETIME
            amount: 19900,                  // 金额,单位为分($199.00)
            currency: 'USD',
            allowPromotionCode: true,
          },
        ],
        isFree: false,
        isLifetime: true,
      },
    },
  },
},

如果您正在设置环境,现在可以回到环境配置文档并继续。本文档的其余部分可以稍后阅读。

环境配置

设置环境变量


核心功能

  • 一次性支付成为终身会员功能
  • 定期订阅支付(月度/年度)
  • 支持免费试用期
  • 托管结账页面,支持买家归属(认证结账)
  • 支付、订阅和退款事件的 Webhook 处理
  • 税务、合规和结算由 Waffo 处理
  • 消费者门户,支持 Magic Link 登录
  • 内置价格组件(表格、卡片、按钮)
  • 安全支付操作的服务器端操作
  • 多种价格计划支持(免费、专业版、终身制)

开发环境

本地开发时,需要使用 HTTPS 隧道将本地服务器暴露到公网,以便 Waffo 能够发送 Webhook 事件。官方文档推荐使用 ngrokcloudflared 也可以。不要使用 localtunnel——它会剥离 X-Waffo-Signature 等自定义请求头:

ngrok http 3000
# 或
cloudflared tunnel --url http://localhost:3000

然后:

  1. 在 Waffo 控制台 > Settings > Webhooks 中,将隧道 URL(例如 https://xxxx.ngrok-free.app/api/webhooks/waffo)设置为测试环境 Webhook URL。永远不要将临时隧道 URL 配置到生产 Webhook。
  2. 在网站上进行一次测试支付,验证事件处理流程是否符合预期。

模板还附带 Waffo 沙盒 E2E 测试套件:

pnpm e2e:waffo

Waffo 提供完整的测试环境,使用测试卡即可模拟支付,不会产生真实交易。在托管的沙盒结账页面中,您可以选择 Credit/Debit Card,并使用 Quick FillSuccess 选项立即完成一笔测试支付。更多信息请参考 Waffo 官方文档中的 测试模式

生产环境

  1. 完成商店审核 / KYB,以启用生产支付——参见 账户审核
  2. 将订阅和一次性产品从测试环境发布到生产环境。发布是单向、仅首次的操作——参见 发布产品
  3. 创建生产环境的 API 密钥,用于 WAFFO_MERCHANT_ID / WAFFO_PRIVATE_KEY
  4. 在 Waffo 控制台 > Settings > Webhooks 中添加生产 Webhook URL:https://YOUR-DOMAIN.com/api/webhooks/waffo
  5. 保持 WAFFO_DEBUG 不设置(或设为 false),这样生产环境会拒绝测试模式事件——沙盒购买绝不能授予真实访问权限

客户门户

Waffo 提供托管的消费者门户:https://pancake.waffo.ai/consumer/portal/login。客户使用购买时填写的邮箱并通过一次性 Magic Link 登录,无需密码。在门户中他们可以:

  • 查看有效订阅、下次扣款日期和支付历史
  • 取消或重新激活订阅
  • 下载 PDF 发票和收据
  • 更新账单信息
  • 申请退款

Billing 页面(/settings/billing)通过"Manage subscription"按钮将客户引导至该门户。更多信息请参考 Waffo 官方文档中的 消费者门户

Webhook 事件

Waffo 支持以下 Webhook 事件:

事件描述
order.completed一次性订单支付成功
subscription.activated订阅首次支付成功
subscription.payment_succeeded续费支付成功(非首次)
subscription.updated订阅产品变更(升级/降级)
subscription.canceling已申请取消——在当前周期结束前仍然有效
subscription.uncanceled取消申请被撤回
subscription.canceled订阅终止(周期结束)
subscription.past_due续费支付失败,Waffo 正在重试
refund.succeeded退款完成——访问权限被撤销
refund.failed退款失败

Waffo 将测试和生产事件投递到同一个端点,每个负载都带有 mode 字段。模板会拒绝与当前运行环境不匹配的事件;在生产构建中,仅当使用沙盒商户对已部署的 Worker 做冒烟测试时,才将 WAFFO_DEBUG 设置为 true

测试卡

要测试 Waffo 集成,请使用 Waffo 测试模式和以下测试卡:

卡号类型结果
4576 7500 0000 0110Visa Credit成功
2226 9000 0000 0110Mastercard Credit成功
4576 7500 0000 0220Visa Credit拒绝

使用任意未来有效期和任意 3 位 CVC 即可。您可以在 Waffo 官方文档中找到更多关于测试模式和测试卡的信息。

最佳实践

  1. 保护 API 密钥:永远不要在客户端代码中暴露 WAFFO_PRIVATE_KEY 或 Merchant ID
  2. 验证 Webhook 签名:始终验证 X-Waffo-Signature 请求头(SDK 的 verifyWebhook() 会完成验证)
  3. 匹配环境:使用与当前运行环境对应的 API 密钥和 Webhook URL
  4. 优雅处理错误:当支付失败时提供用户友好的错误消息
  5. 彻底测试 Webhooks:上线前先在测试模式下完成一次完整购买

参考资料

最后更新于

本页目录