@nozle-js/react 0.11.0 and a backend on @nozle-js/node 0.13.0.
usePlans reads the public plan catalog with the publishable key. The customer hooks read the signed-in customer’s billing data from a route on your own backend, so the browser never holds a secret key and can only see its own customer.
Customer hooks
Serve the hooks from your backend
createBillingHandler from @nozle-js/node is a web-standard request handler that serves every customer hook with your secret key. The customer always comes from getCustomerId, read from your session; a customer id sent by the browser is ignored.
app/api/nozle/[route]/route.ts
subscription, entitlement, credits, credit-history, usage, preview, invoices or invoice-download. invoices lists only the signed-in customer’s finalized invoices, and invoice-download answers 404 for an invoice of another customer. The handler answers 401 without a customer, passes Nozle’s 4xx errors through with their code, and answers 502 billing_unavailable for upstream failures without internal detail. It runs in Next.js route handlers, Hono, Remix, Bun and Deno.
Then point the provider at it:
billingHeaders adds headers such as a CSRF token. Pass your app’s queryClient to share its cache and devtools; without one the provider makes its own. The hooks pass their client explicitly and never replace your app’s QueryClientProvider.
The hooks
Each hook returns TanStack Query’s result (data, isPending, error, refetch and the rest).
useBillingRefresh() returns a function that reloads every billing hook, for example after checkout or a plan change.
BillingRequestError with status, code and requestId. A 4xx is not retried; other failures are retried twice.
For subscription changes with confirmation and recovery, use the billing portal action adapters.
usePlans
Fetches GET /api/v1/plans through the BillingProvider catalog client.
Calls share one cached request per publishableKey and baseUrl, kept for five minutes. A failed load is not retried automatically; call refetch(). usePlans does not fetch during server rendering; it renders the initial loading state.
Return value
CatalogPlan: type versus runtime
The TypeScript type CatalogPlan declares code, name, amount_cents, amount_currency and interval, and the optional description (string | null), entitlements, credit_grant_rules and charges. Its plans pass to PricingTable, PlanCard and PlanComparison as they are.
At runtime, each item in plans is the trimmed plan object the API returns to a publishable key. usePlans does not remove or rename fields. Besides the declared fields, a plan carries nozle_id, trial_period, pay_in_advance, usage_thresholds, taxes, credit_pool_policies, and metadata when set. Invoice names, internal ids, margin settings, grant policies and timestamps are not sent to a publishable key. See the public plan catalog for every field.
The list is not filtered. It includes trial plans and pay-in-arrears plans, and not plans that are pending deletion. Decide in your component which plans to show. To read an undeclared field, widen the type:
useCheckoutSession
Calls BillingProvider.createCheckout with a plan code and return URL.
fetchClientSecret passes only planCode and returnUrl to createCheckout. It sends no idempotency key; use CheckoutButton when you need one.
The hook returns a string only for a Stripe client secret. For a hosted result (or a result with a url), it navigates to the URL and returns null. For completed, scheduled, razorpay, and processing results it returns null without opening Razorpay or polling; read the result from checkout and handle it yourself, for example with Checkout’s checkout prop. Any other result throws Checkout response did not include a supported result type.
Return value
useBillingContext
Returns the required billing context and throws outside BillingProvider.
usePlans and useCheckoutSession for common flows.
useOptionalBillingContext
Returns the billing context or null outside a provider. PricingTable uses this behavior so caller-supplied plans can render without a catalog provider.
useNozleClient
Returns the browser-safe NozleClient:
catalogFetch is for public catalog endpoints only. Do not use it to proxy customer billing requests.
BillingContext
BillingContext is exported for advanced integrations that need React’s direct context API. Its value is either null or:
useBillingContext or useOptionalBillingContext in application components so missing-provider behavior stays explicit.
Checkout result types
createCheckout returns the result your merchant backend received from Nozle. It may be:
Razorpay fields are snake case. A
{ type: 'razorpay', orderId } result fails: useCheckoutSession returns null, and CheckoutButton shows Razorpay checkout requires server-issued checkout_id, key_id and order_id.
Polling calls getCheckoutStatus up to 30 times, one second apart, until both status and fulfillment_status are succeeded.
The components also accept
{ type: 'stripe', url } and a legacy { url }, and redirect to them. Plan and invoice checkout do not return either; they return a client secret.
Stripe webhooks remain authoritative. A browser redirect or completion callback does not activate a paid plan by itself.