> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talosjs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Polar

> Checkouts, customers, discounts, products, the customer portal, and revenue analytics through Polar, using @talosjs/payment's typed models.

`@talosjs/payment` wraps the [Polar](https://polar.sh) merchant-of-record API behind six focused, injectable classes — `PolarCheckout`, `PolarProduct`, `PolarCustomer`, `PolarCustomerPortal`, `PolarDiscount`, and `PolarAnalytics` — plus a full set of typed models (`IProduct`, `IPlan`, `ICredit`, `ISubscription`, `IDiscount`, checkout and customer payload types, and enums like `EPriceType`, `EDiscountType`, `ESubscriptionPeriod`, `EBenefitType`) for pricing, subscriptions, and billing metadata. Each class talks to Polar through the official `@polar-sh/sdk` client and normalizes its responses into these plain Talos types, so call sites never touch the SDK directly.

## Installation

`@talosjs/payment` depends on Polar's SDK.

```bash theme={null}
bun add @talosjs/payment @polar-sh/sdk
```

## Configuration

Every class reads its Polar access token and environment from `AppEnv`, resolved through the container.

```typescript theme={null}
import { AppEnv } from "@talosjs/app-env";
import { container } from "@talosjs/container";
import { PolarCheckout } from "@talosjs/payment";

const checkout = container.get(PolarCheckout);
```

The constructor validates the token eagerly, so a missing one fails fast with `PaymentException` (`TOKEN_REQUIRED`).

## Environment variables

| Variable             | Required | Purpose                                                                                |
| -------------------- | -------- | -------------------------------------------------------------------------------------- |
| `POLAR_ACCESS_TOKEN` | Yes      | Polar organization access token. Missing throws `PaymentException` (`TOKEN_REQUIRED`). |
| `POLAR_ENVIRONMENT`  | No       | `"sandbox"` or `"production"`. Defaults to `"production"`.                             |

```bash theme={null}
POLAR_ACCESS_TOKEN=polar_oat_xxxxxxxxxxxxxxxxxxxxxxxx
POLAR_ENVIRONMENT=sandbox
```

## Usage

### Checkouts

`PolarCheckout` creates and retrieves checkout sessions.

```typescript theme={null}
import { inject } from "@talosjs/container";
import { PolarCheckout } from "@talosjs/payment";

export class BillingService {
  constructor(@inject(PolarCheckout) private readonly checkout: PolarCheckout) {}

  public async startCheckout(productId: string, email: string) {
    return this.checkout.create({
      products: [productId],
      customerEmail: email,
      successUrl: "https://app.example.com/billing/success",
    });
  }
}
```

`get(id)` fetches an existing checkout by id; both methods return a `CheckoutType` with `status`, `clientSecret`, `url`, `amount`, `currency`, and the resolved `customer`.

### Products

`PolarProduct` creates, updates, and archives products, mapping Talos's `IProduct` (prices, images, benefits, custom fields) to Polar's payload.

```typescript theme={null}
import { PolarProduct } from "@talosjs/payment";

const product = await polarProduct.create({
  name: "Pro Plan",
  recurringInterval: "monthly",
  prices: [{ type: "fixed", priceCurrency: "usd", priceAmount: 2900 }],
});

await polarProduct.update(product.key!, { name: "Pro" });
await polarProduct.remove(product.key!); // archives the product
```

### Customers

`PolarCustomer` covers the full customer lifecycle, including lookups by your own `externalId` so you never have to store Polar's id separately.

```typescript theme={null}
import { PolarCustomer } from "@talosjs/payment";

const customer = await polarCustomer.create({
  email: "ada@example.com",
  externalId: "user_123",
});

await polarCustomer.getByExternalId("user_123");
await polarCustomer.updateByExternalId("user_123", { name: "Ada Lovelace" });
await polarCustomer.list({ query: "ada", limit: 20 });
```

### Customer portal

`PolarCustomerPortal` creates authenticated customer sessions and resolves the hosted portal URL for an organization.

```typescript theme={null}
import { PolarCustomerPortal } from "@talosjs/payment";

const session = await polarCustomerPortal.create({ customerId: customer.id });
session.customerPortalUrl; // send the customer here

const portalUrl = polarCustomerPortal.getPortalUrl("my-org-slug");
```

### Discounts

`PolarDiscount` creates percentage or fixed discounts, once, repeating, or forever, and manages their lifecycle.

```typescript theme={null}
import { PolarDiscount } from "@talosjs/payment";

const discount = await polarDiscount.create({
  name: "Launch week",
  type: "percentage",
  amount: 20, // 20%
  duration: "once",
  code: "LAUNCH20",
});

await polarDiscount.update(discount.id!, { maxRedemptions: 100 });
await polarDiscount.remove(discount.id!);
```

### Analytics

`PolarAnalytics` reads revenue and subscription metrics over a date range and interval, plus the query limits Polar enforces per interval.

```typescript theme={null}
import { PolarAnalytics } from "@talosjs/payment";

const analytics = await polarAnalytics.get({
  startDate: new Date("2025-01-01"),
  endDate: new Date("2025-01-31"),
  interval: "day",
});

analytics.periods;          // per-period orders, revenue, MRR, ...
analytics.metrics.revenue;  // metric metadata (slug, displayName, type)

const limits = await polarAnalytics.getLimits();
limits.intervals.day.maxDays; // max range allowed for a "day" interval
```

## API

| Class                 | Methods                                                                                                                                                        |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PolarCheckout`       | `create(data)`, `get(id)`                                                                                                                                      |
| `PolarProduct`        | `create(data)`, `update(id, data)`, `remove(id)`                                                                                                               |
| `PolarCustomer`       | `create(data)`, `update(id, data)`, `remove(id)`, `get(id)`, `list(options?)`, `getByExternalId(id)`, `updateByExternalId(id, data)`, `removeByExternalId(id)` |
| `PolarCustomerPortal` | `create(data)`, `getPortalUrl(organizationSlug)`                                                                                                               |
| `PolarDiscount`       | `create(data)`, `update(id, data)`, `remove(id)`, `get(id)`                                                                                                    |
| `PolarAnalytics`      | `get(options)`, `getLimits()`                                                                                                                                  |

## Exceptions

Every class throws `PaymentException` (extending `Exception`, mapped to `InternalServerError`) with a machine-readable `key`.

| Key              | When                                                     |
| ---------------- | -------------------------------------------------------- |
| `TOKEN_REQUIRED` | A class is constructed without `POLAR_ACCESS_TOKEN` set. |

```typescript theme={null}
import { PaymentException } from "@talosjs/payment";

try {
  const checkout = await polarCheckout.create({ products: [productId] });
} catch (error) {
  if (error instanceof PaymentException) {
    logger.error(`Payment error [${error.key}]: ${error.message}`, error.data);
  } else {
    throw error;
  }
}
```

## Types

Beyond the six service classes, `@talosjs/payment` exports the domain model backing them: `IProduct`, `IPlan`, `IFeature`, `ICredit`, `ISubscription`, `IDiscount`, `PriceType`, `CustomFieldType`, `BenefitType` (and its variants — `CreditsBenefitType`, `LicenseKeysBenefitType`, `FileDownloadsBenefitType`, `GitHubRepositoryAccessBenefitType`, `DiscordAccessBenefitType`, `CustomBenefitType`), the checkout and customer payload types, and the `EPriceType`, `EDiscountType`, `EDiscountDuration`, `ESubscriptionPeriod`, `EBenefitType`, `EGitHubPermission`, `EAnalyticsInterval`, and `EBillingType` enums. Currency amounts are typed against `CurrencyCodeType` from [`@talosjs/currencies`](/utilities/currencies).
