Skip to content
Selected work

ExtraCash · Zedcrest Group · Zedvance

Lending, inside a bank people already trusted

An embedded credit product that decides in-app whether you qualify, then moves the money the moment you do. The interesting part was never the loan calculator. It was every way the flow could stop halfway.

Role
Mobile Engineer
Organisation
Zedcrest Group · Zedvance
Period
2024 - present
Surface
Flutter
01

The problem

Zedvance was already a working retail banking app. Adding credit meant adding three things it didn’t have: an underwriting pipeline, an identity trail strong enough to lend against, and an irreversible money-movement path, all inside a product people already used for their salary.

It couldn’t feel bolted on. And it couldn’t be casual about failure, because a lending flow that breaks in the middle doesn’t just annoy someone; it leaves them thinking they owe money they don’t, or that they don’t owe money they do.

02

Constraints

  • Eligibility depends on data the app doesn’t own: bank statements through Mono’s open-banking APIs, identity through KYC providers, employment and income through user capture.
  • Every one of those is a slow, failable third-party call, made over Nigerian mobile networks, on devices that background aggressively.
  • Money movement is irreversible. Disbursement and repayment have to be safe under retry.
  • A user’s limit is not a stored constant. Income changes, so eligibility has to be re-assessed rather than remembered.
03

Decisions

01

Model eligibility as a resumable state machine, not a form

Statement linkage, KYC tier, employment capture and decisioning are each their own persisted state. A user who abandons at the bank-statement consent screen comes back to exactly that step. A user whose KYC lapses drops a tier rather than being ejected from the flow.

What I rejected

A multi-step form holding progress in local state. It reads simpler right up until a third-party callback fails on step four of six and the user is asked to start again, which, in flows like this, is the single largest source of drop-off.

02

Gate on capability, not on screen order

Each screen asks the domain a question, “can this user disburse yet?”, instead of assuming the screen before it ran. The answer is derived from persisted state, not from navigation history.

What I rejected

Relying on route order for correctness. It holds until deep links, push notifications and app-resume start dropping users into the middle of the flow. This product uses all three.

03

Treat disbursement and repayment as two different reliability problems

Disbursement is ours to retry against our own wallet ledger, keyed idempotently. Repayment goes out through a card rail where the authoritative answer lives on someone else’s server, so the client reconciles against that answer and never asserts its own.

What I rejected

Optimistic local balance updates on repayment. Fast, and wrong at exactly the moment it matters. A user who sees a repayment that never settled will call support, and they’ll be right to.

04

Make re-assessment a first-class event

Income change triggers a re-evaluation rather than a silent overwrite, so a limit that moves has a reason attached to it that support can read back to a customer.

What I rejected

Recomputing the limit lazily on next open. Cheaper, but it produces a number nobody can explain, the worst possible property for a credit decision.

04

Where it landed

  • ExtraCash ships inside Zedvance: eligibility from open-banking statement analysis, automated decisioning, instant disbursement to wallet, and card repayment via Rave/Flutterwave.
  • The same architecture carried into the surrounding surfaces: new service modules in the core banking app, a POS application, and a separate business-banking app for SMEs.
  • Flutter
  • Dart
  • Mono (open banking)
  • Rave / Flutterwave
  • KYC
  • Idempotent money flows

Working on something like this?

I’m open to senior mobile and full-stack roles, and I’m relocation-ready.

donaldamadi15@gmail.com