A payment platform on .NET: what it actually costs, and how much of it you never have to write

Conversations about payment platforms almost always start from the wrong end. People argue message brokers. They debate microservices versus a modular monolith. They draw the schema.

Eighteen months later a team of twelve has produced a million and a half lines, of which maybe two hundred thousand are business logic. The rest is transport, retries, dashboards, deployment, and increasingly elaborate attempts to work out where a payment got stuck last night.

This post is about that second number. About the layer that earns nothing and without which nothing runs — and about how much of it you can simply not write in 2026.

We've spent four years building redb: a typed store on top of PostgreSQL, MS SQL and SQLite; an integration engine; a runtime with clustering and a dashboard; and an identity server speaking OAuth 2.1 and OpenID Connect. It runs in our own production, ships as packages and images, and — relevant to everything below — every Pro capability is free across the whole 3.x line, no keys and no license server.

What follows is a payment platform taken apart by layer, with an honest accounting of where the person-years go. No code: the technical deep dives live in other posts. This one is about money, timelines and risk.

Part of the redb / redb.Route series — recent posts first:

Sources: github.com/redbase-app. Docs: redbase.app.


Three layers, three budgets

Every payment platform — a marketplace's payouts, a wallet, a PSP, a corporate treasury, a SaaS billing engine — decomposes into three parts, and each has a different cost profile.

The core. Holds money and operations. Postings, balances, limits, pricing, reconciliation. This is what you get paid for, and it's where a mistake costs the most: a mis-rounded cent isn't a bug, it's a complaint.

The facade. Decides who gets in. Who is this, what are they allowed to do, how did they prove it, and how is all of that recorded for the auditor and the security team. A layer made entirely of standards, invisible to the business — right up until the first audit.

The integrations. Talk to the outside world. A bank on IBM MQ, an acquirer over HTTP, payout files on SFTP, accounting on a schedule, fraud scoring on a queue. The layer that breaks most often and costs the most when it does, because the thing on the other end belongs to someone else.

Layer by layer: what genuinely has to be yours, and what's already written.


Layer one: the core, where cents don't evaporate

Start with something that looks like an implementation detail and is actually a first-order business risk: how the number that measures money is defined.

In most .NET projects an amount lands in the database as a decimal with whatever precision someone set in the ORM config years ago. Usually two decimal places, sometimes four. Then: a fee is a percentage, the percentage produces a third decimal, the third decimal gets rounded, roundings accumulate — and a year later reconciliation with the acquirer is off by an amount nobody can explain. That's not a hypothetical. It's a genre, and it gets resolved by hand, by an analyst and a developer, over weeks.

In redb, monetary precision isn't in application config. It's in the storage schema itself: numeric values live in a column with 38 digits of precision, 18 of them after the decimal point. The schema comment on that column says why, in as many words: exact decimal numbers for financial calculations, lossless, for money, taxes and percentages where arithmetic accuracy matters.

In business terms:

  • Fees, FX and tax compute without accumulating rounding error. Eighteen decimals is headroom for any chain of calculations, not "two decimals and fingers crossed."
  • Multi-currency and crypto work out of the box. Eighteen decimals is exactly the precision Ether is denominated in. If a crypto line of business shows up next quarter, the storage doesn't change.
  • Precision isn't a function of who configured the project. A developer can't accidentally declare an amount with two decimals, because the decision was made one level below them.

Money doesn't get double-spent

The second baseline guarantee: one operation executes once, and never half-way. This part is standard and boring, as finance should be — real transactions with explicit control, atomic execution of a group of operations, and row-level locking while a record is being modified.

That last one is worth calling out, because it's where homegrown solutions usually break. Classic scenario: two requests read the balance concurrently, both see "sufficient funds", both debit. The defence is locking the row for the duration. In redb that's a first-class mechanism, and it isn't decorative — it's what our own subsystems run on: the identity server's token store, the failed-MFA-attempt counter, the cluster's distributed lock. It carries production load for us, rather than existing "in case someone needs it."

Deletion you can undo

Another thing that gets reinvented in every finance project: correct deletion. You can't hard-delete — reporting, regulators, incident forensics. So it's either an is_deleted flag dragged into every query forever, or archive tables and the job of keeping them in sync.

In redb this is built in, in two phases. First the object is marked — together with the whole tree of records hanging off it — and immediately disa