教程 / 后端集成 / 使用 Polar 接收付款
📝 文字 ● 中级 更新于 2026-05-13

使用 Polar(代商销售模式)接收付款

Polar 替你处理增值税、消费税、商品与服务税及争议退款。Stripe 不负责这些。如果你正在独立发布面向全球的 SaaS,又不想在 50 个税务管辖区逐一注册,这是一个值得认真权衡的选择。

为什么「代商销售模式」很重要

0
  • Stripe 负责刷卡处理并将钱转给你。你是商户——需要在加利福尼亚缴纳销售税、在德国缴纳增值税、在印度缴纳商品与服务税,等等。仅欧盟增值税就有 27 个国家的不同税率。
  • Polar(以及 Lemon SqueezyPaddle是代商销售平台——它们以自己的名义将产品卖给你的客户,你再将产品卖给它们。所有税务合规义务由它们承担,你只需收到以自己货币结算的单一汇款。
  • 代商模式的成本——每笔交易约 5–6%,而 Stripe 约为 2.9% + 30¢。多出来的费用相当于购买税务合规服务。对于年收入不足 100 万美元的独立 SaaS,这笔交换非常值得。
  • 选择 Polar 的时机——当你想要最注重开发者体验的代商平台时(开源、GitHub 风格认证、简洁的 API)。如果你更看重界面精致度,选 Lemon Squeezy;如果需要企业级功能,选 Paddle。
  • 注册——前往 polar.sh/login,使用 GitHub 账号登录。审核(KYC)需要 1–3 个工作日。

创建产品

1

进入控制台,点击 Products → New product,选择:

  • 类型——一次性购买(许可证、终身授权)或订阅制
  • 计费周期——按月、按年,或两者都支持
  • 价格——以美元计价,Polar 在结账时自动换算
  • 权益——Polar 的核心亮点。你可以绑定自动化权益:GitHub 仓库访问、Discord 角色、文件下载、许可证密钥、自定义 webhook。购买成功后自动触发,取消订阅后自动撤销。

保存后,复制 product_id——结账 URL 需要用到它。

获取 API 密钥

2

前往 Settings → API Keys → Create API key,选择以下权限范围:

  • checkouts:write——用于创建结账会话
  • customers:read——用于查询客户状态
  • subscriptions:read——用于校验权益

添加到 .env

POLAR_ACCESS_TOKEN=polar_oat_…
POLAR_WEBHOOK_SECRET=polar_whs_…  # from next step
POLAR_PRODUCT_ID=prod_…           # from step 1
沙盒环境与生产环境。Polar 提供沙盒环境 sandbox.polar.sh,开发阶段请使用它。两个环境的 Token 前缀不同,不要混用。

在应用中创建结账流程

3

安装 SDK:

npm install @polar-sh/sdk

服务端路由——当用户点击「升级」时触发:

import { Polar } from "@polar-sh/sdk";

const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN,
  server: "production", // or "sandbox"
});

// POST /api/upgrade
app.post("/api/upgrade", async (req, res) => {
  const checkout = await polar.checkouts.create({
    products: [process.env.POLAR_PRODUCT_ID],
    successUrl: "https://yourapp.com/welcome?checkout_id={CHECKOUT_ID}",
    customerEmail: req.user.email,
    metadata: { userId: req.user.id },   // critical — see step 5
  });
  res.json({ url: checkout.url });
});

客户端重定向到 checkout.url。Polar 负责处理信用卡输入、根据客户所在国家计算税额,以及发送收据邮件。

另一种方式:Polar Checkout Links——无需调用 API,直接使用类似 polar.sh/checkout/abc 的托管链接,放入按钮即可。非常适合落地页场景。

配置 webhook 完成订单履行

4

Polar 会在状态变更时向你的服务器发送事件。不要依赖成功页面的跳转来确认付款——用户可能中途关闭标签页,卡扣也可能异步重试。

进入 Settings → Webhooks → Add endpoint

  • URL——https://yourapp.com/api/webhooks/polar
  • 事件——subscription.createdsubscription.updatedsubscription.canceledorder.createdorder.refunded
  • 将签名密钥填入 POLAR_WEBHOOK_SECRET

验证并处理事件:

import { validateEvent } from "@polar-sh/sdk/webhooks";

