Capability 09 · Payment & Transaction Systems

Payment & Transaction Systems

Real money, real movement, real reconciliation. The same discipline that keeps KodiiPay's books honest.

Before you read: Written from live engineering practice — the money-moving, million-user work our team runs on our own products, set down so anyone building something can learn from it.

01 · What it is

Money software is different from software. A bug in a content site costs a comment; a bug in payments costs cash and trust. We build payment systems with the discipline our own product demands: real M-Pesa integration through Daraja (STK Push, C2B, B2C, B2B), callbacks verified against the gateway so nothing is credited on faith, idempotency so retries never double-pay, ledgers that record every movement, and reconciliation that reconciles — against the gateway's statement, not just against our own records. If you move money in Kenya, this is where we earn our keep.

What a payment & transaction systems build covers:

  • M-Pesa via Daraja — STK Push, C2B, B2C & B2B flows
  • Card gateways & bank transfer rails where they fit
  • Wallets, ledger entries & double-entry money flow
  • Callbacks, retries & terminal transaction states verified
  • Idempotency & double-payment prevention at every layer
  • Reconciliation, fees & rounding handled exactly, never approximately
  • Fraud checks, limits & freeze logic that protect both sides
  • Settlement & payout automation with audit trails
What we do · How we do it — as TGJOF Enterprise

This is how we do Payment & Transaction Systems

Payment and transaction systems are the one category of software where 'the network probably retried' is not a footnote but the core design constraint. At TGJOF Enterprise, we build money software around a single conviction: the customer's balance is a promise, and a promise has to survive callbacks that arrive twice, networks that drop mid-charge and gateways that answer slowly. That is why the studio builds a rail-agnostic payments layer first — M-Pesa and Daraja as the lived home-turf example, with PayPal, Stripe, PayStack, cards, bank rails and any gateway a client already runs all treated as first-class adapters behind one ledger. The craft is in the machinery that keeps every one of those promises exact, auditable and safe at any volume.

What we do

  • Multi-rail integration — M-Pesa and Daraja may be the home-market lived experience, but the payments layer treats PayPal, Stripe, PayStack, cards (Visa and Mastercard), bank transfers and any gateway a client already uses as first-class adapters behind one shared ledger, lifecycle and verification model.
  • Ledgers and wallets — double-entry books with available, locked and pending genuinely separated, immutable journals written only by verified server procedures, and float kept distinct from revenue so no payout can ever touch the wrong pot of money.
  • Idempotency and verification discipline — every transaction carries a reference born at initiation, replays are answered rather than re-executed, and callbacks are treated as claims to be verified against the gateway's own status API before anything is credited exactly once.
  • Reconciliation — daily line-by-line matching against each gateway's statement, day totals that must tie, drill-down to the single discrepant line, and a scheduled job that surfaces the mismatch before a human ever has to.
  • Security and compliance — rate limiting treated as a financial control, KYC gates on the paths that need them, escrow and holds, freeze semantics, and regulator-readable audit trails carrying actor, timestamp, reference and outcome on every movement.

How we do it

  • Callback-claim-then-verify — the gateway's word is a claim, not a proof: the backend queries the gateway's own status API, credits only a genuinely verified success, returns a retry signal on transport errors, and politely acknowledges definitive non-payment without ever crediting.
  • Exact-once semantics — terminal states and idempotency keys across every money action mean a double callback, a dead-letter replay or a user double-tap changes the ledger exactly once, answering with the same settled truth every time.
  • Escrow and holds — funds move available to locked at initiation, the lock is consumed on confirmed success and released on confirmed failure, and unverified recipients, expired entitlements and pending KYC keep money in an inspectable hold instead of silently sending it.
  • Fee ranges, not constants — fees are computed by one canonical function from a configured thresholds-and-rates table, so the app, the web, the API and the reports never disagree about the price of a transaction, and the customer sees amount, fee and total before confirming.
  • Monitoring the money paths — latency and error budgets on every payment journey, payment-gateway logs that answer 'where is my money' in minutes, cron sweeps that expire stale holds, and reconciliation run as a standing operation rather than a month-end surprise.

02 · The full discipline

Money software is not software with money in it. Money software is a promise that must survive the internet.

A bug in a content site costs a comment. A bug in payments costs cash and trust — and by the time you discover it, it has usually already moved real money. Payment and transaction systems are the closest thing software has to civil engineering: the moment the numbers are wrong, people on both sides of every transaction pay for it.

