跳转到主要内容
Lemon Squeezy 是阻力最小的那条路:所有 MoR 里注册流程最友好,不需要实体公司, 注册完一个下午就能开始收钱。费率是 5% + 50¢,和 Paddle 差不多。2024 年被 Stripe 收购,但产品还在独立运营,对个人开发者的体验也保留得很好。 如果你今天就想上线、是个体户、或者还不想碰 VAT/销售税合规,就选 Lemon Squeezy。

前置条件

  • 一个 Lemon Squeezy 账号(lemonsqueezy.com) —— 注册是即时的,测试模式立刻就能跑真支付链路。
  • 建一个 store(Settings → Stores → New store)。Store ID 后面要用。
  • Postgres 跑起来,schema 已 push。

1. 选定当前 Provider

src/config/site.ts
payment: {
  provider: 'lemonsqueezy' as 'stripe' | 'paddle' | 'lemonsqueezy' | 'creem',
  currency: 'usd',
},

2. 建商品和 variant

Lemon Squeezy 的模型是**产品(product)**下面挂一个或多个 variant。真正 卖出去的是 variant(比如 “Vibestrap Promo 49""VibestrapStandard49" 和 "Vibestrap Standard 99” 可以是同一个产品下的两个 variant,也可以是两个各自只有一个 variant 的产品 —— 都行)。 Products → New product。每个 variant 有一个数字 ID,记下来要塞进 env。 注意:vibestrap 的 Lemon Squeezy Provider 期望 priceId 是一个数字的 variant ID(用 parseInt 解析),不是字符串商品 slug。

3. 找到 store ID

Settings → Stores → [你的 store]。URL 里那串数字就是 store ID,贴到 LEMON_SQUEEZY_STORE_ID

4. 配环境变量

.env.local(变量名严格对齐 src/env.ts):
LEMON_SQUEEZY_API_KEY=eyJ0eXAiOiJKV1Qi...      # Settings → API
LEMON_SQUEEZY_STORE_ID=12345
LEMON_SQUEEZY_WEBHOOK_SECRET=自己想的签名密钥

# vibestrap 本体的 variant ID(数字,但当字符串贴)
LEMON_VARIANT_VIBESTRAP_PROMO=678901
LEMON_VARIANT_VIBESTRAP_STANDARD=678902

5. 配 webhook

Settings → Webhooks → New webhook
  • URLhttps://your-domain.com/api/webhooks/lemonsqueezy
  • Events(至少订阅):order_createdsubscription_createdsubscription_updatedsubscription_cancelledsubscription_expired
  • Signing secret:自己随便编一个长随机字符串,同时贴到这里和环境变量 LEMON_SQUEEZY_WEBHOOK_SECRET
本地联调用 ngrok 或 cloudflared 暴露 localhost:3000

验证

  1. 在 store 里打开 test mode,启动 pnpm dev
  2. 打开 /pricing,点结账 —— Lemon Squeezy 的浮层会弹出来。
  3. 用测试卡 4242 4242 4242 4242,过期日填未来,CVC 随便。
  4. 看终端 webhook 日志。
  5. 查数据库:
    select id, provider, scene, status, amount from payment
    where provider = 'lemonsqueezy'
    order by created_at desc limit 1;
    

常见坑

  • Store ID 是账号级别的,不是 store 自己生成的 —— 一个账号下有多个 store 时必须显式选一个。LEMON_SQUEEZY_STORE_ID 不配的话 Provider 在建 checkout 时会立刻报错。
  • Variant 和 product 容易搞混 —— 定价页常常链接到 product,但 checkout 需要 variant ID。Dashboard URL 里 /products/123 是 product ID,不是 variant ID。要点进具体 variant 才能拿到它的 ID。
  • 没有 customer portal API —— Lemon Squeezy 的客户 portal URL 是 checkout 时一次性下发的,会带在订单 webhook 里(customer.urls.customer_portal)。 你得自己存下来复用,调 paymentManager.createPortalLink 会抛错(设计如此 —— 见 src/payment/provider/lemonsqueezy.ts)。
  • Webhook 密钥是你自己填的 —— 跟 Stripe 不一样,Lemon Squeezy 不替你生成 签名密钥。挑个长随机串(openssl rand -hex 32)。
  • Test mode 的订单 ID 会重置 —— 删掉 webhook 再重建后,旧的 test 事件不能 重投。重新发起一笔测试订单即可。

官方文档