Skip to main content
Gating features on billing state, evaluating usage limits, and the account-level widgets. The state model itself is explained in Concepts.
Examples on this page are fragments. They render inside the provider you mounted in the Quickstart:
If something below does not work, one of these is usually missing. Each links straight to the step that sets it up.

Gating UI with BillingGate

BillingGate conditionally renders based on which billing actions are available in the snapshot (checkout, portal, cancel, reactivate):
Account page

Usage limits

Catalog plans can carry limits (arbitrary numeric keys like projects or aiMessages). evaluateUsageLimits compares your app’s usage counters against the active plan’s limits:
The library evaluates the limits. Your app owns the actual usage counters and decides where they live.
UI gates are user experience, not security. Protected backend actions must check billing state server-side. Read the snapshot inside your Convex function before doing the work.

UI permissions

BillingPermissions controls which widget buttons are enabled. Use it for role-based UI, such as letting only org admins manage billing. Set it once at the provider:
Account page
Disabled permissions render greyed-out buttons. This is cosmetic gating only. Enforce the real rules in your Convex functions, as described in Advanced → Custom auth and RBAC.

Account widgets

Billing portal

Opens the Creem customer portal, which is where payment methods, billing details, and invoice documents live. It hides itself when the entity has no Creem customer record yet, since customers are created on first checkout:
Account page
Requires customers.portalUrl in the connected API.

Billing history

Paginated transaction history, backed by Creem’s transaction search. This renders transaction rows, not invoice documents:
Account page
A billing history table with Date, Description, Type, Status, and Amount columns listing five paid transactions, above a pager running from page 1 to page 22
Requires transactions.search in the connected API. Export transactionsSearch from convex/billing.ts and connectCreemApi wires it automatically.
Portal-only today. Payment methods, billing details, and invoice documents are handled by Creem’s hosted portal, which <BillingPortal> opens for the customer. Prefer them embedded in your own UI as Widgets? Vote for payment method, billing preference, and invoice APIs.

Payment recovery

When a subscription is past due or a payment failed, surface it and route the user to the portal to fix their payment method:
Account page
A yellow past-due banner above a red payment-failed banner, followed by an Update payment method button
The banner derives its state from snapshot.paymentRecoveryState (or accepts an explicit state). The button reads customers.portalUrl from the provider like every other connected widget, and renders nothing when no portal action is wired.

Checkout success

Show a confirmation banner when the user returns from checkout. Creem’s query parameters are parsed for you:
Account page
React additionally exports a useCheckoutSuccessParams() hook returning the parsed params.

Status banners

Presentational components for specific states, fed from the snapshot:

Server-side entitlement checks

The same snapshot powers backend enforcement:
convex/projects.ts
Widget gates keep the UI honest. This check is what actually protects the feature.

Checking a single subscription

When you gate on subscriptions.getCurrent rather than the snapshot, test the status - never the mere existence of a subscription. A returned subscription may be unpaid or paused, in which case it must not grant access:
isActiveSubscriptionStatus treats active, trialing, scheduled_cancel, and past_due as entitling. past_due is a payment-retry window rather than a loss of access - surface it with PaymentRecoveryBanner instead of cutting the user off.