We build payment systems the way a live platform has to — because KodiiPay is a payment platform we operate ourselves, in production, moving real money through M-Pesa on Safaricom's Daraja rails. This is not theory we read about; it is discipline we run every hour of every day: wallets, wallets-with-holds, STK pushes, paybills, tills, B2B runs, B2C withdrawals, subscriptions, fees, floats, ledgers, reconciliation and fraud — held to standards that a regulator would recognise and a customer can feel.

Below is how money software is really built when your own money depends on it. Every layer — the ledger, the lifecycle, the callback discipline, the arithmetic, the fraud posture, the reconciliation, the scale — is what we actually run, and we bring the same machinery to every transaction system we build for clients.

No payment build receives anything less than this. If you store balances, move money, take payments, disburse or charge, the architecture below is the floor, not the ceiling.

03

Money software is different — everything is

Most software failures are recoverable. A form that rejected a valid input can be fixed; a report that was wrong can be regenerated; a session that dropped can be started again. Money failures are not like that. A double credit, a lost payment, a ledger that disagrees with the statement, a fraudster who walked through an open door — each of these is a real, measured, sometimes un-recoverable loss. The entire discipline of payment engineering exists because of that asymmetry.

  • The numbers must always balance — at any instant, a customer's balance plus the platform's float plus the gateway's outstanding settlement must agree with every transaction ever recorded. If they disagree, that is an incident, not a rounding detail.
  • Every promise is eventually exact — pending means 'not final yet', confirmed means 'money moved', reversed means 'money came back'; there is no in-between state where the customer's money is 'sort of' anywhere.
  • Failures are part of the design — the network drops, callbacks arrive twice, a user kills the app mid-payment, a phone battery dies on the pin pad. Money software is engineered so none of those can corrupt the ledger.
  • Fraud is assumed, not discovered — the system behaves as if an adversary is already inside, and checks every high-value or identity-sensitive action structurally, not with a 'please be honest' checkbox.
  • Regulators can audit without being told — every movement carries a reference, a timestamp, an actor and an outcome, so the truth is provable in minutes, not reconstructions over weeks.
  • Running it teaches you — the difference between a payment system that has carried real money and one that hasn't is that the one that has knows exactly where its seams are.

We have had years of that education on our own platform. Every 'we should have known' that a live system hands you, we have been handed — and every one of them is now a rule, a check, a constraint or a test below.

04

The payment rails: what money actually moves on

Kenya's money moves on M-Pesa, and M-Pesa moves through Safaricom's Daraja API — it is where this studio earned its production scars, and it is one rail among many. We integrate the rails the market actually uses: M-Pesa/Daraja, PayPal, Stripe, PayStack, cards, bank transfers and the gateways a client already runs, all as adapters behind the same ledger — never shoehorning an international mental model over a Kenyan reality, and never settling for 'it works in a sandbox'. These are the rails our own platform runs on:

  • STK Push — the gold standard of Kenyan payments: the customer pays with a PIN from their own phone, and nobody ever handles their card or M-Pesa PIN. We fire the charge request, the push lands, the customer confirms, and the money is captured by Safaricom.
  • PayBill — a business number with an account reference, the backbone of deposits, bill payments and rent settlements. A customer pays a business number and the money finds its way to the right account.
  • Lipa na M-Pesa (Till / Buy Goods & Services) — the merchant till, where businesses accept customer payments on a till number or into Pochi la Biashara — the daily reality of small and large merchants alike.
  • C2B — customer-to-business confirmation, the callback that tells us money genuinely arrived at a PayBill or Till.
  • B2B — business-to-business transfers: rent to a landlord, a bill to a provider, a payout to a merchant — money that passes directly between businesses on the Daraja rails.
  • B2C — business-to-customer disbursement: withdrawals, earnings payouts, send-money — money moving out to a phone.
  • Card rails — Visa and Mastercard for e-commerce, international customers and the customers who don't use M-Pesa at all.
  • Bank rails — domestic transfers and funding for users who route through a bank account.

The rail matters less than the discipline around it. We have wired all of these into the same architecture — one ledger, one lifecycle, one callback discipline — so a customer pays the same way the platform guarantees money, whichever rail carried it. The M-Pesa/Daraja path holds the deepest scars; the PayPal, Stripe, PayStack, card and bank paths clear the same verification, idempotency and reconciliation rules before a single shilling moves on them.

05

The rails are many — integration is the point

