Skip to content
Back to all work
Full-Stack / FinOps2026

Recko

Reconciliation, commissions and payouts for high-volume transaction businesses — where the interesting number is not what matched, but what didn't.

Role
Architect & sole engineer
TypeScriptExpressPrismaPostgreSQLNext.js 14RechartsJWT

The application

Screens, captured from the running app

Booted against its seeded database and walked route by route — these are the real pages, not mockups.

Recko operations dashboard with match rate, break count and payout status.
Operations overview. Match rate is the headline number, break count sits next to it — a high match rate with unexamined breaks is not good news.
Recko reconciliation table showing matched transactions and a flagged break across bank, PSP and internal sources.
Reconciliation across three sources. 91% matched over ~2,450 transactions, and the ~250 that didn't are the point of the screen.
Recko commission run detail with tiered rates and refund reversals.
A commission run is a historical record. Changing a rule tomorrow doesn't rewrite what was already paid.
Recko payout list across draft, approved, processing, paid and failed states.
Payouts as a state machine — the seed exercises the failure states too, because that's where payout UIs usually break.
Recko reporting view with reconciliation and commission trends.
Reporting over the reconciled ledger rather than the raw feeds.

The problem

An internet company takes money through a payment provider, the provider settles into a bank, and an internal ledger records what the company thinks happened. Three systems, three views of the same transaction, and none of them agree — because of timing, fees, partial refunds, duplicate webhook deliveries, and the occasional transaction that exists on one side and nowhere else.

Reconciling that by spreadsheet works until volume makes it impossible, at which point companies stop reconciling and start writing off the difference. The write-off is the leakage.

Matching, and being honest about what didn't match

The engine matches on reference first, then falls back to amount-and-date within a configurable tolerance. Anything left over is a break, and breaks are categorised rather than pooled:

  • Timing — present on both sides, settled in different periods
  • Amount mismatch — same reference, different value (usually a fee)
  • Duplicate — the same transaction delivered twice
  • Missing on A / missing on B — present on one side only

That taxonomy is the actual product. A single "unmatched" bucket tells an operator there is a problem; these five tell them which team to send it to. The current seed reconciles around 2,450 transactions at a 91% match rate, leaving roughly 250 breaks — deliberately, because a demo that matches everything demonstrates nothing.

Every match decision writes an audit record, including the rule that produced it, so a disputed match can be explained rather than re-run.

Commissions

A tiered, percentage and fixed-rate engine over the reconciled transactions, with refund reversal — when a transaction is refunded, the commission it generated is clawed back in the period the refund lands, not the period the sale did. Getting that wrong is how commission systems quietly overpay.

Runs are historical records, not recomputations: a commission run for last month keeps the numbers it produced, even if a rule changes afterwards.

Payouts

A payout is a state machine — draft, approved, processing, paid, failed — and the seed exercises the whole thing rather than the happy path, including failures, because the failure states are where the UI usually falls apart.

Architecture

Express and TypeScript over Prisma, with SQLite for development and PostgreSQL for production behind the same schema. JWT and bcrypt for auth. The job runner is in-process but written against a BullMQ-shaped interface, so moving reconciliation runs onto a real queue is a swap rather than a rewrite. The Next.js 14 dashboard reads through SWR; Recharts for the reporting surface.

The repository also carries the public marketing site alongside the operations dashboard — two quite different design registers in one codebase.

You have a process thatshould be a system.

Tell me what arrives, who has to act on it, and where it currently falls over. That conversation is usually enough to scope the build.