Payments from checkout through the processor, a declined card going round a smart retry before it goes through, and each paid one landing on the invoice, receipt and ledger at its own pace; live counts on every card and the day's total climbing. Pauses, and stacks on phones.
When to use
- Payments from checkout through the processor, a declined card going round a retry, money landing.
- Payments and billing products; a live view of your own payments.
When not to use
- Shops that simply take payments: Orders table shows what matters to them.
- Reconciling money: an invoice or a ledger table.
Usage
Payment flow with your own data: every prop has the sample as its default, so pass only what you have.
import { useEffect, useRef } from "react"
import { PaymentFlow, type PaymentsHandle } from "@/components/dashral/blocks/payment-flow"
const payments = useRef<PaymentsHandle>(null)
useEffect(() => webhooks.on("payment", (p) => payments.current?.send({ label: p.customer, amount: p.amount, declined: p.declined })), [])
<PaymentFlow ref={payments} mode="live" outputs={payouts} />API reference
PaymentFlow
The section: a heading, the flow in a dotted panel with a Live badge and Pause, and a row of figures. Every part comes from props, with a sample flow by default.
| Prop | Type | Default |
|---|---|---|
eyebrow | React.ReactNode | "Payments" |
title | React.ReactNode | sample |
description | React.ReactNode | sample |
checkout | FlowCardWhere payments start, the card a declined one waits on, and what each paid one goes to. | sample |
retry | FlowCard | sample |
outputs | FlowCard[] | sample |
processor | { label: string; mark: React.ReactNode; color: string }The processor in the middle; under its name, the day's total, from | sample |
today | numberWhere the day's total starts; every payment that goes through adds its amount. | 128_430 |
stats | { value: string; label: string }[]The figures under the diagram; none hides the row. | sample |
mode | PaymentsProps["mode"]With your own charges instead of sample payments (see Payments' mode="live"). | — |
ref | React.Ref<PaymentsHandle> | — |
Payments
Payments from checkout through a processor to what follows them. Each payment leaves checkout as a hollow dot and is authorized in the processor (its outline pulses); about one card in five is declined at first: the dot turns amber, goes down to the retry card, waits, and comes back to go through. A paid payment becomes a solid square that goes to the ledger and, most of the time, the invoice and the receipt, each copy at its own pace. Cards light up and keep live counts. Side by side when there's room, stacked on a narrow container. One requestAnimationFrame loop writes the SVG and the counts directly; React only hears about payments going through. Sample payments by default; with mode="live", only the ones you send (see PaymentsHandle). It stops off screen, in a hidden tab, when paused and (live) with nothing on its way; with reduced motion it's a still picture.
| Prop | Type | Default |
|---|---|---|
checkout | FlowCardRequired. | — |
retry | FlowCardRequired. | — |
outputs | FlowCard[]Required. | — |
processor | { label: string; mark: React.ReactNode; color: string; description: React.ReactNode }Required. | — |
speed | numberHow fast payments flow: 1 is the usual pace, 2 twice as fast, 0 holds them still. | 1 |
paused | booleanBrings the flow to rest where it is (easing down, not stopping in one frame). | false |
onPaid | (amount: number) => voidCalled as each payment goes through, with its amount, e.g. to add up the day. | — |
mode | "demo" | "live""demo" plays sample payments on its own; "live" moves one only when you send it (see PaymentsHandle). | "demo" |
ref | React.Ref<PaymentsHandle> | — |
className | string | — |
type FlowCard
| Prop | Type | Default |
|---|---|---|
id | string | — |
label | string | — |
description | string | — |
detail | stringMore about it, in a tooltip on hover and keyboard focus (touch screens don't show it, so nothing essential). | — |
icon | LucideIcon | — |
metric | { unit: string; value?: number; base?: number }A count on the card, then | — |
type PaymentsHandle
Moves payments through a live flow (mode="live").
| Prop | Type | Default |
|---|---|---|
send | (payment: { label: string; amount: number; declined?: boolean; to?: string[] }) => voidSends one payment from checkout: | — |