A field guide by Olatunde Success to integrating SafeHaven MFB virtual accounts, Paystack direct-debit mandates, and Sudo Africa card issuing into a production NestJS backend, with the failure modes the documentation does not mention.
Building Cirva meant wiring a NestJS backend into three different Nigerian financial rails: SafeHaven MFB for NUBAN virtual accounts, Paystack for direct-debit mandates, and Sudo Africa for card issuing. The documentation for each covers the happy path. Production covers everything else. This is the guide I wish someone had handed me before the first integration.
Virtual accounts: provisioning is the easy half
Provisioning a NUBAN through SafeHaven is a single API call. The real work is what happens after: inbound transfer webhooks arrive at least once, sometimes late, occasionally out of order. Treat the webhook as a hint, not a fact. Credit the wallet only after the event is deduplicated by provider reference, persisted, and matched by a reconciliation worker against provider statements. Users transfer odd amounts, transfer twice, and transfer from banks that mangle narration fields. Your matching logic needs to survive all three on day one.
Direct debit: mandates are a lifecycle, not a switch
A Paystack mandate moves through authorization, activation, and eventual revocation, and each state change arrives asynchronously.
Schedule debits through a queue with retry budgets. A failed debit is normal life, not an error: balances dip on payday minus one.
Keep the mandate state machine in your own database. When a customer disputes a charge, you want your own audit trail, not a support ticket to the provider.
Card issuing: the ledger is yours
Sudo Africa handles the card network side, but authorization decisions ride on your balance data. That makes your ledger latency part of the card experience. We keep spendable balance in a fast read path, precompute holds, and answer authorization webhooks well inside the timeout, because a slow response is a declined card in someone’s hand at a supermarket in Ibadan.
The cross-cutting rules
Every rail gets the same treatment: idempotency keys outbound, event deduplication inbound, append-only audit logs, and a reconciliation job that assumes the provider and your database will disagree eventually. KYC data collected along the way, BVN and NIN especially, is encrypted at the field level with AES-256-GCM before it ever reaches a row. None of this is optional in a post-NDPA world, and none of it is hard if you design it in from the start.


