Case study · Mobile-money collections
Farmer repayments on M-Pesa, checked before the money moves
One Acre Fund lends seed and fertiliser to smallholder farmers and collects repayment in small M-Pesa instalments. I was lead architect on the payment hub that took those repayments direct to M-Pesa, checked each one against the loan ledger first, and ran in One Acre Fund's own cloud from 2022 to 2024.
The problem
One Acre Fund (OAF) lends in kind. Farmers get certified seed and fertiliser at planting time and repay in many small payments before harvest, and in Kenya almost all of that money arrives over M-Pesa. At OAF's scale, small problems per transaction become large sums: a fee that's slightly too high, a payment credited to the wrong account, a repayment that never reaches the farmer's loan record.
- Collections cost too much and went through a middleman. OAF wanted to cut transaction costs by moving repayments from paybill to M-Pesa's Buy Goods service, and to replace a third-party mobile-money aggregator with a direct M-Pesa integration.
- Farmers needed a simpler way to pay. OAF wanted USSD payments on the same shortcode as its core programme, so a farmer with a basic phone could repay without an app.
- Every shilling had to land on the right loan. Each payment had to be validated against OAF's loan systems before Safaricom collected it, and afterwards three records had to agree: the workflow engine, M-Pesa and the loan ledger.
- It had to stay up through the seasons that matter. This was live money in OAF's own cloud through planting and repayment seasons, and OAF's engineers had to be able to upgrade it without downtime.
- Later, money had to flow out too. By late 2023 OAF needed to pay farmers in the field for produce, with name-to-number checks so money couldn't go to the wrong phone, plus payroll and vendor payments tied into SAP and its approval process.
The outcome
- Live in production from February 2022, starting with a KSh 1 test payment and growing to Buy Goods collection, paybill payments with ledger validation, bulk disbursement and SMS confirmations across QA, UAT and production through 2024.
- About 1.2 million repayment transactions a month flowing directly to M-Pesa instead of through the aggregator.
- One deployment chart instead of three. Customer-specific forks were folded into one maintainable Helm chart, which later became the base for the GovStack G2P sandbox.
- OAF could run it themselves. Terraform and Helm playbooks let OAF operate, replicate and scale the hub in its own hosting.
- Releases stopped needing downtime. Immutable release tags, a published release checklist, and a rule that documentation and tests ship in the same sprint as the feature.
- Not finished, and said so. Payouts were scoped with OAF's finance and market-access teams but aren't proven live, and an effort to cut events per payment workflow from about 700 towards 50 changed its measurement approach midway, so there's no final number to report.
What shipped, when
| When | Milestone |
|---|---|
| Feb 2021 | Requirements agreed: Buy Goods collection, USSD access, ledger validation before collection |
| Feb 2022 | First live M-Pesa transaction in production |
| Mar 2022 | Decision to consolidate three deployment charts into one base chart |
| Jul 2022 | Paybill payments with external validation designed for Kenya |
| Oct 2022 | Immutable release tags and a published release checklist |
| May 2023 | Hotfix releases for amount handling in the loan-system connectors |
| Nov 2023 | Payouts discovery: bulk disbursement, account lookup, name-to-number validation |
| Apr 2024 | Event-persistence reliability work on the reporting pipeline |
| Aug 2024 | OAF delivery patterns turned into templates for new programmes |
How it worked
Farmer dials USSD
└─► USSD gateway (SMS confirmations)
└─► Payment Hub
├─► Loan system: validate (is this a real, open loan?)
├─► M-Pesa: collect (farmer confirms on handset)
◄── M-Pesa callback: success or failure
└─► Loan system: post repayment
Connectors for M-Pesa and OAF's two loan systems (Paygops and Roster) plug into the same open-source hub, so validate, collect and post is one orchestrated BPMN flow rather than custom code per system. The payloads carried country, currency and wallet-operator fields from day one, so the design wasn't Kenya-only.
Tradeoffs worth keeping
- A green dashboard is not a working service. Early on, health checks reported services as healthy while they were failing. Health checks should test the dependency that actually breaks.
- Customer forks are debt. A separate OAF chart felt faster and became the thing blocking upgrades. One chart with feature flags cost more up front and paid for itself on the next customer.
- Release tags are promises. When a released tag was re-pushed, clusters kept the old image and the only reliable fix was a reinstall. Never overwriting a tag turned hotfixes back into routine deploys.
- Decide which errors are retryable, and write it down. Retrying more raises success rates but slows responses. That's a business tradeoff, so we argued it out explicitly with OAF instead of letting defaults decide.