Polar.sh Integration

TurboStack integrates Polar.sh for payments, subscriptions, and customer management.

One-time Payments

Accept single purchases

Subscriptions

Recurring billing support

Customer Portal

Self-service billing management

Features

  • ✅ Product catalog management
  • ✅ Secure checkout sessions
  • ✅ Webhook handling
  • ✅ Customer subscriptions
  • ✅ Usage-based billing support

Setup

1

Create Polar Account

Sign up at polar.sh and create an organization.
2

Get API Keys

Go to Settings → Developers and create an access token.
3

Configure Environment

Add to your .env:
POLAR_ACCESS_TOKEN=polar_at_xxxxx
POLAR_WEBHOOK_SECRET=polar_ws_xxxxx
4

Set Up Webhook

In Polar dashboard, add webhook URL:
https://your-api.com/webhooks/polar
Subscribe to events:
  • order.created
  • subscription.created
  • subscription.updated
  • subscription.canceled

API Endpoints

EndpointMethodDescription
/polar/productsGETList all products
/polar/checkoutPOSTCreate checkout session
/polar/customer-portalGETGet portal URL
/webhooks/polarPOSTHandle Polar webhooks

Usage Examples

// Frontend
const response = await fetch("/api/polar/products");
const { data } = await response.json();

// Response
{
  success: true,
  data: {
    products: [
      {
        id: "prod_xxx",
        name: "Pro Plan",
        description: "Full access to all features",
        prices: [
          { id: "price_xxx", amount: 2900, interval: "month" }
        ]
      }
    ]
  }
}

Webhook Handling

Polar sends webhook events for order and subscription updates:
// apps/backend/src/routes/webhooks.ts
export const webhookRoutes = new Elysia({ prefix: "/webhooks" }).post(
  "/polar",
  async ({ body, headers }) => {
    // Verify webhook signature
    const signature = headers["polar-signature"];
    const isValid = await verifyPolarWebhook(body, signature);

    if (!isValid) {
      return { success: false, error: "Invalid signature" };
    }

    const event = body as PolarWebhookEvent;

    switch (event.type) {
      case "order.created":
        await handleOrderCreated(event.data);
        break;
      case "subscription.created":
        await handleSubscriptionCreated(event.data);
        break;
      case "subscription.canceled":
        await handleSubscriptionCanceled(event.data);
        break;
    }

    return { success: true };
  },
);

Database Models

TurboStack stores payment data in these models:
model Purchase {
  id           String   @id @default(cuid())
  userId       String
  productId    String
  polarOrderId String   @unique
  amount       Int
  currency     String   @default("USD")
  createdAt    DateTime @default(now())

  user User @relation(fields: [userId], references: [id])
}

model Subscription {
  id                  String    @id @default(cuid())
  userId              String
  polarSubscriptionId String    @unique
  status              String    // active, canceled, past_due
  planId              String
  currentPeriodEnd    DateTime
  canceledAt          DateTime?

  user User @relation(fields: [userId], references: [id])
}
Use the Polar dashboard to create and manage products. The API endpoints in TurboStack handle checkout and subscription management automatically.