跳到主要內容

用 Polar.sh 幫你的 SaaS 加上訂閱變現:從開通到收款

你做了一個 SaaS 產品,想加上訂閱收款。打開 Stripe 文件,發現要處理 checkout session、webhook、subscription lifecycle、invoice、proration——光是讀完文件就花掉一個下午。

Polar.sh 是另一個選擇。它專門為獨立開發者和小團隊設計,把 Stripe 那套複雜的計費邏輯包成更簡單的 API,讓你用最少的 code 就能開始收錢。

Polar.sh 是什麼

Polar.sh 是一個開源的 merchant of record 平台。它不只是 payment gateway,而是幫你處理整個交易流程:收款、發票、稅務計算、退款、訂閱管理。

跟 Stripe 的定位差異:

Polar.shStripe
定位Merchant of RecordPayment Infrastructure
稅務處理Polar 負責全球稅務你自己處理(或用 Stripe Tax)
手續費低抽成 + 交易費2.9% + 30¢ per transaction
複雜度高層 API,幾行 code 搞定底層 API,彈性大但 code 多
適合Indie builder、開源專案、內容產品大型 SaaS、複雜計費邏輯
開源

關鍵差異在 merchant of record 模式。Stripe 是你自己當賣家,稅務、合規都是你的事。Polar 是它當賣家再分潤給你,全球稅務它來處理。對獨立開發者來說,這省掉非常多麻煩。

適合誰用

三類人最適合:

  1. 獨立開發者 — 不想花時間搞稅務和合規,想專注在產品上
  2. 開源專案 — 需要一個簡單的方式讓使用者付費支持或購買 pro 功能
  3. 內容創作者 — 賣課程包、訂閱制內容、付費 newsletter

如果你的計費邏輯很複雜(per-seat pricing、usage-based billing、enterprise 合約),Stripe 還是更合適。但對大多數獨立產品來說,Polar 的功能綽綽有餘。

設定流程

Step 1:建立 Polar 帳號和產品

到 Polar.sh dashboard 註冊後,建立你的第一個產品:

  1. 進入 Products 頁面,點 Create Product
  2. 選擇產品類型:Subscription(訂閱)或 One-time(一次性購買)
  3. 設定名稱、描述、價格
  4. 選擇計費週期(月繳 / 年繳)

Polar 支援多個 price tier,你可以建立 Free、Pro、Team 這樣的方案層級。

Step 2:取得 API 憑證

在 Settings → Developers 裡取得:

# .env.local
POLAR_ACCESS_TOKEN=polar_at_xxxxx
POLAR_ORGANIZATION_ID=org_xxxxx
POLAR_WEBHOOK_SECRET=whsec_xxxxx

POLAR_ACCESS_TOKEN 用於 server 端 API 呼叫,不要放到 NEXT_PUBLIC_ 環境變數裡。

Step 3:安裝 SDK

pnpm add @polar-sh/sdk

Next.js 整合

Checkout:引導使用者付款

// app/api/checkout/route.ts
import { Polar } from '@polar-sh/sdk'

const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
})

export async function POST(req: Request) {
  const { priceId, customerEmail } = await req.json()

  const checkout = await polar.checkouts.create({
    productPriceId: priceId,
    successUrl: 'https://yoursite.com/checkout/success',
    customerEmail,
  })

  return Response.json({ url: checkout.url })
}

前端呼叫這個 API 拿到 checkout.url,再 redirect 過去就好。使用者會看到 Polar 的 checkout 頁面,完成付款後跳回你的 successUrl

查詢訂閱狀態

// lib/polar.ts
import { Polar } from '@polar-sh/sdk'

const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
})

export async function getSubscriptionStatus(customerId: string) {
  const subscriptions = await polar.subscriptions.list({
    customerId,
    active: true,
  })

  if (subscriptions.result.items.length === 0) {
    return { active: false, plan: null }
  }

  const sub = subscriptions.result.items[0]
  return {
    active: true,
    plan: sub.product.name,
    currentPeriodEnd: sub.currentPeriodEnd,
  }
}

在 Server Component 或 API route 裡呼叫 getSubscriptionStatus(),就能判斷使用者是否為付費用戶。

Webhook:接收付款事件

// app/api/webhook/polar/route.ts
import { validateEvent, WebhookVerificationError } from '@polar-sh/sdk/webhooks'

export async function POST(req: Request) {
  const body = await req.text()
  const headers = Object.fromEntries(req.headers.entries())

  let event
  try {
    event = validateEvent(body, headers, process.env.POLAR_WEBHOOK_SECRET!)
  } catch (e) {
    if (e instanceof WebhookVerificationError) {
      return new Response('Invalid signature', { status: 403 })
    }
    throw e
  }

  switch (event.type) {
    case 'subscription.created':
      // 新訂閱建立:更新 database、開通權限
      console.log('新訂閱:', event.data.customer.email)
      break
    case 'subscription.updated':
      // 訂閱更新:升降級、續約
      break
    case 'subscription.canceled':
      // 訂閱取消:關閉權限(注意是 period end 才失效)
      break
  }

  return new Response('ok')
}

Webhook 是整個計費整合最重要的部分。付款成功、訂閱取消、退款——所有狀態變化都靠 webhook 通知你的系統。

在 Polar dashboard 的 Settings → Webhooks 設定你的 endpoint URL 和要監聽的事件類型。

Customer Portal

Polar 提供內建的 Customer Portal,讓使用者自己管理訂閱、更換方案、更新付款方式:

// app/api/portal/route.ts
import { Polar } from '@polar-sh/sdk'

const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
})

export async function POST(req: Request) {
  const { customerId } = await req.json()

  const session = await polar.customerSessions.create({
    customerId,
  })

  return Response.json({ url: session.customerPortalUrl })
}

拿到 customerPortalUrl 後,把使用者導過去就行。不需要自己建訂閱管理頁面。

實際架構建議

把 Polar 整合到你的 Next.js 應用,建議的架構是:

app/
├── api/
│   ├── checkout/route.ts       # 建立 checkout session
│   ├── portal/route.ts         # Customer Portal redirect
│   └── webhook/polar/route.ts  # 接收 Polar 事件
lib/
└── polar.ts                    # SDK client 和工具函式

核心邏輯集中在三個 API route 和一個工具模組。Webhook 負責同步狀態到你的 database,checkout 和 portal 負責使用者操作。

台灣用戶注意事項

幾個在台灣使用 Polar 需要知道的事:

  1. 收款幣別 — Polar 支援 USD,你的定價會是美元。台灣用戶付款時銀行會自動換匯
  2. 發票 — Polar 作為 merchant of record 會開立發票,但這不是台灣的統一發票。如果你的客戶需要台灣統一發票,需要另外處理
  3. 提領 — 收入可以提領到銀行帳戶,Polar 使用 Stripe Connect 處理 payout,台灣的銀行帳戶可以接收
  4. 稅務 — Polar 處理的是買家端的稅務(VAT、GST 等)。你自己的所得稅申報還是要自己處理

跟 Stripe 的選擇建議

簡單的判斷標準:

  • 你是獨立開發者,想最快開始收錢 → Polar
  • 你需要複雜計費(per-seat、metered、enterprise) → Stripe
  • 你不想處理全球稅務 → Polar(merchant of record)
  • 你需要完全客製化的 checkout 體驗 → Stripe
  • 你的產品是開源專案想加 premium tier → Polar

兩者不互斥。你可以先用 Polar 快速上線,等規模到了再考慮搬到 Stripe。


想看我們怎麼用 Polar 建立完整的訂閱和 course-pack 變現系統?到 Docs 查看實作細節和架構設計。