Transaction State Machine
Real PassimPay statuses → mapping to Unified Interface
State Diagram
Happy Path
Alternative Paths
Additional Information
Click any state for details
H2H (Host-to-Host)
amountReceive for creditingInvoice (Redirect)
waiting = partial paymenterror = expired or partial failWhat is State Machine and who needs states
State Machine is not a UI component and not a settings table. It is rules in code about which state a transaction can transition to and on which event. Lives on the backend.
Where states come from
States are defined in code as constants -- a set of strings (INITIATED, PROCESSING, COMPLETED, etc.) plus a table of allowed transitions: from which state to which, and on which event (PSP webhook, TTL job, manual operator action). This page is documentation of that logic, so that all three parties (product, developer, PSP) speak the same language.
Who sees states and in what form
| Party | What they see | Source |
|---|---|---|
| Player (frontend) | Simplified status: "Waiting for payment", "Credited", "Error" | GET /payments/:id/status → UnifiedStatus → frontend maps to human-readable |
| Back office / ops | Full UnifiedStatus + transition history from transaction_events | DB query or internal dashboard (Phase 4) |
| Developer | Raw states in DB + full log in transaction_events | Direct DB access or logs |
| PSP (PassimPay) | Their own statuses (approved, rejected, etc.) | Webhook that PSP sends to us |
A webhook from the PSP arrives with a PSP-status (passimpay: approved) → State Machine maps it to UnifiedStatus (COMPLETED) → updates transactions.status → writes an event to transaction_events.
Why the UnifiedStatus layer exists
If PSP #2 is connected tomorrow, it will have its own statuses (paid, declined, hold). The frontend, back office and business logic keep working with the same COMPLETED / FAILED -- nothing changes. The PSP #2 adapter simply maps its statuses to UnifiedStatus.
Practically -- what needs to be done
Implement stateMachine.transition(txId, newStatus, payload) that: verifies the transition is allowed → updates transactions.status → writes a record to transaction_events. Called from Orchestrator, Cascade Manager, webhook handler, and TTL job.
Show the full log from transaction_events -- every transition is visible with timestamp and reason.
Map UnifiedStatus to player-facing text. PENDING_CONFIRMATION → "Awaiting network confirmation". COMPLETED → "Credited".