GenerateSaaS

Credits

Meter any paid feature with usage-based credits - per-plan grants, recurring re-grants, cost-priced debits, custom top-ups, and refund handling.

A general-purpose usage meter owned by @repo/payments: one balance per user that any feature can draw from - AI runs, external API calls, renders, exports. Configured in pricingConfig.credits (packages/config/src/pricing.ts, a separate named export from @repo/config) and gated by credits.enabled; with it false, grants and the credits UI are suppressed and balances stay at 0.

Storage

Balances live on the users table only. Organizations hold no credits: work done inside one draws on the owner's balance. Better Auth persists these columns through billingAdditionalFields (packages/config/src/billing-fields.ts), all input: false so only the server writes them.

FieldDefaultPurpose
credits0Signed balance, numeric(16,6). A negative value is debt.
creditsLastGrantedAt-Timestamp of the last recurring grant.
autoTopUpEnabledfalseAuto top-up opt-in.
autoTopUpThreshold-Balance that triggers a top-up.
autoTopUpAmount-Credits bought per top-up.
numeric(16,6) caps a balance at 9,999,999,999.999999 credits. Upgrading a deployment that already holds a larger value aborts the migration, naming every offending table, column and row count - settle or write those balances down first.

Mutate them only through Billing(...) (packages/payments/src/billing.ts), which exposes addCredits, removeCredits, settleCredits, setCredits, triggerAutoTopUpIfNeeded, a hasCredits getter, and hasCreditsFor(amount).

await Billing(session);                                   // whoever funds this session
await Billing(userId);                                    // that user, explicitly
await BillingForWorkspace(organizationId, actingUserId);  // detached work: a job, an automated run

Pass null as the workspace when the work belongs to no organization. All three apply the same rule - see Organizations.

On this page