A payments platform we build is not a Daraja integration with the occasional bolt-on. It is a rail-agnostic payments layer: one ledger, one lifecycle, one set of money rules, and every payment provider as an adapter behind it. M-Pesa and Daraja are the rails where the studio has lived production scars — the callbacks that arrive twice, the sandboxes that lie, the statements that disagree — and every other rail earns the same standard before it carries a shilling:

  • Daraja (M-Pesa) — STK Push, C2B, B2B, B2C, PayBill and Till; the home-market rail where the discipline below was forged on real money.
  • PayPal — global checkout, stored card payments and marketplace settlements through one of the most widely deployed processors on earth.
  • Stripe — cards, digital wallets, subscriptions and connect-style payouts, with the provider handling the card-brand world.
  • PayStack — card and alternative payments for West African and international markets through a single API.
  • Card rails (Visa / Mastercard) — e-commerce and international customers through whichever processor fits the market.
  • Bank rails — domestic transfers, account funding and payout settlement for users who route through a bank.
  • Client-specific gateways — an internal bank, a telco wallet or an industry switch the client already runs, wrapped in the same adapter contract as everything else.

The value of a multi-rail layer is that the customer pays the way they already pay, and the platform guarantees money the same way on every rail. A verified settlement is a verified settlement whether it arrived through a till, a checkout button or a bank ledger — and it is held to the same claim-then-verify, exactly-once and reconciliation discipline either way.

06

Wallets: the balances are the trust

A wallet is more than a number on a screen. It is a double-entry ledger with a current state, a history, a float that backs it and a rulebook about who may touch it and how. We build wallets that behave — under concurrency, under dispute, under fraud, at scale:

  • Available, locked and pending are different numbers — a user sees what they can spend today; the system also knows what is held (escrowed), and what is still resolving, and never lets the three be confused on screen or in logic.
  • Escrow semantics — initiation moves funds available→locked; a confirmed success consumes the lock; a confirmed failure releases it. A balance is never simultaneously 'spendable' and 'pending'.
  • Three layers of balance protection — a pre-flight check inside the row lock, an atomic decrement, and a database CHECK constraint as the final wall. No single bug — not even a bypassed check — can carry a balance negative.
  • The ledger is immutable and procedural — balances and journals change only through verified server procedures; a direct insert, update or delete from outside is refused by the database itself.
  • Every movement carries a reference and an audit row — born in the same transaction as the balance change, so the trail cannot be detached from the money.
  • Freezes, holds and gates — identity, KYC, subscription and wallet-freeze states can stop money leaving, each with an explicit, reversible rule and the customer told exactly why.

A wallet we build must survive the moment a customer looks at it and sees the wrong number at the wrong time — survived means a proof was possible in minutes. That is the standard wallets are held to here.

07

Transaction lifecycle: from pending to settled, exactly once

Every payment we accept or disburse passes through a lifecycle with explicit states and explicit transitions. Nothing jumps states; nothing settles twice; everything can be asked 'what state is this in?' and get one true answer:

  • Initiated — the transaction is created with a unique reference before any money is touched. This is the moment idempotency begins: retries carry this reference.
  • Pending — the gateway has been asked; money has not moved yet. For STK Push this is when the push has landed on the customer's phone and is awaiting their PIN.
  • Confirmed — the gateway has genuinely verified success; the wallet credit (or debit) and all ledger rows are written atomically. Terminal state: nothing re-runs it.
  • Failed / Reversed — a definitive failure or a genuine reversal moves the transaction to terminal state, the escrow is released, and the customer gets exactly what they should back, with a notification.
  • Timed-out / Expired — a transaction left mid-flight by a dropped network or an abandoned pin pad is expired by a scheduled job that releases any hold and marks it final, so no money lingers in limbo.
  • Replay protection at every transition — a callback or retry arriving at any state is matched against the terminal state and answered with the settled truth, never re-executed.

The discipline is simple to state and brutal to hold: credit each payment exactly once, no matter how many times the gateway, the network or the user says 'again'. The implementation below is how 'exactly once' is actually achieved.

08

Callback discipline: the gateway's word is a claim, not a proof

On most rails — a Daraja/C2B callback, a PayPal or Stripe webhook, a PayStack notification, a card-processor response or a bank message — the gateway calls your server with 'the customer paid'. On mobile money and many rails, that callback arrives over the ordinary (forgable, replayable) internet. The single most dangerous assumption in payment engineering is that the callback is true. We treat every callback as a claim to verify:

  • A callback is a claim, not a proof — before anything is credited, the backend queries the gateway's own status/query API (for M-Pesa, the STK Query / status endpoint) and only a genuinely verified ResultCode 0 settles the transaction.
  • Verified paid → credit — the credit and its ledger rows are written in the same atomic step as the confirmation, so the audience — user, statement, float — updates together.
  • Transport or query error → retry signal — the backend returns the response that tells the gateway to re-send, the money stays pending, and nothing is half-credited.
  • Definitive 'not paid' → acknowledge, never credit — the claim gets a courtesy acknowledgement, the money stays untouched, and the mismatch is written to an audited payment-gateway log for reconciliation.
  • Idempotency keys and terminal states absorb replays — a callback arriving twice, or a retry from a dead-letter queue, always returns the settled answer and never double-counts.
  • Both directions are correlated — outbound money (B2C/B2B) carries a reference the gateway echoes back, so a callback can be matched to its send by that reference, never by guesswork.
  • Every verified interaction is logged — the raw callback and the verification result land in a payment-gateway log that support and reconciliation can query, so 'where is my money' has an inspectable answer.

