Billing
Add provider-neutral pricing, subscriptions, checkout, seats, usage, and billing management.
The billing plugin adds a complete billing tab to personal or organization settings. Its components use a generic BillingAdapter, so the UI does not depend on provider-specific objects. BAUI includes adapters for Stripe, Polar, Autumn, Creem, Dodo Payments, and Commet.
The billing view includes pricing plans, checkout, subscription status, portal access, cancellation, restoration, seats, and metered usage.
Setup
Configure billing in Better Auth
Configure one of Better Auth's supported billing plugins. Add its client plugin and apply its database schema.
See the provider guides for Stripe, Polar, Autumn, Creem, Dodo Payments, and Commet.
Create a provider-neutral adapter
import {
type BillingPlan,
createStripeBillingAdapter
} from "@better-auth-ui/core/plugins/billing"
import { authClient } from "./auth-client"
const plans = [
{
id: "pro",
name: "Pro",
description: "For teams shipping production applications.",
prices: [
{ id: "pro-month", amount: 2000, currency: "USD", interval: "month" },
{ id: "pro-year", amount: 19200, currency: "USD", interval: "year" }
],
features: ["Unlimited projects", "Priority support"],
highlighted: true,
seatBased: true
}
] satisfies BillingPlan[]
export const billingAdapter = createStripeBillingAdapter(authClient, {
plans,
successUrl: "/settings/billing?checkout=success",
cancelUrl: "/settings/billing?checkout=canceled",
returnUrl: "/settings/billing"
})Price amounts use the currency's smallest unit. For example, 2000 USD means $20.00.
Register the HeroUI plugin
This snippet shows only the billing additions. The TanStack Start and Next.js guides cover the full provider, including where authClient and navigate come from.
import { AuthProvider } from "@better-auth-ui/heroui"
import { billingPlugin } from "@better-auth-ui/heroui/plugins/billing"
import { organizationPlugin } from "@better-auth-ui/heroui/plugins/organization"
import { billingAdapter } from "@/lib/billing"
<AuthProvider
authClient={authClient}
navigate={navigate}
plugins={[
organizationPlugin(),
billingPlugin({
adapter: billingAdapter,
user: true,
organization: true
})
]}
>
{children}
</AuthProvider>Personal billing is enabled by default. Set organization: true to add the organization billing tab.
Polar adapter
Use the Polar adapter with the same generic plan list. Map each BAUI plan to a Polar product ID or checkout slug.
import { createPolarBillingAdapter } from "@better-auth-ui/core/plugins/billing"
export const billingAdapter = createPolarBillingAdapter(authClient, {
plans,
products: {
pro: { type: "product", value: "123e4567-e89b-12d3-a456-426614174000" }
},
successUrl: "/settings/billing?checkout=success",
cancelUrl: "/settings/billing?checkout=canceled",
returnUrl: "/settings/billing"
})Polar handles cancellation, restoration, and seat changes in its customer portal. Its adapter marks those direct actions as unsupported, so BAUI shows a manage-billing action instead. Stripe uses Better Auth's subscription endpoints.
Other bundled adapters
| Adapter | Checkout and state | Direct actions | Scope |
|---|---|---|---|
| Stripe | Better Auth subscription API | Cancel, restore, seats | User and explicit organization |
| Polar | Checkout, subscriptions, usage | Portal fallback | User and explicit organization |
| Autumn | Attach, customer subscriptions, balances | Cancel, restore, optional license seats | User |
| Creem | Checkout and active subscription | Cancel | User |
| Dodo Payments | Checkout session and subscription list | Portal fallback | User |
| Commet | Portal and current subscription | Cancel, optional feature usage and seats | User |
import {
createAutumnBillingAdapter,
createCommetBillingAdapter,
createCreemBillingAdapter,
createDodoPaymentsBillingAdapter
} from "@better-auth-ui/core/plugins/billing"
import { createAutumnClient } from "autumn-js/react"
const urls = {
successUrl: "/settings/billing?checkout=success",
cancelUrl: "/settings/billing?checkout=canceled",
returnUrl: "/settings/billing"
}
const autumnClient = createAutumnClient({
pathPrefix: "/api/auth/autumn",
includeCredentials: true
})
export const autumnAdapter = createAutumnBillingAdapter(autumnClient, {
plans,
seatLicensePlans: { pro: "team-seat" },
...urls
})
export const creemAdapter = createCreemBillingAdapter(authClient, {
plans,
products: { pro: "prod_creem_pro" },
...urls
})
export const dodoAdapter = createDodoPaymentsBillingAdapter(authClient, {
plans,
products: { pro: { type: "slug", value: "pro" } },
...urls
})
export const commetAdapter = createCommetBillingAdapter(authClient, {
plans,
planIds: { pro: "commet-plan-id" },
usage: true,
seatFeatureCode: "members",
...urls
})Enable the matching provider sub-plugins. Dodo needs checkout and portal. Commet needs portal and subscriptions, plus features or seats when you enable those adapter options.
User-only provider clients
Autumn, Creem, Dodo Payments, and Commet resolve the signed-in customer. Their browser APIs do not accept an explicit organization ID. Their adapters declare scopes.organization: false. billingPlugin rejects an organization billing configuration instead of reading active organization state.
Custom adapter
Implement BillingAdapter to connect another billing service. The adapter receives an explicit user or organization scope for every operation.
import type { BillingAdapter } from "@better-auth-ui/core/plugins/billing"
export const billingAdapter: BillingAdapter = {
id: "custom",
scopes: { user: true, organization: true },
supports: { cancel: true, restore: true, seats: true },
listPlans: async (scope, signal) => billingApi.listPlans(scope, signal),
getState: async (scope, signal) => billingApi.getState(scope, signal),
checkout: async (scope, input) => billingApi.checkout(scope, input),
openPortal: async (scope) => billingApi.openPortal(scope),
cancel: async (scope, subscriptionId) =>
billingApi.cancel(scope, subscriptionId),
restore: async (scope, subscriptionId) =>
billingApi.restore(scope, subscriptionId),
updateSeats: async (scope, subscriptionId, seats) =>
billingApi.updateSeats(scope, subscriptionId, seats)
}Authorize every scope
Validate organization membership and billing permissions on the server. Do not trust organization IDs or slugs from the browser.
Components and hooks
import {
BillingSettings,
OrganizationBillingSettings,
UserBillingSettings
} from "@better-auth-ui/heroui/plugins/billing"For custom views, import the provider-neutral React hooks from @better-auth-ui/react/plugins/billing.
Prop
Type
Last updated on