It runs. Real money moves through it. A small team operates it without me standing over them.
The client is under NDA. I can describe how things were built and what could go wrong. I can't name them, show their data, or give you enough detail to rebuild their system. The tooling further down is mine, so that part I can show you properly.
Context
A payment company needed its software built from nothing. Four parts: the API other businesses connect to, the portal merchants log into, the checkout their customers pay on, and the engine underneath where the money moves. I was the engineer on it.
The problem
Every payment gateway behaves differently. A merchant should only ever integrate once, so one API had to cover all of them and keep the same shape whichever one ran underneath. Around that: the merchant portal, the checkout, the staff tools, and the reporting. That is a team's worth of work. There was one of me.
Design goals
Be correct where money moves. Swap card numbers for tokens at the door, so real card data never spreads through the system. Keep one stable contract over many gateways. And stay operable by a small team, which rules out anything that only stays safe if someone remembers to be careful.
Key architectural decisions
Serverless, so there is less to run and watch. Payments are handled as explicit steps that can be undone halfway through, because a payment that half-worked is the hard case. Signed requests, with keys that rotate. Every endpoint that changes something can be called twice safely, because a client that retries will call it twice sooner or later. Card data tokenized at the door. Every environment defined in code.
The signed surface, in both directions
Both sides sign what they send, so neither has to trust an unsigned message. Keys are scoped: a restricted key reaches only what it was issued for, and the powers a key should never hold are left out of the list it can be granted from at all. The check at the edge works out who the caller is once, then hands that down to everything behind it. So a request simply arrives knowing which merchant it belongs to. Nobody has to remember to filter a query, which matters, because that is the kind of thing people forget in month nine.
The outbound edge, which is the one people forget
Webhooks send data out to a web address the customer gives you. That points a request from inside your network at somewhere you don't control, which is worth being careful about. Checking the address when it is first saved only catches the obvious cases. A name that quietly points somewhere inside your own network gets through, and it can be re-pointed after you check it. So the check happens at send time, against the exact address the connection is about to use. There is no gap between checking and connecting. It is a small piece of code and it closes the whole category, not one example of it.
The internal console, and the defect I'd built into it
The internal tool that support, operations and risk staff use got its own written design standard, instead of being the screen that gets whatever is left over. Two rules from it. If your role can't use a screen, it isn't there at all, rather than greyed out, because a greyed-out button is a list of things worth attacking. And the banner showing which merchant you are working on is deliberately loud, and carries their status, so nobody acts on a suspended account by accident. The worst thing that review turned up was my own bug. A screen that failed to load looked exactly like a screen with nothing on it. "There is nothing here" and "I could not find out" appeared the same way. On a screen people use to watch money, that is dangerous, because it looks like a quiet day. Every view now lands in one of seven states, including error, and sections load on their own so one failure can not blank the rest of the page.
The machinery: a knowledge layer for the agents
Several agents work at once, each in its own copy of the code, and each one starts with no memory. The context that matters is usually the part nobody wrote down. So I put the knowledge in a service they all read from: what the software is supposed to do, a running journal per ticket, notes on things learned the hard way, and a list of known problems so the same one isn't found again every month. Search uses both keywords and meaning, and falls back to keywords alone if the meaning part is down, because a memory that goes offline is worse than none. Agents check in and check out, so two of them don't edit the same files at once. This is the piece I'd point at first. It is why the platform never needed a team to remember it.
The machinery: standards that are compiled, not promised
Standards written in a document are decoration. These run. The tests are grouped by what they protect: rules the codebase may never break, things that must always be true, who is allowed to do what, security, and full end-to-end runs. Checks are a gate, not a step. That is the only version that holds up against an agent which will happily write whatever gets it unstuck.
The machinery: documentation as a product
The integration docs are their own application, with their own tooling, written for an engineer on the other side who has never met me. It is also the fairest test of the handover promise everyone in this market makes. Documentation only counts as a way out if a stranger can act on it.
Failure modes designed for
A gateway times out halfway: the steps already taken get undone on purpose. The same payment is sent twice: the second one is recognised and ignored. A key leaks: requests are signed and keys rotate. A message the system can't process: it is set aside for a human and can be replayed. The design assumes things break, because the alternative is finding out from a mismatched report at the end of the month.
What this demonstrates
Agents wrote the overwhelming majority of the code. I set the standards, built the tooling that enforces them, and checked what shipped. What came out is a working platform in a field where a wrong number costs somebody real money.
TypeScript · AWS Lambda · Step Functions · DynamoDB · API Gateway · Infrastructure as code · Next.js · React