Billing
Plan & usage for an AI product, in the style of Cursor, Claude and Vercel. A usage meter split by model that projects the end of the cycle and warns before credits run out, a daily chart you can scrub, a spend limit with alerts you can preview on the meter, plan changes with exact proration, a card form with real validation, and invoices that download as PDFs.
Billing is the Plan & usage section of an AI product’s settings. It drops into a SettingsShell section and covers what people come to that page for: how much they’ve used, whether it will last, what they pay, and how to change it.
The usage meter fills by model, with a lighter extension showing where the cycle will likely end at the pace of the last 7 days. When that runs past the included credits, a quiet note says when: “At this pace you’ll run out around Oct 24, 4 days before your credits reset”, with ways to turn on extra usage or see plans. Below it, a daily bar chart built on Chart covers the whole cycle: past days, today in full strength, the rest of the cycle as faint projected bars at your recent pace, and a dashed line for the even pace that would last the cycle. Hover or focus it and use the arrow keys to read any day.
Limits are a draft, like every other settings section. Drag the alert threshold or turn on extra usage and the markers move on the meters straight away, the extra usage meter slides in, and the save bar rises. A limit lower than what was already spent this cycle is refused with the amount.
Upgrades show the exact proration before anything is charged: the new plan for the days left, the unused days of the current one, and “You’ll pay $13.33 today”. Downgrades wait until the end of the period and list what goes away. Canceling is one honest confirmation with no offers, the plan stays on until the period ends, and Keep Pro undoes it until then.
The card form formats numbers as you type without moving the cursor, checks them with Luhn, detects the brand, and validates the expiry and security code. Invoices have status badges, Pay now for failed ones, and download as real PDF files.
Add the Pro registry to components.json
components.json Add your token
Create a token on your account page and put it in
.env.localasHEXTAUI_PRO_TOKEN.Add the block
pnpm dlx shadcn@latest add @hextaui-pro/billing
Wire it to your API
BillingSettings shows the data you pass and calls you back for every change. Return or throw from a callback: a thrown message shows in the dialog that asked, and nothing changes until you pass new props.
Card updates
onUpdatePaymentMethod gets clean digits, the expiry, the security code and the brand once the form passes its checks. Throw to show a decline in the dialog. In production, hand the details to your payment provider’s tokenization and never store them yourself.
Your own invoice files
Invoices download as PDFs built in the browser from the invoice data. Return a Blob from onDownloadInvoice to serve your own file instead, or give an invoice an href.
Anatomy
The parts you compose, from the outside in.
| Part | Description |
|---|---|
BillingSettings | The section content: plan summary, usage meters and the daily Chart, limits, plans, payment, invoices and cancel, with their dialogs. |
BillingSettingsSkeleton | A loading placeholder shaped like the section. Pass it to SettingsSection’s skeleton. |
prorate | The proration math behind the upgrade dialog, for your server or tests. |
forecastUsage | The projection behind the meter and the run-out note. |
createInvoicePdf | Builds the invoice PDF as a Blob. |
BillingSettings
Prices are in cents and dates are ISO days like 2026-10-28. Leave out a callback to hide its action.
| Prop | Type | Default |
|---|---|---|
plans{ id, name, price, included, description?, features }. Features are full lists per plan, so a downgrade can list what’s missing. A plan with price 0 is the plan people move to when they cancel. | BillingPlan[] | – |
subscription{ planId, periodStart, periodEnd, status?, cancelAtPeriodEnd?, scheduledPlanId? }. | BillingSubscription | – |
usage{ models: { id, name, used }[], daily: { date, used }[], extraRate, unit? }. extraRate is cents per unit beyond what’s included. unit defaults to "credits". | BillingUsage | – |
spendLimit{ enabled, limit, alertAt }. limit is in cents and alertAt is a percentage. | BillingSpendLimit | – |
paymentMethod{ brand, last4, expMonth, expYear, name? }. | BillingPaymentMethod | null | – |
invoices{ id, number, date, description, amount, status, href?, lines?, paidWith? }. status is paid, open, failed, refunded or void. | BillingInvoice[] | [] |
todayThe day your server considers today. Used for proration, projections and card expiry, so server and browser agree. | string | the last daily date |
currencyAny ISO currency code. | string | "USD" |
sellerThe name at the top of generated invoices. | string | "Hexta" |
customerPrinted under Bill to on generated invoices. | { name?, email? } | – |
onChangePlanCalled with { planId, when, amountDue }. when is "now" for upgrades and "period_end" for downgrades. | (change) => void | Promise | – |
onCancelPlanCancel at the end of the period. | () => void | Promise | – |
onResumePlanUndo a cancellation or a scheduled downgrade. | () => void | Promise | – |
onSpendLimitChangeSave the limits. Return { limit: message } to show a field error. | (limit) => void | errors | Promise | – |
onUpdatePaymentMethodCalled with { number, expMonth, expYear, cvc, name, brand } once the form is valid. Throw to show a decline. | (card) => void | Promise | – |
onPayInvoiceAdds Pay now to open and failed invoices. | (invoice) => void | Promise | – |
onDownloadInvoiceReturn a Blob to download your own file, or nothing if you handled it. Without it, a PDF is built from the invoice. | (invoice) => Blob | void | Promise | – |
prorate
Returns { kind, amountDue, charge, credit, daysLeft, totalDays, effectiveDate, nextBillingDate }.
| Prop | Type | Default |
|---|---|---|
optionsUpgrades charge the price difference for the days left, rounded once. Downgrades charge nothing and start at periodEnd. Moving off a free plan charges a full month from today. | { from, to, periodStart, periodEnd, today } | – |
| Key | Action |
|---|---|
| ←→ | With the daily chart focused, move the tooltip to the previous or next day. |
| Enter | In the card form, check the details and save. |
| ⌘S | Saves changed limits. Ctrl+S on Windows and Linux. |
- Meters use role meter with a value text that includes the projection and the alert threshold, and the model breakdown is a real list.
- The daily chart is a figure named “Daily usage this cycle”, described by a summary with the total, the recent daily average, the busiest day and the even pace. A visually hidden table lists every day, with projected days marked as estimates.
- Plan changes, cancellations, card updates and downloads are announced politely. Failures show in the dialog that asked, as an alert, and the dialog stays open with your input.
- Card fields use the standard autocomplete tokens, so browsers and password managers can fill them. The first invalid field gets focus.
- Inputs are 16px on touch screens so iOS doesn’t zoom, and buttons grow to 44px.
- Numbers and dates are formatted in English with fixed time zones, so the server and the browser render the same text.
Code
4 files, added to components/blocks/billing.