Skip to main content

FinTech · Java & Spring Boot

Multi-Bank Funds Transfer Service

Led backend development of a funds-transfer service operating across 8+ partner banks, plus the mobile-money integration that extended the platform into MTN's ecosystem.

Organisation
Baxi by Onafriq
Role
Senior Software Engineer
Period
Feb 2020 – Jul 2026
Domain
FinTech · Payments

Sanitised and conceptual. This describes the shape of the problem and the engineering approach in generalised terms. It is not a representation of any employer’s proprietary architecture, source code, data or internal metrics.

Context#

Baxi by Onafriq is a payments business. Money moves through it, which changes the engineering calculus in a way that is hard to appreciate from the outside: a feature that is 99% correct in most software is a feature that is broken in this one. Over six years there I worked across the platform's backend, and the piece I led was the FIAP Transfer Service, the service responsible for streamlining financial transfer operations across 8+ partner banks.

The engineering problem#

A transfer service is mostly other people's systems.

Each partner institution exposes its own contract. The field names differ, the authentication differs, the timeout behaviour differs. Most consequentially of all, the semantics differ. One provider's synchronous success response means the money has moved. Another's means the request was accepted for processing and you will find out later. A third will time out on the wire while completing the transaction on their side.

So the problem is not "call an API." It is: build something that presents a single coherent transfer interface to the rest of the platform while absorbing the fact that no two dependencies behave the same way, and that some of them will be down when you need them.

What I owned#

  • Led backend development of the FIAP Transfer Service in Java and Spring Boot, covering transfer operations across 8+ partner banks.
  • Integrated the B2B middleware with MTN's Mobile Money (MoMo) API, extending the platform into MTN's mobile-money financial ecosystem. A different class of endpoint from a bank, with its own lifecycle.
  • Worked inside the engineering leadership team through this period, mentoring junior developers and coordinating sprint planning and delivery around the work.

Conceptual architecture#

Multi-bank funds transfer, conceptual integration surface

Consumers

  • B2B middlewarePlatform callers
  • Internal servicesServer-to-server
Transfer serviceJava · Spring Boot

External providers

  • Partner bank adapters8+ institutions
  • Mobile moneyMTN MoMo API
  • Settlement & reconciliationAsynchronous
  • Transaction ledgerSystem of record
A funds-transfer service is a translation layer. Each partner institution exposes a different contract, a different timeout profile and a different notion of a settled transaction; the service's job is to absorb that variance so callers see one interface. The CV records this service operating across 8+ partner banks.

Engineering considerations in a service of this shape#

The following are the general principles that govern funds-transfer services, described here as discipline, not as a disclosure of any particular implementation.

Idempotency is not optional. Any operation that moves money needs a client-supplied idempotency key and a server-side record of what that key already produced. Networks retry. Users press buttons twice. Without idempotency, a retry is a second transfer.

The provider adapter is the unit of change. Isolating each institution behind its own adapter, with its own mapping, its own timeout profile, its own error translation, means onboarding a ninth bank does not require reasoning about the first eight. It also means one misbehaving provider is contained rather than systemic.

Pending is a real state, not a failure. The naive model has two outcomes, success and failure. The correct model has three, and the third one, we do not know yet, is where most of the engineering lives: status polling, callback handling, and a reconciliation path that closes the loop.

Reconciliation is a first-class feature. At some point somebody will ask what happened to a specific transaction on a specific day. A system that cannot answer that question precisely is not finished, regardless of how well it performs on the happy path.

Walk it yourself#

Interactive · transaction lifecycle

Pick a scenario and step through it. The interesting one is not the happy path.

The connection dies mid-flight. You do not know whether the money moved. This is the state most systems model wrong.

  • Initiated
  • Pending
  • Indeterminate (current state)
  • Settled
  • Failed
  • Resolved

Step 1 of 4

→ Request sent, socket times out

Mark the transaction indeterminate. Do not release the hold, do not fail it, do not retry it as a fresh operation.

Where this goes wrong, Treating a timeout as a failure is the single most expensive bug in payments. The retry becomes a duplicate transfer.

Provider times out

Generalised from leading backend development of a multi-bank funds-transfer service in Java and Spring Boot across 8+ partner banks, and integrating a mobile-money API and 14+ VAS billers into a B2B middleware. It describes the discipline, not any employer's implementation.

Alongside the transfer service I built the platform's wider integration surface, 14+ value-added-service billers and the aggregation layer that sat under them. That work is written up separately in Biller Aggregation & Value Added Services.

Next case study

Biller Aggregation & Value Added Services

Integrated 14+ VAS billers (airtime, electricity, betting) into a B2B middleware, and built the aggregation platform and reporting service underneath them.

All case studies

Contact

Let’s Build Something Meaningful

I’m open to senior and lead engineering roles internationally, backend, full-stack, architecture and platform work. The fastest route is email; LinkedIn works just as well.