9.3 KiB
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
- Environment Variables: Store credentials securely
- Error Handling: Catch and handle API errors appropriately
- Rate Limiting: Implement backoff for 429 responses
- Pagination: Use auto-paging for large datasets
- Webhooks: Always verify signatures
- Testing: Use sandbox for development
- Logging: Log API calls for debugging
- Retry Logic: Implement for transient failures