Skip to content

useRevenue() — tenant revenue dashboards

useRevenue() reads pre-aggregated revenue buckets for the resolved tenant. It is the React-side consumer of the Phase 3 multi-source revenue capture pipeline (Stripe + Toss + Apple + Google → revenue_aggregates). Use it to render dashboards, MRR/ARR breakdowns, and refund summaries without hand-rolling a query layer.

The hook calls the merchant relay at GET /api/paylera/revenue/aggregates, which proxies to GET /v1/me/revenue/aggregates on Paylera. Like every other @paylera/react hook, it never talks to api.paylera.dev directly.

Install

useRevenue ships in @paylera/react@>=0.0.0 — no extra dependency.

Signature

import { useRevenue, type UseRevenueOptions } from "@paylera/react";
function useRevenue(opts: UseRevenueOptions): UseQueryResult<RevenueAggregateResponse>;

Options

FieldTypeDefaultWhat it does
fromDate | stringWindow start. YYYY-MM-DD or a Date (coerced to UTC). Inclusive.
toDate | stringWindow end. Same shape as from. Inclusive.
grain"day" | "month""day"Bucket granularity.
groupBy"source" | "currency" | "product""source"Faceting axis. Only the matching dimensional field appears on each bucket.
source"stripe" | "toss" | "apple_app_store" | "google_play"Restrict to one upstream source.
currencyISO 4217 (e.g. "USD")Restrict to one currency.
enabledbooleantrueSkip the network call when false.
staleTimenumber (ms)300000 (5 min)Override the default cache window.

The default 5-minute staleTime matches the upstream aggregator recompute cadence — refetching faster will return identical bucket rows.

Return shape

useRevenue returns the raw TanStack UseQueryResult. The data payload:

interface RevenueAggregateResponse {
period: { from: string; to: string; grain: "day" | "month" };
currency: string;
totals: {
gross_amount_minor: number;
refund_amount_minor: number;
net_amount_minor: number; // gross - refund
event_count: number;
};
buckets: RevenueAggregateBucket[];
}
interface RevenueAggregateBucket {
period_start: string; // YYYY-MM-DD (or first-of-month)
source_provider?: RevenueSource; // only on group_by="source"
currency?: string; // only on group_by="currency"
product_id?: string; // only on group_by="product"
gross_amount_minor: number;
refund_amount_minor: number;
net_amount_minor: number;
event_count: number;
refund_count: number;
}

All amounts are in minor currency units (cents for USD, the smallest fractional unit per ISO 4217). Format with the merchant’s preferred locale + currency at render time.

Example — daily revenue, last 30 days, by source

import { useRevenue } from "@paylera/react";
function RevenueChart() {
const today = new Date();
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const { data, isLoading, error } = useRevenue({
from: thirtyDaysAgo,
to: today,
grain: "day",
groupBy: "source",
});
if (isLoading) return <Spinner />;
if (error) return <ErrorBanner error={error} />;
return (
<>
<h2>Net revenue: {formatMoney(data!.totals.net_amount_minor, data!.currency)}</h2>
<BucketTable rows={data!.buckets} />
</>
);
}

Example — single source, single currency

const { data } = useRevenue({
from: "2026-01-01",
to: "2026-01-31",
grain: "day",
source: "stripe",
currency: "USD",
});

Example — monthly view

const { data } = useRevenue({
from: "2025-01-01",
to: "2026-01-01",
grain: "month",
groupBy: "currency",
});

Caching + refetch behaviour

  • The TanStack query key includes { from, to, grain, group_by, source, currency }. Changing any of these triggers a refetch automatically.
  • Within the 5-minute staleTime window, identical option sets return from cache — multiple components mounting useRevenue({ from, to }) with the same arguments share one network round-trip.
  • The hook does not auto-refetch on focus (TanStack default behaviour can be overridden on the Provider’s QueryClient if your dashboard wants live updates).

Relay forwarding

useRevenue calls GET /api/paylera/revenue/aggregates. The relay forwards the path to GET /v1/me/revenue/aggregates on Paylera.

@paylera/server-node (buildRelay) has an explicit allowlist of relay routes and ships proxy support for /revenue/aggregates alongside the React hook. Merchants on hand-written or Go relays should wire the same path-passthrough pattern.

Error handling

Errors are surfaced as PayleraRelayError — the same RFC-7807 shape every other hook returns:

const { error } = useRevenue({ from, to });
if (error && "type" in error) {
// error.type → "paylera.window_too_large", "paylera.window_invalid", …
}