Developer documentation

Build against Vuka Bridge without touching real money.

Vuka Bridge ships with simulators for every party in a credit checkout: a merchant storefront, three financiers and both settlement rails (M-Pesa and PESI). They run on the same deployment as the real bridge code, so you can take a shopper from cart to a paid merchant — webhooks, OTPs, payouts and all — without a Daraja or PESI account, a real shortcode or a credit provider agreement.

Merchant

Merchant simulator

"Soko Demo Store" at /sample/mer — categories, product pages, cart, credit checkout (redirect or embedded), webhook receiver and settlement ledger.

Financiers

Financier simulators

Shop na Pesi (cart credit, /sample/fin), Ngao Credit (asset finance, /sample/ngao) and Faraja Credit (lender-settled, /sample/faraja) — each serving the financier API the bridge calls.

Settlement rail

M-Pesa simulator

Daraja-shaped B2B payment, Account Balance, C2B confirmation and account validation at /v1/_sim/mpesa, with a paybill console at /sample/mpesa.

Settlement rail

PESI simulator

PESI B2B paybill and till disbursement, account-name lookup and transfer status at /v1/_sim/pesi — signed callbacks and failure injection.

How the simulators fit together

Each simulator stands alone — run just the merchant sim for a shopper journey, or just a rail sim for a payout test. End to end, a checkout touches all three layers:

┌─────────────────────────┐       ┌─────────────────────────────────┐
│ /sample/mer             │       │ Financier simulators            │
│ Soko Demo Store         │       │ /sample/fin     Shop na Pesi    │
└────────────┬────────────┘       │ /sample/ngao    Ngao Credit     │
             │ orders API         │ /sample/faraja  Faraja Credit   │
             │ (/v1/checkout)     └────────────────┬────────────────┘
             ▼                                     │ financier API
┌────────────┴─────────────────────────────────────┴────────────────┐
│ Vuka Bridge (real code)                                           │
│ hosted checkout · /admin · /v1/*                                  │
└──────────────┬───────────────────────────────────┬────────────────┘
               │ B2B payout (M-Pesa or PESI)       │
               ▼                                   ▼
┌─────────────────────────┐       ┌─────────────────────────────────┐
│ /v1/_sim/mpesa/*        │       │ /v1/_sim/pesi/*                 │
│ M-Pesa (Daraja) sim     │       │ PESI sim                        │
└─────────────────────────┘       └─────────────────────────────────┘

The merchant creates an order through the orders API and sends the shopper to the hosted checkout. The bridge calls the shopper's financier to link the account, take the OTP and book the loan, debits that financier's virtual account, and pays the merchant over M-Pesa or PESI. The rail's callback marks the payout delivered and the merchant receives order.paid. A lender-settled financier (Faraja Credit) pays the merchant itself, so no rail is involved.

Quick start

  1. Copy .env.example to .env and fill in the database connection — every simulator keeps its state in the database.
  2. Run migrations: npm run migrate. They create the simulator tables, their demo data and the float paybill. Wiring a simulator financier into the bridge is covered in the financier guide.
  3. On a fresh database, create the first platform admin: npm run bootstrap:admin.
  4. Start the dev server: npm run dev.
  5. Open one of the simulator pages above and follow its Quick start. For a full checkout you need the merchant simulator, a financier simulator and one rail.

Conventions used across the simulators

ConventionDetails
URL-parameter sessions (?sid=<uuid>)The simulator storefronts and lender portals don't use the bridge's login. Their sessions are minted server-side and carried on every URL, so there is no cookie surface and a session is easy to share between tabs or tests.
scrypt password hashingBuilt-in Node crypto, stored as scrypt$<salt-hex>$<hash-hex>.
Signed callsEach wire has its own signature:
  • Bridge → financier: the method configured for that financier — by default HMAC-SHA256 of the raw body in the EDOMX-Signature header; a bearer token or an API key are the alternatives. A wrong or missing signature gets 401 signature_mismatch.
  • Bridge → merchant webhooks: Vuka-Signature: t=<ts>,v1=<hmac>.
  • Bridge ⇄ PESI: x-pesi-signature, HMAC-SHA256 of the raw body, both ways.
  • M-Pesa callbacks: unsigned, as Daraja's are.
Fixed test OTPs222222 — the financier OTP on the lender simulators (linking, booking, express checkout).
333333 — the delivery-confirmation OTP for pay-on-delivery orders.
111111 — the bridge's own sign-in OTP when SMS is simulated.
Lender-sim OTP challenges are listed in each lender console under Links & OTP.
Database-backed stateCustomers, orders, credit lines, loans, paybill balances and request ledgers live in the database and survive restarts. Timers for pending rail callbacks do not: the M-Pesa sim loses them, while the PESI sim re-fires stalled callbacks.
Request logsEach simulator records what it received: the Soko webhook log, the lender consoles' Wire Inspector, the M-Pesa console's Requests tab and the PESI request ledger. On the bridge side, /fin/logs shows calls to a financier and /admin/transactions traces an order end to end.

Where to ask questions

Each guide ends with a Troubleshooting section covering the failure modes we have actually hit. If your issue isn't covered there, the specifications in documentation/requirements/ are the reference for behaviour.