---
title: Commet
description: Better Auth Plugin for Billing and Subscriptions using Commet
---
[Commet](https://commet.co) is the billing and payments solution for SaaS and AI products. As a Merchant of Record, Commet handles subscriptions, usage-based billing, tax compliance, and global payments—so you can start monetizing in minutes.
This plugin is maintained by the Commet team. For bugs, issues or feature requests,
please contact [Commet support](https://commet.co).
## Features
- Automatic customer creation on signup
- Customer Portal for self-service billing management
- Subscription management (get, change plan, cancel)
- Feature access control (boolean, metered, seats)
- Usage tracking for metered billing
- Seat management for per-user pricing
- Optional webhook handling with signature verification
## Installation
```bash
pnpm add better-auth @commet/better-auth @commet/node
```
## Preparation
Get your API key from the [Commet dashboard](https://sandbox.commet.co).
```bash title=".env"
COMMET_API_KEY=ck_...
COMMET_ENVIRONMENT=sandbox # or production
```
## Server Configuration
```typescript title="auth.ts"
import { betterAuth } from "better-auth";
import {
commet,
portal,
subscriptions,
features,
usage,
seats,
} from "@commet/better-auth";
import { Commet } from "@commet/node";
const commetClient = new Commet({
apiKey: process.env.COMMET_API_KEY,
environment: process.env.COMMET_ENVIRONMENT, // 'sandbox' or 'production'
});
export const auth = betterAuth({
// ... your config
plugins: [
commet({
client: commetClient,
createCustomerOnSignUp: true,
use: [
portal(),
subscriptions(),
features(),
usage(),
seats(),
],
}),
],
});
```
## Client Configuration
```typescript title="auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { commetClient } from "@commet/better-auth";
export const authClient = createAuthClient({
plugins: [commetClient()],
});
```
## Configuration Options
```typescript
commet({
client: commetClient, // Required: Commet SDK instance
createCustomerOnSignUp: true, // Auto-create customer on signup
getCustomerCreateParams: ({ user }) => ({
legalName: user.name,
metadata: { source: "web" },
}),
use: [/* plugins */],
})
```
When `createCustomerOnSignUp` is enabled, a Commet customer is automatically created with `externalId` set to the user's ID. No database mapping required.
## Portal Plugin
Redirects users to the Commet customer portal for self-service billing management.
```typescript title="Server"
import { commet, portal } from "@commet/better-auth";
commet({
client: commetClient,
use: [
portal({ returnUrl: "/dashboard" }),
],
})
```
```typescript title="Client"
// Redirects to Commet customer portal
await authClient.customer.portal();
```
## Subscriptions Plugin
Manage customer subscriptions.
```typescript title="Server"
import { commet, subscriptions } from "@commet/better-auth";
commet({
client: commetClient,
use: [subscriptions()],
})
```
```typescript title="Client"
// Get current subscription
const { data: subscription } = await authClient.subscription.get();
// Change plan
await authClient.subscription.changePlan({
subscriptionId: "sub_xxx",
planCode: "enterprise",
billingInterval: "yearly",
});
// Cancel subscription
await authClient.subscription.cancel({
subscriptionId: "sub_xxx",
reason: "Too expensive",
immediate: false, // Cancel at period end
});
```
## Features Plugin
Check feature access for the authenticated user.
```typescript title="Server"
import { commet, features } from "@commet/better-auth";
commet({
client: commetClient,
use: [features()],
})
```
```typescript title="Client"
// List all features
const { data: featuresList } = await authClient.features.list();
// Get specific feature
const { data: feature } = await authClient.features.get("api_calls");
// Check if feature is enabled (boolean)
const { data: check } = await authClient.features.check("sso");
// Check if user can use one more unit (metered)
const { data: canUse } = await authClient.features.canUse("api_calls");
// Returns: { allowed: boolean, willBeCharged: boolean }
```
## Usage Plugin
Track usage events for metered billing.
```typescript title="Server"
import { commet, usage } from "@commet/better-auth";
commet({
client: commetClient,
use: [usage()],
})
```
```typescript title="Client"
await authClient.usage.track({
eventType: "api_call",
value: 1,
idempotencyKey: `evt_${Date.now()}`,
properties: { endpoint: "/api/generate" },
});
```
The authenticated user is automatically associated with the event.
## Seats Plugin
Manage seat-based licenses.
```typescript title="Server"
import { commet, seats } from "@commet/better-auth";
commet({
client: commetClient,
use: [seats()],
})
```
```typescript title="Client"
// List all seat balances
const { data: seatBalances } = await authClient.seats.list();
// Add seats
await authClient.seats.add({ seatType: "member", count: 5 });
// Remove seats
await authClient.seats.remove({ seatType: "member", count: 2 });
// Set exact count
await authClient.seats.set({ seatType: "admin", count: 3 });
// Set all seat types at once
await authClient.seats.setAll({ admin: 2, member: 10, viewer: 50 });
```
## Webhooks Plugin (Optional)
Handle Commet webhooks. This is optional since you can always query state directly.
```typescript title="Server"
import { commet, webhooks } from "@commet/better-auth";
commet({
client: commetClient,
use: [
webhooks({
secret: process.env.COMMET_WEBHOOK_SECRET,
onPayload: (payload) => {
// Catch-all handler
},
onSubscriptionCreated: (payload) => {},
onSubscriptionActivated: (payload) => {},
onSubscriptionCanceled: (payload) => {},
onSubscriptionUpdated: (payload) => {},
}),
],
})
```
Configure the webhook endpoint in your Commet dashboard: `/api/auth/commet/webhooks`
## Full Example
```typescript title="auth.ts"
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import {
commet as commetPlugin,
portal,
subscriptions,
features,
usage,
seats,
} from "@commet/better-auth";
import { Commet } from "@commet/node";
import { db } from "./db";
import * as schema from "./schema";
const commetClient = new Commet({
apiKey: process.env.COMMET_API_KEY!,
environment: process.env.COMMET_ENVIRONMENT as "sandbox" | "production",
});
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg", schema }),
emailAndPassword: { enabled: true },
plugins: [
commetPlugin({
client: commetClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }) => ({
legalName: user.name,
}),
use: [
portal({ returnUrl: "/dashboard" }),
subscriptions(),
features(),
usage(),
seats(),
],
}),
],
});
```
```typescript title="auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { commetClient } from "@commet/better-auth";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL,
plugins: [commetClient()],
});
export const { signIn, signUp, signOut, useSession } = authClient;
```
```tsx title="dashboard.tsx"
"use client";
import { authClient } from "@/lib/auth-client";
export function BillingSection() {
const handlePortal = async () => {
await authClient.customer.portal();
};
const checkFeature = async () => {
const { data } = await authClient.features.canUse("api_calls");
if (data?.allowed) {
// Proceed with action
await authClient.usage.track({ eventType: "api_call" });
}
};
return (
);
}
```