Lesson 47Payments railsIntermediate

Instant payment rails

NIBSS, UPI, Pix, FedNow, RTP, same pattern, different country code.

By Solomon Ajayi · Free to read, no signup

Every modern economy ships a real-time payment system in the 2010s-2020s: NIBSS Instant Pay (Nigeria 2011), IMPS/UPI (India 2010/2016), Pix (Brazil 2020), FedNow (US 2023), RTP (US 2017), FAST (Singapore), SEPA Instant (EU). Different jurisdictions, identical engineering shape: 24/7, sub-minute settlement, irrevocable on receipt. The fintech's ledger gets a credit on arrival and immediately marks the funds cleared. The interesting accounting happens at the BOUNDARY: what to do when a customer sends to a wrong recipient, when settlement fails mid-flight, and how interoperability fees flow.

Every country built its own instant rail in the last fifteen years, and from a distance they look like national variations. From inside the ledger they are the same payment. NIBSS, UPI, Pix, FedNow, RTP, all of them are 24/7, settle in under a minute, and are irrevocable on receipt. So the happy-path accounting is boring and identical everywhere: bank account up, user wallet up, and a flat scheme interchange fee booked as expense in the same atomic transaction so reconciliation against the bank statement matches to the kobo.

The hard part is not the happy path; it is the credit you cannot place. Money lands in your bank with a reference that matches no user, a typo, a closed account, junk data, and unlike a card you cannot bounce it back. The cash is genuinely yours to account for, so you park it in a Rail Suspense liability: you owe someone this money, you just do not know who yet. When ops later identifies the user, the release is a pure reclassification from Suspense to User Wallet, with no bank movement because the cash was already there.

Suspense is a holding pen, and the discipline is to keep it small and young. A growing or aging suspense balance is a red flag in any audit, because it means money you took in has been sitting unattributed for too long, and the release is a judgement call that needs an actor and a reason code on the entry. The real engineering, meanwhile, lives in the listener: a reliable consumer of the rail's notifications, idempotent on the scheme reference, reconciled against the bank statement on a tight loop.

Worked example, step by step

Inbound rail credit (the happy path)

A user sends ₦10,000 to your fintech via NIBSS Instant Pay. Your bank receives a credit. The user's NIBSS reference comes with it. Your inbound listener matches the reference to a user account and credits the wallet within seconds.

Inbound NIBSS deposit ₦10,000 + ₦50 interchange
AccountDebitCredit
Bank Account (1200)₦10,000.00
Rail Interchange Expense (5200)₦50.00
User Wallet (2000)₦10,000.00
Rail Receive Fee Revenue (4200)₦50.00

Bank Account UP ₦10,000 (asset, debit). User Wallet UP ₦10,000 (liability, credit). A scheme like NIBSS often charges a flat ₦50 inbound interchange that comes out of YOUR pocket (not the user's). We book that as an expense in the same atomic transaction so reconciliation matches.

Reference doesn't match: park in suspense

Another inbound ₦4,000 lands but the reference doesn't match any user (typo, closed account, junk data). Real rails don't let you reject, money is in your bank, you have to account for it. Park it in a SUSPENSE liability until ops figures out where it should go.

Inbound NIBSS ₦4,000 (unmatched, suspense)
AccountDebitCredit
Bank Account (1200)₦4,000.00
Rail Suspense (2150)₦4,000.00

Bank Account UP ₦4,000 (debit). Rail Suspense UP ₦4,000 (liability, credit). Suspense is a holding pen: you owe SOMEONE this money, you just don't know who yet. Critical to size and age this account, old suspense balances are a red flag in any audit.

Ops identifies the user, releases suspense

Ops digs in, the ₦4,000 was meant for a user whose account number on file changed. Manual mapping fires the release: suspense clears, user wallet credits.

Release suspense ₦4,000 to identified user
AccountDebitCredit
Rail Suspense (2150)₦4,000.00
User Wallet (2000)₦4,000.00

Rail Suspense DOWN ₦4,000 (debit). User Wallet UP ₦4,000 (credit). The bank account doesn't move, the cash was already there. This is a pure RECLASSIFICATION between two liability accounts. Audit trail (metadata: actor=ops-tool, reasonCode=suspense_release) matters here because someone made a JUDGEMENT call.

Takeaway

Instant rails are converging globally on the same pattern: 24/7, sub-minute, irrevocable, scheme-fee per transaction, with mandatory suspense handling for unmatched credits. The accounting shape is short and the same everywhere, Bank UP, User Wallet UP, scheme fee booked as expense/revenue. The infra work is in the LISTENER: a reliable consumer of the rail's webhook/notification, idempotency keys per scheme reference, reconciliation against bank statement every ~10 minutes. Get those three right and the ledger follows naturally.

Practice this on a real ledger

Reading is half of it. Open this lesson in the lab to post the entries yourself against a real Postgres-backed double-entry ledger, with the validation on. Free, your sandbox is yours.

More in this section

Search lessons

Type to find any of the 85 lessons. Press Enter to open.