Files
english/.opencode/skills/payment-integration/references/polar/sdk.md
2026-04-12 01:06:31 +07:00

9.3 KiB

Polar SDK Usage

Multi-language SDKs and framework adapters.

TypeScript/JavaScript

Installation:

npm install @polar-sh/sdk

Configuration:

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

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

Usage:

// Products
const products = await polar.products.list({ organization_id: "org_xxx" });
const product = await polar.products.create({ name: "Pro Plan", ... });

// Checkouts
const checkout = await polar.checkouts.create({
  product_price_id: "price_xxx",
  success_url: "https://example.com/success"
});

// Subscriptions
const subs = await polar.subscriptions.list({ customer_id: "cust_xxx" });
await polar.subscriptions.update(subId, { metadata: { plan: "pro" } });

// Orders
const orders = await polar.orders.list({ organization_id: "org_xxx" });
const order = await polar.orders.get(orderId);

// Customers
const customer = await polar.customers.get({ external_id: "user_123" });

// Events (usage-based)
await polar.events.create({
  external_customer_id: "user_123",
  event_name: "api_call",
  properties: { tokens: 1000 }
});

Pagination:

// Automatic pagination
for await (const product of polar.products.listAutoPaging()) {
  console.log(product.name);
}

// Manual pagination
let page = 1;
while (true) {
  const response = await polar.products.list({ page, limit: 100 });
  if (response.items.length === 0) break;
  // Process items
  page++;
}

Python

Installation:

pip install polar-sdk

Configuration:

from polar_sdk import Polar

polar = Polar(
    access_token=os.environ["POLAR_ACCESS_TOKEN"],
    server="production"  # or "sandbox"
)

Sync Usage:

# Products
products = polar.products.list(organization_id="org_xxx")
product = polar.products.create(name="Pro Plan", ...)

# Checkouts
checkout = polar.checkouts.create(
    product_price_id="price_xxx",
    success_url="https://example.com/success"
)

# Subscriptions
subs = polar.subscriptions.list(customer_id="cust_xxx")
polar.subscriptions.update(sub_id, metadata={"plan": "pro"})

# Orders
orders = polar.orders.list(organization_id="org_xxx")
order = polar.orders.get(order_id)

# Events
polar.events.create(
    external_customer_id="user_123",
    event_name="api_call",
    properties={"tokens": 1000}
)

Async Usage:

import asyncio
from polar_sdk import AsyncPolar

async def main():
    polar = AsyncPolar(access_token=os.environ["POLAR_ACCESS_TOKEN"])

    products = await polar.products.list(organization_id="org_xxx")
    checkout = await polar.checkouts.create(...)

asyncio.run(main())

PHP

Installation:

composer require polar-sh/sdk

Configuration:

use Polar\Polar;

$polar = new Polar(
    accessToken: $_ENV['POLAR_ACCESS_TOKEN'],
    server: 'production' // or 'sandbox'
);

Usage:

// Products
$products = $polar->products->list(['organization_id' => 'org_xxx']);
$product = $polar->products->create(['name' => 'Pro Plan', ...]);

// Checkouts
$checkout = $polar->checkouts->create([
    'product_price_id' => 'price_xxx',
    'success_url' => 'https://example.com/success'
]);

// Subscriptions
$subs = $polar->subscriptions->list(['customer_id' => 'cust_xxx']);
$polar->subscriptions->update($subId, ['metadata' => ['plan' => 'pro']]);

// Orders
$orders = $polar->orders->list(['organization_id' => 'org_xxx']);
$order = $polar->orders->get($orderId);

// Events
$polar->events->create([
    'external_customer_id' => 'user_123',
    'event_name' => 'api_call',
    'properties' => ['tokens' => 1000]
]);

Go

Installation:

go get github.com/polarsource/polar-go

Usage:

import (
    "github.com/polarsource/polar-go"
)

client := polar.NewClient(
    polar.WithAccessToken(os.Getenv("POLAR_ACCESS_TOKEN")),
    polar.WithEnvironment("production"),
)

// Products
products, err := client.Products.List(ctx, &polar.ProductListParams{
    OrganizationID: "org_xxx",
})

// Checkouts
checkout, err := client.Checkouts.Create(ctx, &polar.CheckoutCreateParams{
    ProductPriceID: "price_xxx",
    SuccessURL:     "https://example.com/success",
})

Framework Adapters

Next.js (@polar-sh/nextjs)

Quick Start:

npx polar-init

Configuration:

// lib/polar.ts
import { PolarClient } from '@polar-sh/nextjs';

