Loading...
Loading...
Deep integration of Polar payments with Better Auth. Use for zero-config billing, automatic customer syncing, usage-based billing, and customer portals when using Better Auth.
npx skill4agent add tuzzy08/skills polar-better-authNote: Fetch complete documentation index at:https://polar.sh/docs/llms.txt
npm install better-auth @polar-sh/better-auth @polar-sh/sdk
# or
yarn add better-auth @polar-sh/better-auth @polar-sh/sdk
# or
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdkPOLAR_ACCESS_TOKEN=polar_oat_...
POLAR_WEBHOOK_SECRET=...import { betterAuth } from "better-auth";
import {
polar,
checkout,
portal,
usage,
webhooks,
} from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
server: "sandbox", // Use 'sandbox' for testing, defaults to 'production'
});
const auth = betterAuth({
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true, // Auto-create Polar customer
// Optional: Custom metadata for new customers
getCustomerCreateParams: ({ user }, request) => ({
metadata: { source: "better-auth" },
}),
use: [
// 1. Checkout Plugin
checkout({
products: [{ productId: "prod_123", slug: "pro" }],
successUrl: "/success?checkout_id={CHECKOUT_ID}",
authenticatedUsersOnly: true,
returnUrl: "https://myapp.com",
theme: "dark", // 'light' or 'dark'
}),
// 2. Customer Portal Plugin
portal({
returnUrl: "https://myapp.com",
}),
// 3. Usage Billing Plugin
usage(),
// 4. Webhooks Plugin
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onOrderPaid: (payload) => console.log("💸 Order Paid:", payload),
onCustomerStateChanged: (payload) =>
console.log("👤 State Changed:", payload),
onPayload: (payload) => console.log("📨 Other Event:", payload),
}),
],
}),
],
});| Option | Type | Description |
|---|---|---|
| | Required. The Polar SDK instance. |
| | Auto-create Polar customer on signup. |
| | Array of sub-plugins (checkout, portal, usage, webhooks). |
| | Returns metadata/params for interaction. |
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth";
export const authClient = createAuthClient({
plugins: [polarClient()],
});products{ productId, slug }successUrl{CHECKOUT_ID}authenticatedUsersOnlytruereturnUrlthemelightdarkawait authClient.checkout({
// Option A: Use slug defined in config
slug: "pro",
// Option B: Use direct Product ID
products: ["prod_123"],
// Optional: Link to an Organization (B2B)
referenceId: "org_123",
});await authClient.usage.ingestion({
event: "ai_generation", // Must match Meter definition in Dashboard
metadata: {
tokens: 156,
model: "gpt-4",
},
});const { data: meters } = await authClient.usage.meters.list({
query: { page: 1, limit: 10 },
});
// Returns: consumed units, credited units, current balance// Redirects user to Polar Customer Portal
await authClient.customer.portal();const { data: state } = await authClient.customer.state();// List Active Subscriptions
const { data: subs } = await authClient.customer.subscriptions.list({
query: { active: true },
});
// List Orders (Purchases)
const { data: orders } = await authClient.customer.orders.list();
// List Benefits
const { data: benefits } = await authClient.customer.benefits.list();webhooks/api/auth/polar/webhooksPOLAR_WEBHOOK_SECRETimport { polar, webhooks } from "@polar-sh/better-auth";
const auth = betterAuth({
plugins: [
polar({
client: polarClient,
use: [
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
// Handlers
onCustomerStateChanged: (payload) => {
console.log("Customer state changed:", payload);
},
onOrderPaid: (payload) => {
console.log("Order paid:", payload);
},
// ... over 25+ handlers available
onPayload: (payload) => {
console.log("Catch-all event:", payload);
},
}),
],
}),
],
});onPayloadonCheckoutCreatedonCheckoutUpdatedonOrderCreatedonOrderPaidonOrderRefundedonRefundCreatedonRefundUpdatedonSubscriptionCreatedonSubscriptionUpdatedonSubscriptionActiveonSubscriptionCanceledonSubscriptionRevokedonProductCreatedonProductUpdatedonCustomerCreatedonCustomerUpdatedonCustomerDeletedonCustomerStateChangedonBenefitCreatedonBenefitGrantCreatedonBenefitGrantRevokedconst auth = betterAuth({
user: {
deleteUser: {
enabled: true,
afterDelete: async (user) => {
await polarClient.customers.deleteExternal({
externalId: user.id,
});
},
},
},
});const orgId = (await authClient.organization.list())?.data?.[0]?.id;
const { data: orders } = await authClient.customer.orders.list({
query: {
active: true,
referenceId: orgId, // Filter by Org ID
},
});
const hasAccess = orders.length > 0;