Payment statuses
Complete state machine: request status, proof status, Trails status, and display logic.
Overview
Every payment in JoPay has three independent status fields that combine to determine what the user sees. Understanding these statuses is essential for integrating with webhooks and building accurate payment UIs.
Request status
The request_status tracks the lifecycle of the payment request itself — whether it has been created, viewed by the customer, or expired.
| Status | Meaning | Transitions to |
|---|---|---|
| requested | Payment request has been created by the merchant. The customer has not yet opened the payment link. | opened, expired |
| opened | The customer has opened the payment link and can see the payment details. | expired |
| expired | The payment request has passed its expiration time without a verified payment. This is a terminal state. | None (terminal) |
Request status diagram
requested ──→ opened ──→ expired
│ ↑
└────────────────────────┘Proof status
The proof_status tracks whether an on-chain payment proof has been attached and verified for this request.
| Status | Meaning | Transitions to |
|---|---|---|
| none | No payment proof has been submitted. The customer has not yet sent USDC. | attached |
| attached | A proof (transaction hash) has been submitted but not yet verified by the Trails engine. | verified |
| verified | The Trails engine has verified the on-chain transfer matches the payment request. This is the terminal success state. | None (terminal) |
Proof status diagram
none ──→ attached ──→ verifiedproof_status is verified. The attached status means a transaction hash has been submitted but the on-chain data has not yet been validated. Do not treat attached as payment verification.Trails status
The trails_status tracks the state of the Trails verification engine as it checks the on-chain proof. This is primarily used internally and in webhook payloads.
| Status | Meaning | Transitions to |
|---|---|---|
| pending | A verification job has been queued but has not started yet. | in_progress |
| in_progress | The Trails engine is actively checking the blockchain for the transaction. | success, failed, cancelled |
| success | Verification succeeded. The on-chain transfer matches the payment request. | None (terminal) |
| failed | Verification failed. The transaction does not match (wrong amount, recipient, asset, or chain). | None (terminal) |
| cancelled | The verification was cancelled (e.g., the request expired before verification finished). | None (terminal) |
Trails status diagram
pending ──→ in_progress ──→ success
│
├──→ failed
│
└──→ cancelledDisplay status logic
The merchant dashboard and payment pages do not show all three status fields separately. Instead, they combine into a single display status that describes the overall state of the payment. The logic is:
| Display status | Condition | Shown as |
|---|---|---|
| Awaiting payment | proof_status = none and request is not expired | Neutral / default state |
| Verifying | proof_status = attached (Trails is checking) | In-progress indicator (spinner or pulse) |
| Paid | proof_status = verified | Green success indicator |
| Expired | request_status = expired and proof_status != verified | Grey / muted indicator |
| Failed | trails_status = failed | Red error indicator |
proof_status = verified above all else. Even if a request has technically expired, if the proof was verified before or shortly after expiry, the display status will show Paid.Complete state diagram
┌─────────────────────────────────────────────────┐
│ PAYMENT LIFECYCLE │
│ │
│ Request: requested → opened → expired │
│ │
│ Proof: none → attached → verified │
│ │
│ Trails: pending → in_progress → success │
│ → failed │
│ → cancelled │
│ │
│ Display: Awaiting → Verifying → Paid │
│ payment │
│ └──→ Expired │
│ └──→ Failed │
└─────────────────────────────────────────────────┘Webhook events and statuses
Webhooks fire on proof status transitions:
payment.proof_attached— Fires whenproof_statustransitions fromnonetoattached.payment.proof_verified— Fires whenproof_statustransitions fromattachedtoverified.
See Webhook events for full payload schemas and signature verification.