export const polar = new PolarClient({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
  webhookSecret: process.env.POLAR_WEBHOOK_SECRET!
});

Checkout Handler:

// app/actions/checkout.ts
'use server'

import { polar } from '@/lib/polar';

export async function createCheckout(priceId: string) {
  const session = await polar.checkouts.create({
    product_price_id: priceId,
    success_url: `${process.env.NEXT_PUBLIC_URL}/success?checkout_id={CHECKOUT_ID}`
  });

  return session.url;
}

Webhook Handler:

// app/api/webhook/polar/route.ts
import { polar } from '@/lib/polar';

export async function POST(req: Request) {
  const event = await polar.webhooks.validate(req);

  switch (event.type) {
    case 'order.paid':
      await handleOrderPaid(event.data);
      break;
    // ... other events
  }

  return Response.json({ received: true });
}

Laravel (polar-sh/laravel)

Installation:

composer require polar-sh/laravel
php artisan vendor:publish --tag=polar-config
php artisan vendor:publish --tag=polar-migrations
php artisan migrate

Configuration:

// config/polar.php
return [
    'access_token' => env('POLAR_ACCESS_TOKEN'),
    'webhook_secret' => env('POLAR_WEBHOOK_SECRET'),
];

Checkout:

use Polar\Facades\Polar;

Route::post('/checkout', function (Request $request) {
    $checkout = Polar::checkouts()->create([
        'product_price_id' => $request->input('price_id'),
        'success_url' => route('checkout.success'),
        'external_customer_id' => auth()->id(),
    ]);

    return redirect($checkout['url']);
});

Webhook:

use Polar\Events\WebhookReceived;

// app/Listeners/PolarWebhookHandler.php
class PolarWebhookHandler
{
    public function handle(WebhookReceived $event)
    {
        match ($event->payload['type']) {
            'order.paid' => $this->handleOrderPaid($event->payload['data']),
            'subscription.revoked' => $this->handleRevoked($event->payload['data']),
            default => null,
        };
    }
}

Express

const express = require('express');
const { Polar } = require('@polar-sh/sdk');
const { validateEvent } = require('@polar-sh/sdk/webhooks');

const app = express();
const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN });

app.use(express.json());

app.post('/checkout', async (req, res) => {
  const session = await polar.checkouts.create({
    product_price_id: req.body.priceId,
    success_url: 'https://example.com/success',
    external_customer_id: req.user.id
  });

  res.json({ url: session.url });
});

app.post('/webhook/polar', (req, res) => {
  const event = validateEvent(
    req.body,
    req.headers,
    process.env.POLAR_WEBHOOK_SECRET
  );

  handleEvent(event);
  res.json({ received: true });
});

Remix

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

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

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  const priceId = formData.get('priceId');

  const session = await polar.checkouts.create({
    product_price_id: priceId,
    success_url: `${request.url}/success`
  });

  return redirect(session.url);
}

BetterAuth Integration

Installation:

npm install @polar-sh/better-auth

Configuration:

import { betterAuth } from 'better-auth';
import { polarPlugin } from '@polar-sh/better-auth';

export const auth = betterAuth({
  database: db,
  plugins: [
    polarPlugin({
      organizationId: process.env.POLAR_ORG_ID!,
      accessToken: process.env.POLAR_ACCESS_TOKEN!
    })
  ]
});

Features:

  • Auto-create Polar customers on signup
  • Automatic external_id mapping
  • User-customer sync
  • Access customer data in auth session

Error Handling

TypeScript:

try {
  const product = await polar.products.get(productId);
} catch (error) {
  if (error.statusCode === 404) {
    console.error('Product not found');
  } else if (error.statusCode === 429) {
    console.error('Rate limit exceeded');
  } else {
    console.error('API error:', error.message);
  }
}

Python:

from polar_sdk.exceptions import PolarException

try:
    product = polar.products.get(product_id)
except PolarException as e:
    if e.status_code == 404:
        print("Product not found")
    elif e.status_code == 429:
        print("Rate limit exceeded")
    else:
        print(f"API error: {e.message}")

Best Practices

  1. Environment Variables: Store credentials securely
  2. Error Handling: Catch and handle API errors appropriately
  3. Rate Limiting: Implement backoff for 429 responses
  4. Pagination: Use auto-paging for large datasets
  5. Webhooks: Always verify signatures
  6. Testing: Use sandbox for development
  7. Logging: Log API calls for debugging
  8. Retry Logic: Implement for transient failures