DigitalPay payouts
A rewrite of the consumer payout portal into a configuration-driven state machine. It carried a payout to about a million recipients, and launching a new program went from days of often-buggy work to ready to test within a morning.
I owned the consumer payout portal from its first version and rewrote it end to end. The payments backend was built by the company’s CTO, who was also its principal engineer.
the problem
DigitalPay started out serving a few large government and airline customers: one-off payments, created on the fly, for a monthly fee. Then the business pivoted to class-action settlement payouts, earning a commission on volume. Settlement administrators run many programs each, and every program could need different payment methods, often chosen by the customer or the judge on the case, plus its own copy and branding. I had written most of the first version of the payout portal early in my career, on the old assumptions, with core logic hard-coded. Adding per-program customization after the fact was slow and risky: every new program meant days of changes and a deploy, and testing a single screen meant walking a test payment through one specific network. Our first big payout, to about a million recipients, was coming, and I did not trust v1 with it.
building it before pitching it
I started the rewrite on my own, on nights and weekends, designing it to be composable from the first line and then rebuilding every v1 feature on the new architecture. It took five to six months of spare time, fitted around sprint work, from the first line to feature-complete, and it ended up with more features than v1 and far less fragile code.
By the time I proposed it, it was largely built and I could demo it.
We kept every existing program running on v1 and piloted v2 on the next new program, so live payouts were never at risk during the switch.
thinking like the product owner
That rewrite was not a one-off. Many of the features I shipped at DigitalPay were my own ideas, which I demoed before anyone asked for them, so the team could decide on something real instead of a description.
I spent more time inside the payment flows than anyone, so I was usually the first to see the gaps, and to spot where the design would fail when a future program arrived with new requirements. The layered configuration came from that habit of designing for the programs we had not signed yet.
configuration in layers
Each customer has one base configuration for branding, copy and defaults. Programs override it, and overrides can go further, per payment network, payment status or amount. The resolved configuration becomes the props passed to each component, so it is always clear why a screen looks the way it does.
The portal itself is a state machine whose states mirror the backend’s payment statuses, with composable views and flows for dozens of payout methods: ACH and PayPal, direct to bank, paper checks with address collection and normalization, rewards programs and gift cards.
designing with the backend
I worked in lockstep with the CTO, who wrote the backend. The backend stayed the source of truth for business logic, and my job was to handle every state it could produce; together we designed the API, the config schema and even the property names, which let us move very quickly.
After the frontend shipped, he rewrote the backend as a v2 of its own, partly inspired by the frontend rewrite.
launch day
The payout to about a million recipients ran on v2. At launch, an unrelated backend bug broke one payment option. Because everything was configuration, we disabled that option and added a notice on page load with the expected fix time, telling recipients to choose another payment method or check back later. No deploy, no hot-fix under pressure, and the rest of the payout carried on.
Each program sent email from its own domain, so notification emails went out gradually to warm those domains up rather than all at once.
architecture
what i built
- A ground-up rewrite of the consumer payout portal as composable payment flows driven by layered JSON configuration stored in the database.
- Configurable copy, payment language, branding to each customer’s spec, payment flows, available payment options, notifications and error messaging, with overrides per program, payment network, payment status and amount.
- Incident controls in configuration: disable a payment network during an outage and show recipients a page-load notice with the expected fix time, with no code change.
- Scripts that generate every user notification template from a program’s configuration and load it into the production database.
- Storybook previews of every component and state, driven by the same configuration props as production.
what changed
- The payout to about a million recipients ran smoothly on v2, including a backend incident at launch that was contained through configuration alone.
- Launching a program became a new row in the programs table with its configuration in a column: ready to test within a morning, instead of days of often-buggy work.
- Onboarding a new customer went from days to minutes or hours, limited mainly by collecting logos and custom language and provisioning their portal URL.
- Product managers and customer representatives could finally see every screen in every payment state. They described what they wanted, and it translated directly into configuration.
- Fewer code changes to test and deploy, and a stable, predictable product for recipients in the middle of live payouts.
looking back
Engineers still edited the JSON by hand. Storybook already showed exactly what any configuration would render, so the natural next step would have been a constrained visual editor that let product and support staff launch programs themselves.