useOrderStatus()
Polls GET /events/{eventId}/ticketing/orders/{orderId}/status until
the order reaches a terminal state (completed, failed, or
refunded), the component unmounts, or the caller invokes stop().
Picks up eventId, orderId, and orderStatusToken from SDK state
by default — these are populated automatically during checkout.
Import
import { useOrderStatus } from "@feelrift/react";
Basic usage
import { useOrderStatus } from "@feelrift/react";
function OrderStatusBanner() {
const { data, isTerminal } = useOrderStatus();
if (!data) return null;
if (isTerminal) {
return (
<p>
Order {data.orderId}: {data.status}
</p>
);
}
return <p>Finalizing order {data.orderId}…</p>;
}
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
options | UseOrderStatusOptions | No | {} | All options optional. Defaults read from SDK state and use a 2-second poll interval. |
options.eventId | string | No | from SDK | Override the event namespace for the order. Defaults to the active reservation's event. |
options.orderId | string | No | from SDK | Override the order to poll. Defaults to the orderId populated by useCheckout. |
options.token | string | No | from SDK | Override the bearer token. Defaults to the orderStatusToken populated by useCheckout. |
options.pollIntervalMs | number | No | 2000 | Poll cadence in milliseconds. Lower bound enforced by the server's rate limiter. |
Returns
| Field | Type | Description |
|---|---|---|
data | OrderStatusResponse | null | The most recent successful poll result. null before the first poll completes or when no order is configured. |
isLoading | boolean | true while a poll request is in flight. |
error | RiftApiError | null | The most recent RiftApiError. Stays set after subsequent successful polls cleared it. |
isTerminal | boolean | true when data.status is completed, failed, or refunded. The hook also stops polling at this point. |
stop | () => void | Stops the poll loop. Safe to call multiple times. The loop also stops on unmount. |
OrderStatusResponse:
type OrderStatusResponse = {
orderId: string;
status: "awaiting_payment" | "completed" | "failed" | "refunded";
};
Behavior
- Source of
eventId,orderId, andtoken. Reads from the SDK's persistent state by default. Checkout populates these values on success. Override viaoptionswhen polling an order outside the checkout flow, such as a post-purchase landing page that receives the token via URL. - Auto-start. The poll loop starts in a
useEffectas soon as bothorderIdandtokenare non-null. Mounting the hook without either is a no-op. - Auto-stop on terminal. When
data.statusiscompleted,failed, orrefunded, the hook clears the interval and setsisTerminal = true. No further polls fire. - Auto-stop on unmount. The effect cleanup clears the interval and marks the component as cancelled, so in-flight responses resolved after unmount don't update React state.
- First poll is immediate. The hook calls the first poll
synchronously when the effect runs, then schedules the next at
pollIntervalMs. - Error behavior. Server errors are caught and assigned to
error. The poll loop continues — transient errors don't stop polling. The hook does NOT auto-stop onerror; consumers should callstop()for fatal errors (token expired/tampered). - Token expiry. The order-status token is ~10 minutes. After
expiry the server returns
code: "order_status_token_expired"; the hook surfaces it onerrorbut the poll loop keeps attempting. Callstop()and route the user to an email-link recovery flow. - Rate limits. The server allows generous polling (60 req/min
per identity by default). Setting
pollIntervalMsbelow ~500ms risks tripping the per-identity rate limit and surfacingcode: "rate_limited"errors.
Errors
Errors assigned to error (the hook never throws). All are
RiftApiError instances. See the errors reference
for typed extensions per code.
| Status | Code | When | Recovery |
|---|---|---|---|
401 | order_status_token_tampered | Token signature failed verification (or sub mismatch with orderId). | stop(); route to an email-link recovery flow. |
404 | order_not_found | The order ID doesn't exist. | stop(); the URL is probably stale. |
410 | order_status_token_expired | Token's TTL passed (~10 minutes). | stop(); route to email-link recovery. |
429 | rate_limited | Poll cadence too high for the per-identity limiter. | Increase pollIntervalMs. |
Examples
Default — pick up from useCheckout automatically
import { useOrderStatus } from "@feelrift/react";
function PostCheckoutBanner() {
const { data, isTerminal, error } = useOrderStatus();
if (error) return <p>{error.detail}</p>;
if (!data) return null;
if (data.status === "completed") return <p>You're in!</p>;
if (data.status === "failed") return <p>Payment failed.</p>;
if (data.status === "refunded") return <p>Refunded.</p>;
return <p>Finalizing…</p>;
}
Poll an order from a URL parameter (post-redirect)
function ReturnPage({
eventId,
orderId,
token,
}: {
eventId: string;
orderId: string;
token: string;
}) {
const { data, isTerminal } = useOrderStatus({ eventId, orderId, token });
return isTerminal ? <Receipt orderId={data!.orderId} /> : <Spinner />;
}
Slower poll cadence (low-priority background)
function BackgroundStatus() {
const { data } = useOrderStatus({ pollIntervalMs: 10_000 });
return data ? <small>Status: {data.status}</small> : null;
}
Stop polling on a fatal error
import { isRiftApiError, useOrderStatus } from "@feelrift/react";
function GuardedPoll() {
const { data, error, stop } = useOrderStatus();
useEffect(() => {
if (
isRiftApiError(error, "order_status_token_expired") ||
isRiftApiError(error, "order_status_token_tampered") ||
isRiftApiError(error, "order_not_found")
) {
stop();
router.push("/orders/recover");
}
}, [error, stop]);
return data ? <p>{data.status}</p> : null;
}
Related
useCheckout— persistsorderIdandorderStatusTokenon success.usePaymentStatus— separate, narrower poll for the Stripe authorization side. Public types exported from@feelrift/react.- Error handling — the narrowing pattern this hook expects consumers to use.
- Securing the order-status token —
how to carry the
tokenacross a Stripe redirect without leaking it into history, theRefererheader, or server logs.