useduro.com
The product home: the recovery-first story, pricing, and the 30 Nomba APIs behind it.
Merchant dashboard
Where a business runs on Duro, across test and live: subscriptions, recovery, plans, promo codes, customers, invoices, payouts, VAS, webhooks, and API keys.
Hosted checkout
Duro’s branded subscribe and top-up pages, where customers pay by card, bank transfer, USSD, or a saved method. The card is tokenised on the first charge.
Customer portal
The self-serve portal a customer signs into with an email code: wallet top-ups, saved cards, virtual account, direct debit, and every subscription across every Duro merchant.
Commerce plugins
Installable Nomba payment modules for WooCommerce, PrestaShop, and Joomla, verification-first with 88 automated tests. Full guides in the Integrations tab.
Quill
A Substack-for-Nigeria built entirely on Duro: writers publish, readers subscribe with a Nomba card, and Duro runs the recurring billing, recovery, and payouts. See the Quill tab.
Demo video
The walkthrough above, on its own page if you want to share it.
Documentation
You are here: the full engineering docs, the Nomba API surface, the security model, and the interactive API reference.
MCP server
Operate Duro from Claude, Cursor, Windsurf, or VS Code: 28 tools over a hosted MCP at
mcp.useduro.com. Judge keys inside.Judges start here. Everything above is live on real Nomba rails. Sign in with the ready-made logins below, or create your own account — you are free to sign up. On any verification or OTP screen, enter
123456.
Live API keys for the API playground and direct calls. Paste the secret key as the Bearer token (or swap
sk_live_ for a sk_test_ key for a safe sandbox — same URL, the prefix picks the mode):
mcp.useduro.com — point Claude, Cursor, Windsurf, or VS Code at it and authenticate with the key (the prefix picks the mode, same as the API keys):
How this is judged
Full breakdown with evidence: Judging Criteria.
Every subscription business in the world quietly bleeds. Not from churn you can see — the customer who clicks “cancel” — but from the failure you never notice: the card that declines on renewal day because the customer got paid on the 28th and you tried to charge on the 1st. The bank that was down for an hour. The card that expired. Globally, involuntary churn is 20–40% of all churn. In Nigeria, where most cards are debit cards tied to a salary that lands once a month, it is worse. Most billing platforms will tell you what you earned. Duro is built around a different number: what you almost lost, and got back.
30 Nomba APIs, integrated. Duro drives 30 distinct Nomba API endpoints end to end: hosted checkout, tokenised cards, charge, bank transfers, direct-debit mandates, virtual accounts, refunds, VAS, and transaction verification. See the full Nomba API surface, one row per endpoint.
Recovery-first
A failed charge isn’t an error log line. It’s a state machine with a payday-aware retry schedule, automatic rail-switching, a dunning email sequence, and a live recovery ledger.
Identity on the phone
A customer saves a card once — verified over WhatsApp — and it works at every Duro merchant. The wallet lives with the person, not the store.
Built for Nigerian rails
Card, bank transfer, USSD, virtual accounts, direct-debit mandates. When the card fails, Duro doesn’t give up — it relays down the rails your customer actually has.
Multi-tenant from line one
Two physically separate Postgres schemas for
test and live, a third for tenant identity. No “mode” column you can forget in a WHERE clause. Isolation by construction.The two ideas everything hangs on
If you read nothing else, read this. The whole codebase is two convictions made concrete.1. Recovery is a first-class subsystem, not a cron job
When a renewal charge fails, most platforms drop a row in afailed_payments table and email you a weekly digest. Duro runs the failure through a decision engine (DunningStrategy) that classifies why it failed, then chooses an action:
- Insufficient funds → retry on the customer’s wallet rail with a doubling exponential backoff (
2^(n-1)hours, capped at 168h/7d), so the next attempt can draw from a topped-up balance instead of hammering the same empty card. - Hard decline (stolen, lost, fraud) → stop hammering the card. Switch rails: USSD, then transfer, then virtual account, then direct debit.
- Expired / unsupported card → don’t retry at all. Pause and ask the customer to update their card.
- Processor error / timeout → transient. Retry on the configured offset curve, default
[0, 1, 3, 6, 12, 24, 48, 96, 168]hours.
StoreSettings row (max attempts, offset curve, enabled rails), and every recovered naira is counted on a live dashboard. Duro also ships a Nigeria-native PaydayWindow primitive (anchor day 28, early-month grace on the 1st-3rd); it is a standalone helper, and the shipped DunningStrategy.decide() uses the wallet + backoff path above for insufficient funds. Read the recovery chapter →
2. The payment method belongs to the customer, across merchants
A customer pays Merchant A through Duro’s inline popup, ticks “save to my phone,” and confirms a one-time code over WhatsApp. Duro mints a universal identity keyed to that phone number. When the same human checks out at Merchant B — a completely different business — Duro recognises the phone, and their saved card is one tap away. This is the network effect a single-merchant tokenizer can’t have: a conventional saved card belongs to the merchant. Duro’s belongs to the person. One customer portal shows all their subscriptions across all Duro businesses. Read the identity chapter →The system at a glance
Three Node services, a fleet of shared@duro/* packages, two data schemas per mode, and a worker that turns the crank on time. Everything below this page drills into one box.
How to read these docs
1
Get the thesis
The Thesis is the five-minute version for someone deciding whether Duro is interesting. Recovery-first, identity-on-phone, why Nigeria changes the math.
2
Walk the architecture
Architecture → Overview is the whole system at altitude, then the data model and request lifecycle.
3
Go deep on the engine
The Billing Engine and Recovery chapters are where the real algorithms live. This is the part worth your attention.
4
Build against it
The API Reference has an interactive playground for every public endpoint. Authenticate with a test key and fire real requests from the page.
The payment rail is real and runs live. Charging goes through a
ChargeGateway abstraction (@duro/payments); the worker wires the NombaChargeGateway (@duro/nomba-client), which drives real Nomba tokenised-card charges, direct-debit mandate debits, refunds, bank-transfer payouts, virtual accounts, and VAS. Duro runs two environments that the merchant selects: test targets Nomba’s sandbox host (sandbox.nomba.com) and live targets production (api.nomba.com), chosen by the API-key prefix (sk_test_ / sk_live_) or the x-duro-mode header. Both drive the real Nomba APIs. A SimulatedChargeGateway exists only for the internal integration test suite. Some flows are deliberately test-mode-safe (refunds and payouts short-circuit to a deterministic result in test), and features that require a real banking product (virtual accounts, direct debit) are live-mode only.