Selected Work

Payment messaging, core banking and the infrastructure underneath it.

Core banking platform

Multi-Tenant SACCO Core Banking Platform

Owned end to end — leading delivery with a team of 6 engineers

A multi-vendor core banking platform serving many financial institutions from one deployment, covering member and account processing, loan products and transaction workflows, with tenant isolation across the data model.

300+ SACCOs and 10,000+ active customers concurrently

Java Spring Boot Microservices MariaDB Multi-tenancy Hibernate
Multi-tenant SACCO core banking platform Over 300 financial institutions are served from one codebase and one deployment. The tenant is resolved from the incoming request, the core banking application handles members, accounts, savings, loans, transactions and general ledger for more than 10,000 concurrent customers, and per-tenant configuration gives each institution its own product catalogue, loan rules, pricing, charges and limits without a code change. Below the service layer, the data access layer applies a tenant scope to every read and write, so each institution's data stays isolated and a query arriving without a tenant scope does not run. Institutions SACCO A SACCO B SACCO C … 300+ in total tenant resolved Core banking application one codebase, one deployment members · accounts · savings loans · transactions · GL 10,000+ concurrent customers Per-tenant configuration product catalogue loan rules and pricing charges, limits, branding same code, different behaviour Data isolation Data access layer tenant scope applied to every read and every write SACCO A own data SACCO B own data + 300 more own data A query that arrives without a tenant scope does not run. Isolation is enforced below the service layer, not by each caller remembering.
One codebase serving every institution. The isolation sits below the service layer, so it cannot be forgotten at a call site.
Payments messaging

SWIFT MT ↔ ISO 20022 MX Messaging Engine

Owned end to end — leading delivery with a team of 6 engineers

A production messaging engine for bank clients, performing bi-directional transformation between legacy SWIFT MT and ISO 20022 MX. Handles schema validation, field-level mapping where MT free-text has to be decomposed into structured MX elements, and the strict validation gates MX enforces, so payments clear without manual repair.

MT103, MT202, MT940 ↔ pacs.008, pacs.009, camt.053 — built for straight-through processing

Java Spring Boot ISO 20022 SWIFT MT JAXB XML Schema validation
SWIFT MT to ISO 20022 MX conversion pipeline Inbound MT103, MT202 and MT940 messages are parsed from FIN blocks, mapped field by field to ISO 20022 elements, validated against XSD schema and CBPR+ rules, then emitted as pacs.008, pacs.009 or camt.053. Messages failing validation go to a repair queue instead of straight-through processing. A reverse path converts MX back to MT for downstream systems still on FIN. Inbound MT MT103 MT202 MT940 Parse FIN blocks 1-5 tag/value pairs Map field → element repeat groups Validate XSD schema CBPR+ rules Emit MX pacs.008 pacs.009 camt.053 Repair queue manual fix, no STP reverse path: MX → MT for downstream systems still on FIN Representative field mapping :50K: Ordering customer Dbtr / DbtrAcct :59: Beneficiary Cdtr / CdtrAcct :32A: Value date, ccy, amount IntrBkSttlmDt, IntrBkSttlmAmt :70: Remittance info (free text) RmtInf/Ustrd → Strd where parseable Free text carries no structure, so decomposition is the part that actually costs time.
Conversion pipeline, both directions. The mapping table shows the four fields that cause most of the work.
Core banking integration

Banking Integration API Layer

A centralised API layer giving external channels — mobile and agency banking, USSD and the Interswitch network — controlled access to the Finacle Core Banking System. It orchestrates requests, converts JSON ↔ XML, enforces idempotency so a retried request cannot post a duplicate transaction, and validates and transforms messages before they reach the core.

Sits behind an API gateway handling external routing and edge security

Spring Boot Finacle Interswitch REST JSON / XML Idempotency
Banking integration API layer External channels - mobile banking, agency banking, USSD and the Interswitch network - all reach an API gateway that handles TLS, routing and rate limiting. Behind it the API Manager orchestrates requests, converts JSON to XML and back, validates before the core is touched, and checks an idempotency store keyed on channel and reference so a replayed request returns the original response rather than posting a duplicate transaction. Validated requests then go to the Finacle Core Banking System, the only system downstream of the API Manager. External channels Mobile banking Agency banking USSD Interswitch API Gateway TLS termination, routing, rate limiting, edge auth not my layer API Manager • request orchestration and workflow sequencing • JSON ↔ XML transformation • schema + business validation before the core is touched • message enrichment and response normalisation Idempotency store key: channel + ref replayed request returns the original response instead of posting a second transaction validated, idempotent request Finacle Core Banking accounts, postings, balances
Request path from channel to core. The idempotency check is what stops a retried request posting twice.
Payments integration