app.post(
  "/api/webhooks/polar",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;
    try {
      event = validateEvent(
        req.body,
        req.headers,
        process.env.POLAR_WEBHOOK_SECRET,
      );
    } catch {
      return res.status(401).send("bad signature");
    }

    switch (event.type) {
      case "subscription.created":
      case "subscription.updated": {
        const userId = event.data.metadata.userId;
        await db.users.update(userId, {
          plan: event.data.product.name,
          polarSubscriptionId: event.data.id,
          subscriptionStatus: event.data.status, // active | past_due | …
        });
        break;
      }
      case "subscription.canceled": {
        const userId = event.data.metadata.userId;
        await db.users.update(userId, { plan: "free" });
        break;
      }
    }
    res.json({ ok: true });
  },
);

这与 Stripe webhook 教程的结构完全一致,适用规则也相同:快速返回 2xx、处理函数保持幂等、在写入数据库前完成签名验证。

使用 CLI 在本地测试

5

Polar 没有像 Stripe 那样的 webhook 转发 CLI。有两种替代方案:

  • ngrok——运行 ngrok http 3000,将生成的公网 URL 设置为沙盒 webhook 端点
  • Svix Play——捕获并重放 webhook,无需反复创建测试订单

使用 Polar 的测试卡号在沙盒中发起购买——与 Stripe 相同(4242 4242 4242 4242)。查看服务器日志,确认 webhook 已带签名到达,并验证数据库已正确更新。

在每次请求时校验权益

6

不要在每次请求时都查询 Polar 的 API——速度慢且有频率限制。直接从数据库读取:

// middleware
function requirePlan(...plans) {
  return (req, res, next) => {
    if (!plans.includes(req.user.plan)) {
      return res.status(402).json({ error: "Upgrade required" });
    }
    next();
  };
}

app.post("/api/pro-only", requirePlan("pro", "max_pro"), handler);

webhook 负责保持数据库与 Polar 同步。Polar 服务器是权威数据源,你的数据库是缓存层。

客户门户(管理订阅)

7

不要自己搭建取消或升级界面。Polar 提供托管的客户门户

app.post("/api/billing/portal", async (req, res) => {
  const session = await polar.customerPortal.sessions.create({
    customerId: req.user.polarCustomerId,
  });
  res.redirect(session.customerPortalUrl);
});

用户可以在此:更换绑定的信用卡、升级或降级套餐、取消订阅、查看历史账单、下载收据(含正确的税务信息)。套餐变更的折算由 Polar 处理。

常见问题

8
  • 测试 webhook 未触发——沙盒端点和生产端点是分开配置的,需要管理两套 webhook 端点,不要混淆。
  • metadata 未正常传递——结账时附带的 metadata 会流转到订单和订阅对象,但需要从 event.data.metadata 读取(而非 event.metadata)。这个拼写错误很容易犯。
  • 手续费高于 Stripe——是的,约 5%。换个角度理解:多出来的 2% 就是你的税务合规团队。仅欧盟增值税注册,每个国家每年就需要约 2000 美元。
  • 只支持美元结算——Polar 通过电汇/ACH 以美元付款。如果需要本地货币结算,Paddle 更灵活(但更贵且偏企业化)。
  • 部分地区无法使用——Polar 无法接受受制裁国家的付款,所有代商平台都一样。如果需要覆盖全球所有地区,可能需要同时接入 Stripe 和某家代商平台。

不适合使用 Polar 的情况

9
  • 你已有实体公司和税务注册——如果你有美国 LLC、欧盟增值税注册,并使用了 TaxJar 或 Anrok 等税务软件,用 Stripe 加会计师反而更省钱。
  • ARR 超过 100 万美元且仍在增长——规模大了之后,代商模式多出的 2% 手续费会累积成一笔不小的数字。到时候应该迁移到 Stripe 加专属税务工具。
  • 你销售实体商品——Polar 只支持数字产品(SaaS、下载内容、许可证)。实体商品请使用 Shopify 或 Stripe。
  • B2B 需要开具发票/采购订单——Polar 支持这些功能,但在企业级开票方面不如 Paddle 成熟。

定价实情

10
  • Polar——每笔交易 4% + 40¢,无月费,无最低金额限制。
  • Lemon Squeezy——5% + 50¢。2024 年被 Stripe 收购,目前仍独立运营,但未来走向不明朗。
  • Paddle——标准版 5% + 50¢,企业版费率可协商。
  • Stripe + 自行处理税务——2.9% + 30¢,加上你的时间成本、税务软件(Anrok 约每月 500 美元,TaxJar 入门版约每月 200 美元)、会计师费用,以及各地报税费用。

定价页面:polar.sh/pricinglemonsqueezy.com/pricingpaddle.com/pricing

官方参考资料

11
权衡框架。Stripe = 更低手续费,你自行处理合规。代商模式 = 更高手续费,由平台处理合规。独立创业者面向全球发布产品?多花那 2%。有了融资和财务团队?再切换回 Stripe。