Worked float example
Assume an agent has ₦40,000 available and submits a ₦30,000 transfer with operation ID F-01. The first request reserves ₦30,000. A second F-01 request arrives before the bank answers. Expected: ₦10,000 remains available, one reservation exists, and the second request returns the state of F-01. If the bank accepts, one debit and one recipient credit replace the reservation. If the bank rejects, release the reservation once. A new operation ID for another ₦30,000 request must fail while F-01 is pending. This example is synthetic. It tests the balance invariant, not a real agent incident.
Map the float path
Draw agent wallet, super-agent wallet, bank settlement account, and the ledger as separate boxes. Mark who can start, approve, cancel, and reverse each move. A device ID does not prove the agent owns a wallet. Check the authenticated actor against the stored agent relationship on every request.
Checks to run
- Bind each transfer to the authenticated agent and destination account; reject an agent ID supplied only by the client.
- Reserve or debit the source once inside a transaction. Replays with the same operation ID must return the first result.
- Require a separate approval for manual float correction and keep the original transaction linked to it.
Pass and fail examples
Pass: a second request with the same key returns the original transfer and no new ledger debit. Fail: the agent balance falls twice while the recipient receives one credit. Another fail is a manual correction entered by the same person who requested it. Test a transfer to an account outside the agent hierarchy as well as a permitted account.
Float limits must be checked against available funds after reservations, not against a balance cached at login. On provider uncertainty, hold the reservation until the provider outcome or a confirmed cancellation supports release.
One more boundary test
Now change the destination after the first request while keeping operation ID F-01. The server must reject the changed payload or return the original result; it must not treat the same ID as a new instruction. Next, cancel a pending transfer at the same moment the bank sends success. Only one final state may win. If success wins, cancellation cannot release the float and leave the recipient credited. If cancellation wins before bank submission, no recipient credit should appear. Record the order of events because a final balance alone may hide a short period of double spending.
Reconcile a failed float move
For a second check, let F-01 reach the bank, then drop the response. The agent still has ₦10,000 available and ₦30,000 reserved. Query the bank with F-01. If the bank says paid, post the final debit and matching destination credit once. If the bank says rejected, post a linked release of ₦30,000. If it says pending, leave the reserve in place. Compare the source ledger, destination ledger, and bank record; a screen balance alone cannot prove settlement. Paystack says to reuse the same reference when a transfer result is not conclusive. The same rule makes this test safe to rerun.
Keep uncertain float reserved
For synthetic F-01, the source starts with 40,000 NGN and holds 30,000. A reservation timer expires while the provider result is still unknown. Expiry alone must not make the held 30,000 spendable if the provider can still complete the transfer. Query the original reference and escalate the unresolved hold to its owner. Release only under a rule that establishes failure or cancellation; otherwise a later success can leave the source overdrawn. In the no-fee example, one final source debit equals the destination credit. With fees, reconcile source debit to destination credit plus named fee entries and settlement accounts. Record which ledgers the test covers.
Primary sources
Next step
Start with one transfer that times out after the source wallet is reserved. Compare all balances with the bank result before release. Read the related guide. For a review of your own system, request a security review.