Building a Reversals and Disputes Engine for a Digital Wallet Platform
Reversals and disputes are among the hardest engineering problems in wallet platforms. Getting them wrong means funds disappear, double-credits appear, and reconciliation becomes unmanageable. This is the architecture that handles them correctly.
Most wallet platforms handle the happy path well. A user initiates a transfer, funds move, both balances update correctly. The engineering complexity that accumulates quietly over time is the unhappy path: what happens when a transaction needs to be reversed, when a user disputes a charge, when a merchant claims funds they never received, or when a regulatory order requires a freeze and subsequent return of funds.
These scenarios are handled inconsistently across most wallet platforms because they were not designed for at the outset. The result is a collection of manual processes, ad hoc database corrections, and reconciliation exceptions that grow more expensive to manage as volume increases.
This article covers the architecture of a reversals and disputes engine that handles these cases correctly, consistently, and at scale.
Why reversals are not the inverse of the original transaction
The most common mistake in reversals design is treating a reversal as the mirror image of the original transaction. The original debited wallet A and credited wallet B, so the reversal credits wallet A and debits wallet B. Simple.
This fails in practice for several reasons.
The time between the original transaction and the reversal is not zero. In that interval, the recipient may have spent the funds. Debiting wallet B for the reversal amount when wallet B no longer holds those funds creates a negative balance. If the platform prevents negative balances - which it should - the reversal fails. The original sender is waiting for funds that cannot be returned because the system cannot process the debit.
The correct model is to treat a reversal as a new transaction in its own right, not as an undo of the original. The reversal creates new ledger entries with their own timestamps, their own idempotency keys, and their own state machine. The link between the reversal and the original transaction is a reference, not a mechanical inverse. The business logic for what happens when a reversal cannot complete - because the recipient has insufficient funds - is a separate concern from the reversal transaction itself.
The reversal state machine
A reversal is not binary. It is not simply initiated and then either completed or failed. A production reversal engine needs to handle intermediate states that arise from the distributed nature of the platform.
The states a reversal moves through:
Requested - the reversal has been initiated but no funds have moved. This is the entry point for all reversals regardless of source.
Recipient debit pending - an attempt to debit the recipient's wallet is in progress. The system holds a pending debit entry on the recipient's ledger.
Recipient debit failed - the recipient does not have sufficient funds. The reversal cannot proceed automatically. Human review or an alternative resolution path is required.
Recipient debit completed - funds have been removed from the recipient's wallet and are held in a transit account.
Sender credit completed - funds have been credited to the sender's wallet from the transit account. The reversal is complete.
Cancelled - the reversal was requested but cancelled before completion, typically because the parties reached an alternative resolution.
Every state transition must be recorded as a ledger event. The current state is derived from the event log, not stored as a mutable status field. This ensures that the reversal history is complete and auditable regardless of what failures occur during processing.
Disputes are not reversals
A dispute is a claim that a transaction was unauthorised, incorrect, or unfulfilled. A reversal is the resolution mechanism that may or may not follow from a dispute. They are separate processes with separate state machines, and conflating them creates a platform that cannot manage either correctly.
The dispute process:
A user or merchant raises a dispute against a specific transaction. The dispute is logged with a timestamp, the disputing party, the disputed amount, and the reason code. The disputed funds may be frozen - removed from the available balance of one or both parties - while the dispute is investigated. The investigation produces a finding: the dispute is upheld, partially upheld, or rejected. The resolution triggers a reversal, a partial reversal, or no action.
This process requires several capabilities that are frequently absent from wallet platforms built without an explicit disputes design:
Fund freezing without ledger modification. Freezing disputed funds means they are unavailable for spending but are not yet moved. The common implementation mistake is to move funds to a holding account on dispute initiation. This creates ledger entries that must be reversed if the dispute is rejected, compounding the reconciliation complexity. The correct approach is a freeze flag on the ledger entry or a shadow balance that reduces the available balance without creating a transaction.
Partial resolution. A dispute for 500 XOF may be partially upheld for 300 XOF. The resolution engine must be capable of splitting the original transaction for resolution purposes, creating a reversal for 300 XOF and releasing the freeze on the remaining 200 XOF.
Reason code tracking. Regulatory reporting in BCEAO, CBN, and BCEAO jurisdictions requires dispute reason codes and resolution outcomes to be reported. The disputes engine must capture and store reason codes in a format that maps to the reporting requirements of the relevant regulatory authority.
Related: Ledger Design for Wallet Platforms
The transit account pattern
All reversals and dispute resolutions should move funds through a transit account rather than directly between wallets. The transit account is a platform-controlled ledger account that acts as the intermediary in every multi-step fund movement.
The sequence for a reversal:
- Debit the recipient wallet, credit the transit account
- Debit the transit account, credit the sender wallet
If step 1 completes and step 2 fails, the funds are held in the transit account. They are not lost. The platform can retry step 2, escalate for manual resolution, or initiate a different resolution path - but the funds are in a known location with a clear audit trail.
Without a transit account, a partial completion leaves funds in an ambiguous state: debited from one wallet but not credited to another. The only way to find them is to compare total ledger debits against total ledger credits and identify the discrepancy - which is exactly the kind of reconciliation work that scales poorly as volume increases.
The transit account balance should be zero at the end of every business day under normal operating conditions. Any non-zero transit account balance at end of day is a signal that one or more reversals or dispute resolutions are in an incomplete state and require attention.
Regulatory freeze orders
A category of fund freeze that wallet platforms in MENA and Africa increasingly need to handle is the regulatory freeze order: a directive from a central bank, financial intelligence unit, or court to freeze the funds in a specific wallet pending investigation.
This is distinct from a dispute freeze. A regulatory freeze may cover the entire wallet balance, may have a specified duration, and may require specific reporting to the issuing authority. It cannot be reversed by the account holder and may not be visible to them depending on the jurisdiction.
The architectural requirement is a freeze mechanism that operates at the wallet level rather than the transaction level, that integrates with the platform's compliance reporting pipeline, and that maintains a complete audit trail of when the freeze was applied, by whom, under what authority, and when it was lifted.
Platforms that handle regulatory freezes through manual database intervention - rather than through a designed compliance operations workflow - accumulate audit exposure with every freeze event. When the regulatory authority requests a report of all freeze orders and their resolution, the platform that has been handling them manually cannot produce it cleanly.
Idempotency in the reversals engine
The reversals engine has the same idempotency requirements as the original transaction processing layer, but the failure modes are more damaging. A duplicate payment is a customer service problem. A duplicate reversal - crediting the sender twice - is a financial loss and a reconciliation problem that may not be detected until the monthly close.
Every reversal request must carry an idempotency key. The engine must insert the key before processing begins. Retries on the same key must return the current state of the reversal, not initiate a new one. The idempotency key must be scoped to the specific reversal action, not to the original transaction - a transaction may have multiple partial reversals, and each must be idempotent independently.
Related: Why Wallet-to-Wallet Transfers Break at Scale
Reconciliation integration
The reversals and disputes engine is not complete without integration into the platform's reconciliation process. Every reversal and dispute resolution creates ledger entries that must be accounted for in the end-of-day reconciliation.
The reconciliation process must:
- Account for in-flight reversals - reversals that were initiated before the reconciliation period closed but not completed until after
- Reconcile transit account balances against open reversal items
- Produce a report of disputed funds frozen at period end
- Match regulatory freeze orders against their corresponding ledger entries
Platforms that build reversals without explicit reconciliation integration discover the gap at month-end when the ledger does not balance and the only way to find the discrepancy is to audit individual transactions.
Related: The Hidden Costs of Manual Reconciliation
A reversals and disputes engine built correctly is invisible to operations teams - cases resolve through automated workflows, the ledger balances at end of day, and regulatory reporting is producible on demand. Built incorrectly, it becomes the source of the most expensive manual work on the platform. Start with a scoped architecture assessment if your platform is handling reversals or disputes through manual processes.
Our wallet and ledger engineering service covers reversal engine design, dispute workflow architecture, and reconciliation integration for wallet platforms at any stage of maturity.
CoreInnovate
Working on a payment platform challenge?
Our specialist engineers work directly with payment gateways, wallet providers, and fintech platforms. Start with a scoped architecture assessment.