/WRITING · NIGERIA · PAYMENTS
Flutterwave's 2026: open banking, stablecoins and what it means for developers
Flutterwave bought open-banking start-up Mono in January, raised a Ripple-backed Series E at about $3.25 billion in June, and is building digital-asset rails. Here is what changed for Africa's largest payments company, and the engineering patterns I use to build reliable payments on top of providers like it.
Most Nigerian developers have integrated Flutterwave at some point. It is the default way many products in the country, and across Africa, accept cards, bank transfers and mobile money. In 2026 the company made three moves that change what that integration can do: it bought open-banking provider Mono, raised a Ripple-backed Series E, and extended into digital-asset payments.
This post covers what happened and, more importantly for developers, how to build payment flows that stay correct no matter which provider sits underneath.
The problem: payments in Nigeria are asynchronous by nature
Card payments can fail on 3-D Secure, bank transfers can confirm minutes or hours later, and users close the tab before the redirect returns. Many integrations still treat the browser redirect as proof of payment, which produces both false failures (the user paid but the app shows unpaid) and, worse, false successes.
Meanwhile the rails themselves are changing. Open-banking APIs let users link accounts and pay directly from them; stablecoin settlement promises faster cross-border transfers. More rails mean more asynchronous events to handle correctly.
The insight: the rails are multiplying, the fundamentals are not
On 5 January 2026 Flutterwave announced it was acquiring Mono, Nigeria's best-known open-banking infrastructure provider, in a deal Afridigest called Africa's first YC-to-YC exit. Reporting described it as an all-stock transaction valued at up to about $40 million. Mono's APIs let businesses link customers' bank accounts, verify identity and income data, and initiate payments directly from accounts.
In June, TechCabal reported a Series E valuing Flutterwave at about $3.25 billion, with Ripple taking an equity stake; the amount was not disclosed. The Cryptonomist reported that Ripple will supply infrastructure for Flutterwave's digital-asset offerings, following a 2025 stablecoin partnership with Polygon Labs. Flutterwave says it operates cross-border payments across 35 African countries and has raised more than $500 million in total.
Read together, these moves add new ways to collect and settle money: account-to-account payments through open banking and digital-asset rails for cross-border flows. Each new rail is another asynchronous system sending events to your server. The products that benefit are the ones whose payment core is already event-driven, idempotent and reconciled.
Implementation walkthrough
The patterns below are provider-agnostic. Use your provider's current documentation for exact field names and signature schemes; the structure is what matters.
Step 1: create the intent in your database first
TAIWO EMMANUEL / CODE NOTEimport { randomUUID } from "node:crypto";
export type PaymentStatus = "pending" | "succeeded" | "failed" | "refunded";
export async function createIntent(db: Db, input: { orderId: string; amountKobo: number; currency: "NGN" | "USD" }) {
// Money as integer minor units: never floats.
const reference = `ord_${input.orderId}_${randomUUID()}`; // also your idempotency key with the provider
await db.payments.insert({ reference, orderId: input.orderId, amountKobo: input.amountKobo, currency: input.currency, status: "pending" });
return reference; // pass to the provider's checkout or charge API
}Step 2: verify the webhook and apply it exactly once
Providers retry webhooks, so the same event will arrive more than once. Verify authenticity using the scheme in your provider's docs, re-fetch the transaction from the provider's API rather than trusting the payload blindly, and make the state change idempotent.
TAIWO EMMANUEL / CODE NOTEimport { timingSafeEqual } from "node:crypto";
const safeEqual = (a: string, b: string) => a.length === b.length && timingSafeEqual(Buffer.from(a), Buffer.from(b));
export async function handleWebhook(req: Request, db: Db, provider: ProviderClient) {
// 1. Authenticity: compare the signature header using your provider's documented scheme.
if (!safeEqual(req.headers.get(provider.signatureHeader) ?? "", provider.expectedSignature(await req.clone().text()))) {
return new Response("invalid signature", { status: 401 });
}
const event = await req.json();
// 2. Never trust the payload alone: confirm with the provider's API.
const tx = await provider.verifyTransaction(event.data.reference);
// 3. Apply once: the WHERE clause makes retries harmless.
const expected = await db.payments.findByReference(tx.reference);
const matches = expected && tx.amountKobo === expected.amountKobo && tx.currency === expected.currency;
await db.payments.updateWhere(
{ reference: tx.reference, status: "pending" },
{ status: tx.successful && matches ? "succeeded" : "failed", providerId: tx.id },
);
return new Response("ok"); // acknowledge quickly; do heavy work in a queue
}Note the amount and currency check. A common fraud pattern is a successful payment for a smaller amount against the reference of a larger order; confirming the amount against your own record closes that gap.
Step 3: reconcile every day
Webhooks can be missed, endpoints can be down during a deploy, and settlements can differ from charges. A daily job compares your records with the provider's settlement report and flags differences for a human.
TAIWO EMMANUEL / CODE NOTEexport async function reconcile(db: Db, provider: ProviderClient, day: string) {
const [ours, theirs] = await Promise.all([db.payments.forDay(day), provider.settlements(day)]);
const byRef = new Map(theirs.map((s) => [s.reference, s]));
const issues = [];
for (const p of ours) {
const s = byRef.get(p.reference);
if (p.status === "succeeded" && !s) issues.push({ reference: p.reference, problem: "succeeded locally, not settled" });
if (p.status === "pending" && s?.successful) issues.push({ reference: p.reference, problem: "settled, still pending locally" });
if (s && s.amountKobo !== p.amountKobo) issues.push({ reference: p.reference, problem: "amount mismatch" });
}
return issues; // alert finance/ops; never auto-correct money silently
}What open banking and stablecoins add
Open banking changes the collection step: instead of typing card details or copying an account number for a transfer, users link their bank once and authorise payments directly. That reduces failure rates, but it adds consent states and token expiry to manage, which belong in the same state machine as everything else.
Digital-asset rails mainly change settlement for cross-border flows. For product developers the lesson is the same as above: another asynchronous rail, another set of events, the same need for idempotency and reconciliation. Teams with solid payment foundations will be able to adopt new rails quickly; teams that trust redirects will not.
Takeaway
- Flutterwave acquired Mono in January 2026, raised a Ripple-backed Series E at about $3.25 billion in June, and is building digital-asset rails across its 35-country network.
- Payments in Nigeria are asynchronous: treat the redirect as a hint and the verified webhook as the truth.
- Store intent first, verify and apply webhooks exactly once with amount checks, and reconcile daily.
- New rails such as open banking and stablecoins reward teams that already get these basics right.
References
- 1. Flutterwave hits $3.25 billion valuation in Ripple-backed Series E — TechCabal, 16 June 2026
- 2. Flutterwave funding: valuation and Ripple — The Cryptonomist, 16 June 2026
- 3. Deal of the Week: Flutterwave acquires Mono in Africa's first YC-to-YC exit — Afridigest, 5 January 2026
- 4. Pan-African fintech Flutterwave buys Nigerian open-banking startup Mono in deal valued up to $40mn — bne IntelliNews, January 2026
- 5. Idempotent requests — Stripe API docs (general pattern)
- 6. crypto.timingSafeEqual() — Node.js docs