Skip to content

TypeScript — React SDK

@paylera/react ships a <PayleraProvider> plus 12 hooks. The Autumn-compatible read/mutate surface (useFeature, useTrack, useAttach, useCustomer, useBillingPortal, usePricingTable) is primary; Paylera-native lifecycle hooks (useInvoices, useUpgrade, useChangePlan, useCancel) complete the surface. SSR is supported via the /server subpath.

The React SDK never talks to api.paylera.dev directly. Every call goes through the merchant-backend relay at /api/paylera/*. The relay authenticates the inbound request, attaches the secret token, and forwards to Paylera.

Install

Terminal window
npm install @paylera/react @paylera/sdk

@paylera/sdk is a peer-dep (the React SDK reuses its generated wire-format types). React 18 or 19 is required.

Quick start

import { PayleraProvider } from "@paylera/react";
function App() {
return (
<PayleraProvider backendUrl="/api/paylera" authVersion={user?.id}>
<YourRoutes />
</PayleraProvider>
);
}

authVersion is an opaque per-identity token (usually user?.id). When it changes — sign-out, account switch — the CSRF token is refetched so the previous session’s token cannot be replayed.

Provider props

PropTypeDefaultWhat it does
backendUrlstringWhere the merchant relay is mounted (e.g. /api/paylera).
authVersionstring | number | nullRotates the CSRF on identity change.
initialStatePayleraInitialStateundefinedPre-hydrated SSR cache.
checkStaleTimenumber (ms)30_000Default TanStack Query staleTime for read hooks.
fetchFetchLikeglobal fetchInject for tests / SSR.
tracer@opentelemetry/api TracerOpens Paylera.<Op> spans on mutations.
queryClientQueryClientnew oneReuse your existing TanStack client.
disableCsrfbooleanfalseSkip CSRF when your stack has its own anti-CSRF.

Granular hooks

import {
useFeature,
useEntitlements,
useCustomer,
useTrack,
useAttach,
useBillingPortal,
usePricingTable,
useInvoices,
useUpgrade,
useChangePlan,
useCancel,
} from "@paylera/react";

useFeature(code) — entitlement gate

const { allowed, balance, includedUsage, nextResetAt, isLoading } =
useFeature("api_calls");
const { track } = useTrack();
if (isLoading) return <Spinner />;
if (!allowed) return <Paywall />;
return (
<button onClick={() => track("api_calls", 1, { dedupKey: "btn-1" })}>
Use API ({balance} left, resets {nextResetAt})
</button>
);

useEntitlements()

Returns the full entitlements bundle: { features, privileges, data, isLoading, error, refetch }.

useCustomer()

Resolves the customer record: { customer, isLoading, error, refetch }.

usePricingTable() / usePlans()

The public plan catalog. Identical hooks — the names mirror Autumn’s surface.

const { plans, isLoading } = usePricingTable();

useInvoices(opts?)

The customer’s invoices: { invoices, data, isLoading, error, refetch }.

useTrack()

Record a usage event. Accepts an optional dedupKey that becomes the Idempotency-Key header.

const { track, isPending } = useTrack();
await track("api_calls", 1, { dedupKey: "btn-click-1" });

useAttach()

One-shot subscribe → Paylera-hosted checkout.

const { attach } = useAttach();
const res = await attach({
plan_id: "...",
success_url: "https://acme.com/success",
cancel_url: "https://acme.com/pricing",
});
window.location.href = res.checkout_url;

useBillingPortal()

Mint a customer-portal URL.

const { openBillingPortal } = useBillingPortal();
const { url } = await openBillingPortal({});
window.location.href = url;

useUpgrade() / useChangePlan()

Identical hooks — both wrap POST /api/paylera/subscriptions/{id}/upgrade. Use whichever name reads better.

useCancel()

const { cancel } = useCancel();
await cancel({ subscription_id: "...", at_period_end: true });

usePaylera() — composite Autumn alias

Returns { attach, check, track } on a single object — handy when migrating from an Autumn-shaped codebase.

Mutation invalidation

MutationInvalidates
useTrackuseFeature(featureCode), useEntitlements
useAttachuseCustomer, useEntitlements
useUpgrade / useChangePlanuseEntitlements, useCustomer, useInvoices
useCanceluseCustomer, useEntitlements

Every mutation accepts an optional dedupKey that becomes the Idempotency-Key header. Within TanStack’s mutation-cache window, identical keys are de-duped before they hit the network.

CSRF protocol

<PayleraProvider> fetches GET ${backendUrl}/csrf-token on mount, caches the token in a ref, and attaches Paylera-CSRF-Token: <token> on every state-changing relay call. The relay middleware compares the header to the double-submit cookie with crypto.timingSafeEqual.

Pass disableCsrf when the merchant runs their own anti-CSRF middleware.

Server-side rendering

Use the /server subpath in a Server Component:

// app/pricing/page.tsx — Next.js App Router
import { PayleraProvider } from "@paylera/react";
import {
getEntitlements,
getCustomer,
getFeature,
buildInitialState,
} from "@paylera/react/server";
export default async function Page() {
const initialState = await buildInitialState({
customerId: session.user.payleraCustomerId,
apiToken: process.env.PAYLERA_API_TOKEN!,
entitlements: true,
customer: true,
features: ["api_calls"],
});
return (
<PayleraProvider backendUrl="/api/paylera" initialState={initialState}>
<PricingClient />
</PayleraProvider>
);
}

The SSR helpers require an explicit customerId — they never read ambient request state, which would let one server-component render leak another user’s entitlements through a stale AsyncLocalStorage value.

SSR helpers

FunctionReturns
getFeature(code, opts)FeatureCheck — same shape useFeature consumes.
getEntitlements(opts)EntitlementsBundle — feeds the useEntitlements cache.
getCustomer(opts)Customer
buildInitialState(opts)Combined payload for <PayleraProvider initialState={…}>.

All four accept { customerId, apiToken, baseUrl?, apiVersion? }.

OpenTelemetry

import { trace } from "@opentelemetry/api";
<PayleraProvider
backendUrl="/api/paylera"
tracer={trace.getTracer("Paylera.React")}
>
...
</PayleraProvider>

Every mutation hook opens a span named Paylera.<Op> (Paylera.Track, Paylera.Attach, …) with the attribute keys defined in the SDK protocol. @opentelemetry/api is an optional peer-dep; without a tracer the spans are no-ops with zero overhead.

API reference

HookReturns
useFeature(code, options?){ allowed, balance, usage, includedUsage, unlimited, nextResetAt, data, isLoading, error, refetch }
useEntitlements(){ features, privileges, data, isLoading, error, refetch }
useCustomer(){ customer, isLoading, error, refetch }
usePricingTable() / usePlans(){ plans, data, isLoading, error, refetch }
useInvoices(opts?){ invoices, data, isLoading, error, refetch }
useTrack(){ track(code, value, opts?), isPending, error, reset }
useAttach(){ attach(input, opts?), isPending, error, data, reset }
useBillingPortal(){ openBillingPortal(input, opts?), isPending, error, reset }
useUpgrade() / useChangePlan(){ upgrade | changePlan(input, opts?), isPending, error, reset }
useCancel(){ cancel(input, opts?), isPending, error, reset }
usePaylera(){ attach, check, track } — composite Autumn alias

Cross-package compatibility

@paylera/react@X.Y.Z peer-depends on @paylera/sdk@^X.Y.Z (matching major). Mismatched majors cause an install-time peer-dep warning.

Example

A full Next.js 15 App Router example lives at examples/typescript/nextjs/ in the monorepo — relay route, SSR entitlements, pricing table, gated feature.