Statuses and projections
Why a payment status is a projection, the finality outcomes, and the duplicate-payment caveat.
Every status Tekmerion exposes for a payment is a projection of canonical on-chain truth. It is derived, it may lag, and it is never authoritative on its own. The authoritative record is the finalized on-chain outcome — surfaced as a finality_outcome — and the signed Proof-of-Payment.
Status is a projection, not truth
A status describes where a payment sits in its expected lifecycle. It is rebuilt from canonical finalized outcomes and can change as on-chain truth is confirmed. A status MUST NOT be treated as settlement truth. Reconcile through the API and the Proof-of-Payment, not through a status read or a received notification.
One settlement per intent
A payment_intent_id is settled at most once. When more than one finalized, matched payment exists for the same intent, exactly one is the canonical winner, selected deterministically — independent of arrival order, delivery order, or timing. The winner settles the intent; every other finalized match for that intent is recorded as duplicate_payment and is not an additional settlement.
Finality outcomes
At finality, a payment carries one canonical finality_outcome:
finality_outcome | Meaning |
|---|---|
paid | The deposit was swept to the merchant's approved destination; the protocol fee was collected on-chain. |
refunded | The merchant returned a reject decision with a refund destination; the deposit was refunded; the protocol fee was collected on-chain. |
failed | The payment reached a terminal failure state without settlement or merchant-directed refund. No funds moved. |
duplicate_payment | A finalized, matched payment for an intent that was already settled. The intent's settlement is unaffected. |
Status maps to outcome
| Status | finality_outcome | Meaning |
|---|---|---|
PAID | paid | Swept to the merchant; the intent is settled. |
REFUNDED | refunded | The merchant rejected; the deposit was refunded. |
FAILED | failed | Terminal failure; no funds moved. |
FAILED | duplicate_payment | The payment lost winner selection; the intent is already settled. Not a failed payment. |
EXPIRED | — | The expected window elapsed without a confirmed deposit. A late deposit may still settle. |
HELD | — | Held pending compliance or operational resolution. Not terminal; not an outcome. |
FAILED is not always a failed payment
This is the central caveat. A status of FAILED carrying finality_outcome duplicate_payment means the payment lost the deterministic winner selection for its intent. The payment_intent_id is already PAID through the winning payment, and the merchant has received funds. It does not mean the customer's payment was invalid or that money was not received.
Correlate by payment_intent_id, not by a per-attempt status. A FAILED payment with finality_outcome duplicate_payment is a signal to reconcile an extra on-chain payment — not a signal that a payment did not go through.
The notification plane carries a parallel signal
The same events surface as notifications, where the field that selects meaning is notification_class:
notification_class | finality_outcome | When |
|---|---|---|
payment_observed | null | Deposit observed on-chain at the observed threshold. Progress, not settlement. |
payment_finalized | paid | refunded | failed | A terminal outcome was finalized. |
payment_held | null | The payment is held; no funds moved and no outcome is finalized. |
duplicate_payment_incident | null | An additional finalized payment was detected for an already-settled intent. |
Note the asymmetry between the two planes. On the notification plane, a duplicate is its own class — duplicate_payment_incident, with finality_outcome null — delivered alongside the canonical payment_finalized (paid) record. On the status projection, the same event surfaces as status FAILED with finality_outcome duplicate_payment. Both describe one fact: an extra payment on an already-settled intent. Full notification rules are in the Event catalog.