Mobile Money Integration Middleware

Owned end to end — leading delivery with a team of 6 engineers

Middleware connecting SACCO core banking systems to mobile money platforms, enabling real-time deposits, withdrawals, loan disbursements and repayments. Built around transaction consistency and reconciliation, so a timeout at the provider never leaves the ledger and the wallet disagreeing.

Real-time financial operations across multiple institutions and providers

Spring Boot REST APIs Reconciliation Kafka MySQL
Mobile money integration middleware SACCO core banking sends disbursement and withdrawal instructions to the middleware, which records the intent as pending and calls the mobile money provider. Customer-initiated deposits and loan repayments arrive the other way, as a provider callback. On a result the middleware posts to the ledger or voids the intent. When no callback arrives, a status query is retried with backoff: success posts to the ledger, failure voids the intent, and anything still unknown is held for a reconciliation sweep, so the ledger is never credited on an assumption. customer push: deposit, loan repayment SACCO core member accounts loan ledger GL postings Middleware intent log state machine idempotent callbacks Mobile money M-Pesa Airtel Money third party, not ours 1 disburse / withdraw 2 B2C request 3 result / callback 4 post or void When step 3 never arrives Timeout, no callback intent stays PENDING Status query retried with backoff SUCCESS post to the ledger FAILED void the intent STILL UNKNOWN held for reconciliation The ledger is never credited on an assumption. A wallet debit with no matching posting is the failure this exists to stop.
Both directions, plus the path that matters: what happens when the provider goes quiet mid-transaction.
Infrastructure

High-Availability Banking Infrastructure

Load-balanced, fault-tolerant infrastructure for critical banking services: HAProxy and Keepalived for automatic failover across a multi-node deployment, a MariaDB master–slave cluster fronted by ProxySQL for query routing, and Jenkins pipelines automating build, test and deployment.

Automatic failover and near-zero downtime for critical services

HAProxy Keepalived ProxySQL MariaDB Docker Jenkins Oracle Linux
High-availability banking infrastructure topology Client traffic reaches a virtual IP held by Keepalived, which moves between a master and backup HAProxy node over VRRP if one fails. HAProxy health-checks and routes to three application nodes, which reach the database through ProxySQL. ProxySQL splits reads from writes across a MariaDB primary and a replicating replica that doubles as the failover candidate. Client traffic Virtual IP held by Keepalived, moves on failure HAProxy health checks, L7 routing MASTER HAProxy health checks, L7 routing BACKUP VRRP Application node 1 Application node 2 Application node 3 idle until failover ProxySQL splits reads from writes MariaDB primary writes MariaDB replica reads, failover candidate replication
Failover topology. Every tier has a second path; nothing in the chain is a single point of failure.

Also delivered

Data migration

SACCO Data Migration & Onboarding Platform

An end-to-end migration system for onboarding institutions onto the core banking platform. Automates extraction, transformation, validation and loading of legacy member, account, loan and transaction records, with structured onboarding workflows and documentation and training for implementation teams.

Full data consistency across member, account, loan and transaction records

Python SQL ETL Spring Boot Data validation
  • Financial Reporting PlatformStructured balance sheets, statements and regulatory reports.
  • SFTP Integration PlatformAutomated file exchange and scheduled ingestion pipelines for banking messages.
  • Loan & Transaction Processing ServicesBackend workflows for validation, scheduling and reconciliation.
  • Internal ToolingOperational and automation tools in Spring Boot, Angular, Python and PHP.

Writing on payments engineering

I write about how payment systems actually work. Thirteen posts over the past year have drawn 125,000+ impressions from an audience of 3,700+ banking and payments engineers.

Read all posts on LinkedIn →

Earlier work — 2020 to 2023

Web applications and sites built before moving into banking systems.

  • Online Registration SystemMember registration and records platform. tumcathcom.com
  • Career Guide SystemStudent and lecturer course-guidance application. GitHub
  • Tecsol Company WebsiteProduct and services site with an admin dashboard. tecsol.co.ke
  • Gaming Web ApplicationBrowser-based gaming app with chess and a calculator, player accounts and friend matches. Source on GitHub
  • Programming Tutorial SiteTutorial site for developers learning to program. Source on GitHub

Happy to talk through any of these in more depth.