On our own platform this exact loop is what separates the sandbox from production: the sandbox accepts the callback; production asks the gateway to prove it. We bring that proof requirement to every build.

09

Concurrency: two customers racing must both get the right answer

At 3pm in a housing estate, a hundred tenants pay rent at once; behind them, a marketing batch triggers and a cron job sweeps stale transactions. If any two of those overlap on the same wallet, the result must still be exactly correct. This is the hardest discipline in the list, and it is non-negotiable for money:

  • Row-level locks (FOR UPDATE) — the wallet row is locked before the decision is made, so two payments physically cannot both decrement the same available balance.
  • Atomic check-and-write — the balance check, the lock and the decrement are one transaction; the system can never observe a state that was never true.
  • Advisory locks plus SKIP LOCKED on money crons — scheduled jobs claim a lock and skip rows another run already holds, so overlapping runs cannot double-deduct, double-notify or double-send.
  • Terminal-state matching — a transaction that is already confirmed or reversed is matched and answered, never re-executed, no matter how many threads reach it.
  • Optimistic checks where locks are heavy — versioned rows and conditional updates refuse stale writes with a clean retry for operations that do not need a hard lock.
  • Dead-letter honesty — work that fails repeatedly goes to a visible queue with its payload intact, so nothing is silently lost and nothing is silently replayed.

Concurrency bugs are the most expensive bugs in money software because they only reproduce in production, under load, when real money is on the wire. We engineer them out of the architecture, and prove it with tests that simulate the exact overlaps production will throw at us.

10

Idempotency: the retry is a friend, the duplicate is the enemy

Networks retry. Gateways redeliver. Humans double-tap. None of that should ever double-charge a customer, but without idempotency, all of it does. Idempotency is not a nice-to-have wrapper on a payment call — it is a property of the transaction itself:

  • A unique reference born at initiate — the reference is the identity of the intent; the same reference replayed is answered with the same outcome, never a second execution.
  • Idempotent confirmation — replaying a confirmation callback, or delivering it twice, credits exactly once; the second arrival returns the settled state.
  • Idempotent disbursement — a B2C or B2B send is dispatched once, correlated by its reference, and a reversal can only ever reverse one matched send.
  • Idempotent refunds — a refund carries the source transaction and is applied once; a retried refund request returns 'already refunded' with the original outcome.
  • Idempotent bills and notifications — an auto-renewal or a dunning cycle creates one pending bill per period and one notification per cycle; retries cannot multiply them.
  • Idempotency is tested, not assumed — the test suite re-delivers every callback and re-sends every request and asserts the ledger changed exactly once.

Your payment gateway may be down, your phone may drop the network at the pin pad, and your customer may tap pay three times while nothing seems to happen. All of that is survivable if the transaction remembers itself. Ours do.

11

Fees, rounding and arithmetic done exactly

Fees are where payment platforms quietly leak or customers quietly get robbed — not by malice, but by floating-point arithmetic, static numbers and 'close enough' rounding. Every fee in our systems comes from a configured, maintained table and is computed exactly:

  • Fees from a configurable ranges table — a fee schedule (thresholds and rates) read from a maintained table, never a hard-coded constant in code — admin-visible and editable.
  • One canonical fee function — every surface (app, web, API, reports) calls the same fee function for the same amount, so the app and the backend never disagree about the price of a transaction.
  • Exact arithmetic — amounts are integers (or exact decimals), never floating-point; rounding is defined once, applied identically everywhere, and stated plainly to the customer before they confirm.
  • Total = principal + fee, shown before confirm — the customer always sees the amount, the fee and the total before the money moves; the backend then validates the total it was given.
  • Shortfall telling, not guessing — an insufficient balance returns the exact amount required (principal + fee − available), so the user tops up exactly once and succeeds, never in a loop.
  • Fee accounting is separate — platform fees are booked as revenue, tracked apart from customer float, so a payout or withdrawal can never spend customer money.

When a customer asks 'why KES 115 not KES 100?', the answer must be a short, exact, true sentence — and the receipt must show it. We engineer the arithmetic so the question is rare and the answer is always right.

12

