Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
The whiteboard version had three parts: virtual accounts, payouts to Nigerian banks, and customer funds that earn interest. Each had a handful of endpoints. According to Tobiloba’s DEV Community post, Building a Fintech Infrastructure Platform From Scratch, the system that was actually built was far larger: 94 entity types, more than 100 database migrations, five authentication schemes, two virtual-account providers, a general ledger, webhook retries, and distributed job locking. The gap between those two pictures was less about features than about correctness when things fail and about the operations needed to see and recover from failure.
Every figure here is the author’s own account of a project they built. None has been independently verified, and the post is not an audit or a benchmark of how fintech platforms are normally built. The post page shows “Posted on Apr 17” without a year, so this article does not assign one.
The brief and the whiteboard version
The product was a multi-tenant API that fintech companies could use to provision virtual bank accounts, send payouts to Nigerian banks, and hold customer funds with interest accrual. The author names neobanks, savings applications, and lending products as the kind of companies that would integrate. Each tenant is a company, identified by a CompanyId that has to be carried through the data layer, the services, and the request path.
The first sketch looked like account management, payments, and interest, with a few endpoints for each. The author’s framing of what came next is blunt: “Here’s the gap between the whiteboard and the reality.” The sections below cover the parts of that gap the post treats as most important.
#1 Best Overall
What happens if the HTTP request to the payment provider times out after we’ve sent the money but before we get the confirmation?
The author calls retry safety the first major lesson. If a provider processes a transfer but the response never reaches the caller, the caller cannot tell whether money moved. A naive retry can then move it twice.
The baseline defence in the post is a client reference that must be unique per company and is checked before processing begins. That handles the simple case: a request that completed is repeated. The harder cases are the ones the author spends most of the section on:
- A duplicate arrives while the original request is still in flight, so there is no final result to return yet.
- The provider has processed the transfer, but the local system has not recorded that outcome.
- The provider and the local system disagree about whether the payment succeeded.
- A partial failure leaves some steps complete and others not.
The author’s point is that the correct response to a duplicate depends on which state the original request is in, and that defining those responses is the real work. Working through those edge cases took the author two days. That is a personal effort estimate for this project, not a general figure for payment-system work.
Rank #2
A team building something similar would need explicit answers to questions like these before writing the handler:
- Has this client reference already been seen for this company?
- If so, is the original request still in flight, finished, or in an unknown state?
- If it finished, what does the duplicate receive back?
- If the provider’s outcome is unknown, what does the caller see, and when is the transfer reconciled?
Provider abstraction and the second integration
The virtual-account side and the payout side each got their own interface. Runtime resolvers choose the provider implementation for a given request. The author reports that providers differ in APIs, credentials, error codes, rate limits, and webhook behaviour, so an interface has to absorb all of those differences rather than only the happy path.
The post’s caution is about timing. The author retrofitted an abstraction after the first integration, and that left provider-specific assumptions in shared code. Adding the second provider took about a week. The author attributes most of that time to documentation review and credential handling, plus careful refactoring to remove assumptions carried over from the first integration. That is an anecdotal project estimate, not a typical integration duration. In the post, the providers appear only as Bank A and Bank B.
Why a transaction history does not explain a balance
The author separates two problems that look similar. A transactions table answers “what events happened?” A balance has to answer “how did we get here, and does it add up?” A list of credits and debits can record events without making the resulting balance easy to explain.
The post describes a general ledger built from accounts, journals, and journal lines. Each journal must have equal total debits and credits, and an unbalanced journal fails at commit. The invariant is enforced in the write path rather than checked afterwards in a report. Foreign-exchange conversion rates are recorded when the conversion executes, so a later reader can see which rate applied to an earlier movement.
These are the author’s implementation choices, presented as their own design rather than a prescribed one.
Rank #4
Webhooks are a reliability contract
The author puts the point directly: “Webhooks are not just sending HTTP requests. They’re a reliability contract.” The post splits the work into inbound and outbound sides.
Inbound credits
For inbound credits from the virtual-account providers, the system stores the provider’s transaction reference under a per-company unique constraint. A duplicate delivery runs into that constraint and is treated as already processed, so the same credit is not applied twice.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsOutbound notifications
On the outbound side, the platform notifies tenants of events. The author reports the following:
Best Value
- HMAC-SHA512 signing, so receiving systems can verify where a notification came from
- Retries when delivery fails
- Delivery status records
- Replay of missed events
- A testing mode that runs without live transactions
- A delivery history that support staff and tenants can inspect
The operational point is that status, replay, testing, and history make up most of the scope. Sending the HTTP request is the small part.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Tenant isolation in three layers
The post describes three layers of enforcement:
- The database layer, where CompanyId constraints apply to the data itself.
- The service layer, where a company context is injected into service code.
- The authentication middleware, which validates a tenant-bearing token before a request reaches a controller.
The author argues that each layer catches a different class of mistake, which is why keeping all three matters more than trusting any one. The author puts the argument this way: “Defense in depth is not paranoia in financial software. It’s the minimum.”
Layers reduce the chance that one mistake exposes another tenant’s data. They do not by themselves make a system secure or compliant, and the post does not claim they do.
Free tools Windows power users keep installed
One-click scans. No signup required.
Design trade-offs at a glance
The four choices above can be compared against the simpler option each replaced. These are the author’s descriptions of the approaches, not a controlled comparison.
| Design question | Simpler option and what it gives now | Author’s approach and what it gives later or in addition |
|---|---|---|
| Provider integration | One provider with provider-specific code: simplicity now | Provider abstraction with separate interfaces and runtime resolvers: switching and failover flexibility later |
| Recording money movement | Transactions table: basic event recording | Double-entry ledger: explainable, balanced account movements |
| Outbound notifications | Best-effort HTTP sends: simplicity | Managed delivery lifecycle: deduplication, authenticated delivery, retries, replay, and observability |
| Tenant data access | Filtering in one layer: fewer implementation points | Layered enforcement: multiple safeguards against data-access mistakes |
Reading the status and scale claims
- The counts of entity types, migrations, authentication schemes, and providers describe the size of the codebase. They do not measure quality, reliability, or security.
- The post says the platform was in production and onboarding companies at publication. That is self-reported and time-sensitive. The post names no customers and gives no transaction volumes, loss rates, or reliability metrics.
- The article is a single first-person engineering account. It supports what the author says they built and learned, not claims about the fintech industry as a whole.
- The post is not regulatory guidance. It does not establish which Nigerian licences, safeguarding rules, or partner-bank obligations applied to the platform. Any team building in this space has to settle those questions separately.
Features versus correctness properties
The author’s closing principle is to treat correctness as invariants that must hold under failure, rather than as a feature checklist or a suite of passing tests. In the author’s words: “I think that reframe from features to correctness properties is the most useful thing I took out of this project.”
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

