Commet
Commet 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.
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
pnpm add better-auth @commet/better-auth @commet/nodePreparation
Get your API key from the Commet dashboard.
COMMET_API_KEY=ck_...
COMMET_ENVIRONMENT=sandbox # or productionServer Configuration
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
import { createAuthClient } from "better-auth/react";
import { commetClient } from "@commet/better-auth";
export const authClient = createAuthClient({
plugins: [commetClient()],
});Configuration Options
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.
import { commet, portal } from "@commet/better-auth";
commet({
client: commetClient,
use: [
portal({ returnUrl: "/dashboard" }),
],
})// Redirects to Commet customer portal
await authClient.customer.portal();Subscriptions Plugin
Manage customer subscriptions.
import { commet, subscriptions } from "@commet/better-auth";
commet({
client: commetClient,
use: [subscriptions()],
})// 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.
import { commet, features } from "@commet/better-auth";
commet({
client: commetClient,
use: [features()],
})// 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.
import { commet, usage } from "@commet/better-auth";
commet({
client: commetClient,
use: [usage()],
})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.
import { commet, seats } from "@commet/better-auth";
commet({
client: commetClient,
use: [seats()],
})// 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.
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
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(),
],
}),
],
});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;"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 (
<div>
<button onClick={handlePortal}>Manage Billing</button>
<button onClick={checkFeature}>Use Feature</button>
</div>
);
}