Fraud and abuse: attacked structurally, not hoped away

Fraud is not stopped by a check we hope people run; it is prevented by rules that run always. On a payments platform the adversary is a feature of the environment — harvesters, clean-ups, self-payments, stolen devices, money laundering through features. Every control below is structural:

  • Rate limiting is a financial control — per-user, per-action windows (5-second budgets on every money path) at the gateway and inside business logic, so a CDN bypass cannot dodge them.
  • Identity gates — KYC on the paths where it protects (large withdrawals, payouts, unverified recipients), with holds, refund schedules and the customer told exactly what is waiting and why.
  • Self-dealing blocked — you cannot pay yourself through a feature that is designed to pay someone else; recipient checks reject it before the dispatcher is ever armed.
  • Subscription and entitlement gates — expired or unverified entitlements hold outbound money in the wallet instead of sending it, forcing the legitimate renewal path before funds move.
  • Escrow and holds — unverified recipients, expired subscriptions, pending KYC and high-risk operations hold money in an escrowed state until the gate resolves; the customer sees the hold, not a silent freeze.
  • Account freezes — suspected fraud freezes the account with a banner and an owner-informed path back; legitimate users fight the system rarely because legitimate behaviours do not trip the structural rules.
  • Audit trails a regulator can read — every movement is explainable end-to-end: who, what, when, reference, outcome — hours, not weeks, to reconstruct.

The design goal is deliberately asymmetric: the legitimate user completes a payment in seconds with zero friction, while the fraudster meets a wall at every feature. Structural rules, not hoping.

13

Reconciliation: the statement is the truth

However good your ledger is, the ultimate truth is the gateway's statement — Safaricom's record of what actually moved on M-Pesa, PayPal's on its rails, the card processor's on cards, the bank's on transfers. Reconciliation is the daily proof that your ledger, your customers' wallets, your float and every gateway's statement all agree. We build it as a system, not a spreadsheet ritual:

  • Match by gateway reference — deposits, B2B send confirmations and B2C results are matched to their gateway references line-by-line, not by amount-and-date guesswork.
  • Total-your-side-vs-their-side — the platform's day totals reconcile against the gateway's statement; a mismatch is an incident with an investigation, never a quiet rounding.
  • Drill to the discrepancy — when totals disagree, the system isolates the specific lines, so 'which transaction' has an answer before anyone starts guessing.
  • Scheduled and unattended — a cron job runs the reconciliation at the close, flags exceptions, and pages the right person instead of waiting for a human to remember.
  • Self-healing where legitimate — expected mismatches (pending at statement cut, in-flight settlements) are classified and parked, so only genuine anomalies reach a human.
  • Survives up to millions of lines — batched matching and indexed references keep the daily reconcile fast even as the transaction volume grows to the scale where 'end of day' is tens of thousands of records.

We hold our own platform to this and we hold client platforms to it: if the gateway's statement disagrees with your books, someone must be able to explain it in minutes — and the system should have already found it.

14

The float, the platforms, and whose money is where

A payment platform is a stack of accounts: user wallets, an operational float that holds customer money in aggregate, a company revenue account, and the gateway's settlement. The architecture must make it impossible to confuse them — a payout of customer funds should never be able to touch platform revenue by accident:

  • User float vs company revenue are separate accounts — customer money (the float) and platform earnings (revenue) live in distinct ledgers with distinct rules; revenue is booked where it belongs and never co-mingles.
  • The float is the liability — customer balances are backed by the float; every customer credit is a float debit and every customer debit is a float credit, tracked so the platform always knows its position.
  • Subscription revenue redirects — subscription and fee income books into the revenue account via a booking layer, keeping the float clean and the company's income auditable without a giant rewrite.
  • Admin cash-outs can only reach company revenue — an owner-approved withdrawal draws from the earnings account, never from the operational float that customer balances sit on.
  • Ledgers per account — each pot has an immutable ledger with its own entry types, so 'did this movement touch the right pot' is a query, not a belief.
  • Triple-entry discipline — every movement has its counterpart row somewhere in the stack; the books always tie, and when they don't the system finds the mismatch.

The moment a platform holds balances, it holds other people's money. Separating the pots is not accounting hygiene — it is the difference between a mistake a customer feels and a mistake a regulator finds first.

15

Subscriptions, auto-renewal and money that repeats

