Payments
Polar
Payment and subscription system with Polar
Beztack includes Polar as one of the supported payment providers, with Better Auth integration and subscription-oriented flows.
When to use Polar vs Mercado Pago
- Use Polar when your product is centered on Better Auth checkout/portal workflows and recurring SaaS subscriptions.
- Use Mercado Pago when you need direct Mercado Pago APIs, plan sync, card processing, and MP-specific events.
See also: Mercado Pago
Setup
Configure the required Polar variables in apps/api/.env:
POLAR_ACCESS_TOKEN=polar_at_xxxxxxxxxxxxx
POLAR_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
POLAR_SERVER=sandbox
POLAR_BASIC_MONTHLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_BASIC_YEARLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_PRO_MONTHLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_PRO_YEARLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_ULTIMATE_MONTHLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_ULTIMATE_YEARLY_PRODUCT_ID=00000000-0000-0000-0000-000000000000
POLAR_SUCCESS_URL=http://localhost:5173/checkout-success
POLAR_CANCEL_URL=http://localhost:5173/pricing
POLAR_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000Implementation references:
- Better Auth + Polar plugin config:
apps/api/server/utils/auth.ts - Polar route handlers:
apps/api/server/routes/api/polar/*
Core flows
1) Products and pricing data
GET /api/polar/products- Returns tier-oriented product data from Polar plus local DB feature/limit/ permission metadata.
2) Checkout
POST /api/polar/checkout- Creates a Polar checkout session for a selected product.
Minimal example:
const response = await fetch("/api/polar/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productId: "<polar-product-id>" }),
})
const data = await response.json()
window.location.href = data.checkoutUrl3) Subscription updates
PATCH /api/polar/subscription- Validates ownership and updates a subscription product with proration.
4) Customer portal
GET /api/polar/customer-portal- Returns a customer portal URL from Polar.
5) Usage tracking
Polar usage support is wired through the Better Auth plugin (usage()), so
you can report usage events from your app and enforce limits in product logic.
Webhooks and membership enforcement
Webhook endpoint
POST /api/polar/webhooks- Uses shared webhook validation/processing utilities and updates user/org
subscription state for key events (
order paid,subscription active,subscription canceled,customer state changed).
Server-side access checks
Use membership utilities in protected API handlers, for example via
requireAuth and membership tier checks in apps/api/server/utils/membership.ts.
Common patterns and troubleshooting
Recommended patterns
- Keep product IDs and webhook secrets environment-specific.
- Enforce access on the server (not only in the UI).
- Use webhook handlers as the source of truth for subscription state changes.
- Use
sandboxfirst, then switch toproductiononly when flows are stable.
Quick checks
- Checkout failing: validate
POLAR_ACCESS_TOKEN, product IDs, and payload. - Missing webhook effects: validate public endpoint reachability and
POLAR_WEBHOOK_SECRET. - Incorrect permissions in app: verify membership sync logic and webhook processing logs.