Journal · September 28, 2026

Designing a payment state machine you can explain to support

Fourteen states, each with a reason to exist. How we kept the FeitPay lifecycle precise without making it unreadable.

Payment states end up in support tickets, finance exports and customer emails. If a state cannot be explained in one sentence, it will be misunderstood — usually at the worst moment.

One authority for transitions

Every transition is decided in one place: the gateway’s state machine. Background workers never write a status directly. They record evidence (an observation, a new chain tip, an expiry) and ask the state machine to re-evaluate. The machine locks the payment row, computes the target state from the evidence and applies the transition only if it is allowed. Each change writes a state event and an outbox message in the same database transaction.

The happy path

created → awaiting_payment → detected → confirming → confirmed

Most payments only ever touch these five states. Each has a plain-language description that appears in the dashboard, the checkout and the API documentation.

The honest exceptions

The remaining states exist because reality is messy:

State Plain meaning
underpaid Less than the quoted amount arrived and confirmed.
overpaid More arrived than was quoted. The order can be fulfilled; the excess needs a decision.
expired The quote ran out before anything was observed.
late_payment Something arrived after expiry. It is recorded, never silently credited.
reorged A block with this payment was replaced. Do not fulfil until it confirms again.
manual_review Held for a documented decision.
resolved A person resolved it, with a reason that stays on record.
failed / canceled Ended for a validated reason, or by the merchant before payment.

No colour-only meaning

Each state has a label, an icon and a sentence of explanation, so it reads the same with or without colour, animation or sight. The same vocabulary is shared by every FeitPay interface, so a status means one thing everywhere.

← All journal entries