Recurring money is a particular kind of trust: a promise that keeps charging. Billing cycles, retries, grace and the quiet dignity of a customer who missed a payment — all of it must be exact, gentle and legally defensible:

  • Exact-time entitlements — access is computed against precise timestamps, so a 30-day trial or a monthly plan cannot silently re-arm on a calendar wrap; a trial is used once, forever.
  • Cycle-aware auto-renewal — the charge hits at the correct period end with the correct price (monthly for monthly plans, annual for annual), guarded by locks against any double-debit, and extending from the real period end so early renewals never forfeit paid days.
  • Grace, not ambush — an insufficient balance produces one pending bill and one notification per cycle, full access continues through a defined grace window, and only a genuinely lapsed grace freezes the account.
  • Expiry and freeze jobs — cron routines with advisory locks sweep due renewals, expiry checks and freezes, each idempotent, batched and re-runnable without side effects.
  • Failed payments handled with dignity — retries on a sensible schedule, notices that explain, a downgrade or freeze path that protects the data while protecting revenue.
  • One clear account — invoices, receipts and fee breakdowns are correct, searchable and forever; 'was I charged?' gets an answer in seconds from support.

We run subscription billing against real money on our own platform — trials, auto-renewal, grace windows, KES plans, wallet top-ups. These mechanics are precisely the ones we hold ourselves to, and we hold your platform to the same.

16

Performance: fast at ten users and a million

Payment systems hit scale spikes few other software sees: payday, rent day, the first of the month, a viral promotion, a disaster appeal. The system must be right and fast at all of them, and slow money software is money software users abandon:

  • Indexed heat — the hot paths (pending-by-owner, transaction-by-reference, ledger-by-wallet) are indexed before they are needed, and bad indexes appear as delays before they appear as outages.
  • Batched sweeps — cron jobs process in bounded batches with LIMIT, advisory locks and SKIP LOCKED, so even millions of rows are drained steadily without one long query freezing the database.
  • Multi-minute monitoring — payment paths are watched with latency and error budgets; a path trending slow is a papercut with a timeline, not a mystery at month end.
  • Read/write separation where it pays — hot reads served from dedicated paths while writes stay on the transactional ledger, balanced against the honesty that the committed ledger is the truth.
  • Budgeted query surface — every dashboard, report and admin screen is a query with a plan; none of them runs untamed against the transactional core at peak.
  • Proven under real load — we test the balances, the floats and the reconciliation at the volume the customer actually expects, not the volume that fit on a slide.

A rent platform that is slow on the fifth of the month is a rent platform that taught its users to use cash again. We build payments that stay fast when it matters most — because that is when the money matters most.

17

Compliance and the regulator in the room

Money attracts regulators the way blood attracts sharks, and the regulation is not optional theatre — it is the operating licence. We build systems that make the compliance conversation easy because the truth is inspectable:

  • Audit trails as a first-class feature — every money and admin action is logged with actor, timestamp, reference and outcome, append-only and protected from tampering.
  • KYC where the rules say, with grace — identity verification on the paths that need it, with holds that protect both parties, refund schedules and the customer told exactly what is waiting.
  • Receipts and statements that satisfy accountants — correct, searchable, downloadable, forever, matched to transactions.
  • Regulator-ready exports — the day a regulator (or an auditor) asks, the truth reconstructs in hours: movements, references, holds, freezes, reversals.
  • Fee disclosure — the fee schedule is an inspected, maintained table and the customer sees it before confirming; the platform never argues with its own pricing.
  • Data residence and protection — customer and money data stored and handled per the law of the market, with signing, encryption and access controls that survive an audit.

We are not lawyers and we do not pretend to be — but we build the machinery that makes the legal team's life easy. When the books are provable, the compliance conversation stops being scary.

18

How we build and run payment systems

The process is the same whether we are building your money module or operating (and improving) the one that runs our own platform. It is disciplined, honest, and shipped in stages a business can absorb:

  • 01 · The money audit — we map every money movement in your current system: what can double-fire, what cannot reconcile, what a fraudster would do first. Usually the audit alone is worth the engagement.
  • 02 · The ledger design — accounts, entry types, references, states and the float/revenue split decided before any screen is drawn; the books are the foundation.
  • 03 · The lifecycle — every transaction state, transition and terminal state specified so that 'exactly once' is a property of the schema, not an aspiration.
  • 04 · The gateway wiring — real sandbox keys, real callbacks, real verification loops against the gateway's own status APIs, rehearsed under simulated failures.
  • 05 · The concurrency and fraud layer — locks, idempotency, rate limits, escrow, holds and the structural rules, written and tested before features are layered on.
  • 06 · The arithmetic — fees from the ranges table, exact amounts, shortfall messages and receipts agreed in numbers before the UI exists.
  • 07 · The reconciliation — the daily match against the statement, the drill-down, the scheduled job and the exception workflow — built the same week as the payments.
  • 08 · The surfaces — apps, web, admin and API all wired to the same ledger so no surface can drift from the truth.
  • 09 · The tests — replay, racing, double-callback, network-drop, freeze, reversal, reconciliation — the failure simulations are part of the build.
  • 10 · The ops — monitoring on the money paths, the cron sweeps, the support logs, the incident runbook and the 'where is my money' answer path.
  • 11 · The growth — more rails, more volume, more markets, more rules — added to the same architecture, never as a bolt-on that breaks the books.

You own the ledger, the code, the gateway keys and every audit. No hostageware, no 'you must stay on our cloud to keep your finances' — the money machinery is yours.

19

Honesty about payment systems

Because money is involved, we are direct about the trade-offs — the ones a vendor who wants the logo would never mention:

  • Payment systems are never 'done' — the gateways change, the regulation changes, the fraud changes; running money is a standing discipline, not a onetime build.
  • There is no such thing as fully automatic fraud-proofing — the controls are structural and powerful, but a determined, well-resourced adversary will keep testing; monitoring and response are part of the product.
  • Your fees and dunning decisions are a business choice — we implement what your model needs; we do not pretend one fee shape fits every business.
  • Sandbox is not production — a payment flow that passed a sandbox test but skipped the verification loop is a paperweight; the truth is in the gateway statement.
  • Concurrency cannot be eyeballed — it must be engineered and tested; the honest path is architecture plus failure simulation, not a code review blessing.
  • Reconciliation reveals the truth — the day your first statement matches your ledger to the penny is the day you know it is real; we build towards that day from the first sprint.
  • You own it all — the ledger, the code, the keys and the books; nothing that moved money should ever be hostage to a vendor relationship.

We will tell you honestly when your model, your volumes or your market does not need a full blown ledger-and-float platform — and give you the smallest correct build. And when it does need the real thing, this is the real thing.

The toolchain

The payment toolchain

This is the exact stack powering a live, production payment platform on M-Pesa — the same machinery we bring to every transaction system we build. Every layer exists because a live system required it.

stack.toolchain

01

Ledger & database

Where the truth lives

  • Managed PostgreSQLThe transactional ledger, double-entry books, RLS at the row level and the immutable record behind every wallet.
  • Double-entry wallet schemaAvailable/locked/pending balances, entry-type types, references and audit rows born with every movement.
  • Row-level lockingFOR UPDATE on the wallet and transaction rows so concurrent payments physically cannot race.
  • CHECK constraintsBalance ≥ 0 as the final wall behind the pre-flight check and the atomic decrement.
  • Advisory locksMoney crons and sweeps claim locks so overlapping runs cannot double-execute.
  • Indexed hot pathsPending-by-owner, by-reference and ledger-by-wallet indexed before the volume arrives.

02

Payment rails

How money actually moves

  • Daraja (M-Pesa)The Safaricom gateway: STK Push, C2B, B2B, B2C and PayBill/Till on the real rails.
  • STK Query / verificationThe status API that proves a callback is genuine before anything is credited.
  • B2B disbursementPayBill/Till payouts, rent and bill settlement routed business-to-business.
  • B2C disbursementWithdrawals, earnings and send-money to a phone, correlated by send reference.
  • Card / bank railsVisa/Mastercard and bank transfers for the surfaces M-Pesa does not cover.
  • Fallbacks & retriesTransport errors kept pending, definitive failures acknowledged, retries answered with the settled state.

03

Concurrency & safety

Exactly once, under pressure

  • Idempotency keysReferences born at initiate; replays answered, never re-executed.
  • Transaction statesInitiated → pending → confirmed/failed/reversed/expired with terminal-state protection.
  • Escrow semanticsavailable → locked at initiate; consumed on success, released on failure.
  • Three-layer balance protectionPre-flight inside the lock, atomic decrement, CHECK constraint — none can fail alone.
  • Rate limitingPer-user, per-action windows at the gateway and inside logic.
  • Dead-letter queuesFailing work rests visibly with its payload; nothing silently lost, nothing silently replayed.

04

Fraud & identity

Structural walls, not hope

  • KYC gatesIdentity on the paths that need it, with holds, refund schedules and clear customer messaging.
  • Escrow holdsUnverified recipients, expired subscriptions and pending KYC hold money until the gate resolves.
  • Freeze/unfreezeAccount freezes with the owner informed and a clear path back.
  • Self-dealing blocksRecipient checks reject paying yourself through a peer-to-peer feature.
  • Enumeration resistanceIdentical responses whether an account exists or not, tested not assumed.
  • Audit trailsActor, timestamp, reference, outcome — append-only and regulator-readable.

05

Operations

Running money while you sleep

  • pg_cron jobsAuto-renewal, expiry sweeps, stale-order expiry, KYC refund schedules, reconciliation.
  • Reconciliation engineMatch by gateway reference, total-against-statement, drill to the discrepancy.
  • Payment gateway logsRaw callbacks and verification results queryable by support and reconciliation.
  • Notification fan-outThe customer is told when money moves, is held, or is refunded — never surprised.
  • Monitoring & budgetsLatency and error budgets on money paths; a papercut with a timeline, not a mystery.
  • Incident runbooks'Where is my money' answered in minutes from the ledger and the gateway log.

06

Surfaces

One truth, many faces

  • Mobile appsSTK push, wallets, top-up, pay, send, withdraw — the customer's front door to the ledger.
  • Web & adminDashboards, operations, fee ranges, holds, freezes and reconciliation views.
  • DesktopPoint-of-sale with money-safe acceptance through the same shared ledger.
  • Public/partner APIsSame dispatch, same callbacks, same idempotency for the platform's partners.
  • Exact-shortfall UXInsufficient balances return the precise amount needed so a user tops up once and succeeds.
  • Receipts & statementsCorrect, searchable, downloadable, forever.

07

Testing

Failure rehearsed before money moves

  • Replay testsEvery callback redelivered; the ledger must change exactly once.
  • Race/overlap testsTwo payments, a webhook and a cron on the same wallet — the result must be correct.
  • Network-drop drillsKill the connection mid-payment; the recovery is proven, not hoped.
  • Fault injectionTimeouts, duplicates, partial writes — the money survives each.
  • Verification-loop testsA forged or garbage callback is never credited; a verified one is credited exactly once.
  • Volume testsThe balances, floats and reconciliation proven at the volumes the business actually expects.

Lifecycle

The payment lifecycle — from audit to scale

Building and running a payment system is a standing discipline. This is the lifecycle every transaction system we build or operate passes through — including the one we run ourselves.

01

Audit

Map every money movement in the current system: double-fire risks, reconciliation gaps, fraud surface, fee leaks.

02

Design the ledger

Accounts, entry types, references, states and the float/revenue split — the books decided before screens.

03

Specify the lifecycle

Every state and transition; 'exactly once' as a property of the schema.

04

Wire the gateway

Real keys, real callbacks, real verification loops against the gateway's own status APIs.

05

Build safety

Locks, idempotency, rate limits, escrow, holds and the structural fraud rules.

06

Set the arithmetic

Fees from the ranges table, exact amounts, shortfall messages, receipts agreed in numbers.

07

Build reconciliation

Daily match against the statement, drill-down, scheduled job and exception workflow.

08

Connect surfaces

Apps, web, admin, API — every surface wired to the one ledger.

09

Rehearse failure

Replays, races, double-callbacks, network drops, freezes, reversals — simulated in the build.

10

Operate

Monitoring on the money paths, cron sweeps, support logs, incident runbooks.

11

Reconcile daily

The statement matched every day; anomalies surfaced as workflows, never as surprises.

12

Grow

More rails, more volume, more markets — added to the architecture, never as a bolt-on.

Closing

More than development

Payment and transaction systems are where software stops being an app and starts being a promise with a ledger behind it. We build the promise so it holds — under concurrency, under fraud, under regulators and under your busiest hour. That includes:

Real M-Pesa integration through Daraja — STK, C2B, B2B, B2C, PayBill and Till.Cards and bank rails where the market needs them.Wallets with available, locked and pending genuinely separated.Escrow semantics on every hold and settlement.Three-layer balance protection against any negative balance.A rigorous transaction lifecycle with terminal states.Callback discipline: claims verified against the gateway before credit.Idempotency on every money action, tested not assumed.Fees and rounding done exactly from a configurable table.Exact-shortfall top-up messaging.Rate limits as a financial control, enforced at gateway and logic.Structural fraud prevention: KYC, holds, freezes, self-dealing blocks.Subscription and auto-renewal billing with grace, not ambush.Revenue and customer float separated into their own ledgers.Daily reconciliation against the gateway statement.Scheduled sweeps that expire stale transactions and release holds.Payment gateway logs that answer 'where is my money' in minutes.Regulator-ready audit trails on every movement.The discipline run live on our own payments platform.You own the ledger, the code, the keys and the books.

A payment platform is a promise: that the money you see is the money you have. Everything above exists to make that promise true, all the time, at whatever volume lands on it.

We run one of these every day. When you need one, you get the system that survived being live — not the one that survived only a sandbox.

Previous capability

API Development

Next capability

Business Software

Building something like this?

The discipline above is what we run on our own products every day. If it would help on yours, our door is open.