Problem
Approach
Decisions made
Implementation
What's next
Closing

Payment operations

Rebuilding the back office of a B2B payments platform

Context

A payment gateway with a back office attached. Three roles hold different rights over the same money — super admin, tenant admin, tenant finance — plus the merchant.

The work spanned the dashboard, the accounting module (invoices, settlements, pricing configuration, balance reports, credit notes, fee allocation, email templates), the notification system, and the customer-facing deposit and withdrawal flows.

Why this shape of problem is specific to payments

  • Multi-currency by default. Three different dollars, plus PLN, CZK, INR. A currency symbol is ambiguous; an ISO code is not.
  • Reconciliation is regulated work. The gap between what the platform did and what a merchant is owed has to be legible, never inferred.
  • Role-split pages. The same data, different rights, without doubling what has to be maintained.

Role

Senior Product Designer — sole designer on the module

Team

CTO, backend team, internal finance users

Timeline

Nov 2025 – Jul 2026

Client

Name withheld

Problem

What was wrong

Nothing said what mattered. Row actions, system messages and amounts all carried identical weight. The page had no opinion about why you had opened it. The amount — the only number anyone came for — competed with four exposed icon buttons, a menu on every column header and an amber status dropdown.

Competing patterns. Invoices had a bespoke filter card. Settlements used merchant multi-select tags. Balance reports used a drawer. Same job, three answers, and every new feature added a fourth.

Colour carried no meaning. Purple applied filters, grey reset them, green generated an invoice. On the dashboard, red and green ran as chart series in a product where red already meant failed and green meant captured. The same status could appear in four colours across four widgets.

The numbers disagreed with each other. Currency symbols in a multi-currency platform. Two decimals in one card, none in the next. Deltas with no sign. One month-on-month figure printed as 1005.88% and nobody had blinked.

Events and money looked alike. A status change, a log line and a settled amount shared one visual voice. For people who reconcile money for a living, that ambiguity is the expensive kind.

The clearest symptom: four lines of instructions sat above the invoice table explaining which fields were editable, what the processing badge meant and how to merge PDFs. When a screen needs a manual printed on it, it has already failed.

Approach

Audit and proposals

Two patterns and one contract — not eight redesigned pages.

A. The contract: colour and numbers

Everything else depends on this, so it came first.

  • Colour has three jobs that never overlap. Functional for interface chrome. Semantic for state — error, warning, success, info. Categorical for data series, drawn from a single hue family. After the split, no data colour can be misread as a status.
  • Checked, not eyeballed. Every adjacent series pair and every text-on-fill combination tested against WCAG AA. Series stay separable in greyscale, so colour is never the only signal — deltas carry a sign and an arrow as well.
  • Numbers follow one rule everywhere. ISO code after the amount, never a symbol. Two decimals on every financial value. Tabular figures, right-aligned. Em dash for missing, explicit zero for zero — a hyphen reads as a minus.
  • Shipped as tokens. Separators, decimal places, abbreviation threshold and null display defined as tokens rather than described in prose, so the rule survives the handoff and lives in code.

B. One list, everywhere

  • One header, one order: breadcrumb, title with a live count, primary action, status tabs, search, filters, active filter chips. Always that order, so what you learn on one table works on the next.
  • The states people work in became tabs — All, Draft, Outstanding, Past Due, Paid — sitting above the table rather than inside a filter panel. The filter people reach for most often needs no filtering.
  • Four exposed icons became one row menu: view, download, status history, logs, then delete, separated and always last. Row noise dropped and the amount got the attention.
  • Columns belong to the user. A per-table control to choose and reorder columns. Sensible defaults, but a reconciliation job and a finance lead's job don't need the same six.
  • Inherited by settlements, credit notes, balance reports, fee policies and email templates.

C. The configurator

Pricing configuration decides what a merchant is charged — fees, tax, payment terms, transaction rules. Getting it wrong costs real money, so the form had to teach while it was being filled in.

  • An accordion, not a wall. Three collapsible sections — general settings, transaction fee rules, financial details — so forty fields arrive as three decisions. Required sections carry an asterisk and open by default.
  • Numbered steps that say why. Each section splits in two: the left names the step and what it's for, the right holds the fields. The reason sits beside the input permanently, not behind a tooltip.
  • Rules where the mistake happens. Anything that behaves unexpectedly gets an inline note — how dynamic invoice numbering resolves, when a setup fee is skipped, what triggers a minimum fee. Written once, at the point of use.
  • Blank means inherit. Read-only reference cards show what the system already holds for that merchant, so overriding a stored value is a visible choice rather than an accident.
  • Three exits, not two. Cancel, Save as Draft, Save and Apply — because commercial terms move over days and a half-filled config is a normal state, not an error.

D. The kit, recombined

Two later pieces of work tested whether the approach actually scaled.

Integrations. Merchants ask for the payment methods their own customers use, and every provider arrives with its own fields, verification steps and mandatory disclosure text. Each integration looked bespoke. None of them were — they're the same components rearranged: method selector, amount with currency, provider hand-off, pending, confirmation, failure, retry. Where a provider needs something extra — an IBAN, an extra consent line — it becomes a variant of a component that already exists rather than a fork in the flow. The next integration is an assembly job, not a project.

Notifications. The ask was one feature: let an admin send a message to every user. Building it exposed the real problem — notifications had accumulated in four unrelated places with no logic to which was which. I resolved it with three patterns (banner, modal, flag), a six-question rule for choosing between them, and 31 use cases specified against that rule across five categories. Severity and pattern became separate axes, because conflating them was what produced the original mess.

Decisions made

How I decided

Start from the pattern, not the page. Fixing eight pages one at a time produces eight more patterns. Fixing the pattern once and applying it is the only version that holds after you leave.

Explain in place. If a rule is non-obvious, the explanation belongs beside the field, not behind a help icon. Those four instruction lines above the old table were the right instinct in the wrong location.

Two visual registers, kept apart. Financial values are typographic — tabular, right-aligned, ISO code, never coloured for decoration. System events are chips and log rows — never right-aligned, never in the numeric style, always timestamped with a timezone. Colour on a number only ever means direction. Colour on a chip only ever means state.

Split by rights, not by hiding. Merchants read invoices; tenants manage them. Rather than conditionally hiding controls inside one page, the merchant gets a simpler top-level Invoices page — no create, no edit, no delete. Fewer permission branches to maintain, and neither audience sees a screen designed around someone else's job.

Define components by property, not by drawing them. The button resolves to 320 combinations from four properties; the input to 144 from four more. Making Link and Destructive hierarchies rather than separate components means a text-link action inherits the same sizes, icon slots and states as every other button — so it can't drift, and a new case is a selection rather than a new artboard.

Write the rule down. Patterns that live only in a Figma file get re-invented by the next feature. The number formatting guide, the colour role definitions and the notification decision rule exist as documents and tokens so the decisions outlive the handoff.

Implementation

Shipped: the dashboard rebuild, the list pattern across the accounting module, the pricing configurator, and multiple payment flows.

What worked

  • Two patterns instead of eight. The second and third table took a fraction of the time the first one did, because there was nothing left to decide.
  • Explanation beside the field. The configurator stopped being a form only its author could fill in correctly.
  • Reserving semantic colour. Once it was reserved, the dashboard and the accounting tables came out consistent without either one referencing the other. That's the test of whether a rule is real.
  • The kit held. By the time the open banking flow was built, it was assembled from existing components rather than designed from scratch — which was the whole bet.

Full notification framework, signed off and built. Three patterns, one decision rule, 31 cases. The rule matters more than the cases — it's what resolves the ones nobody has listed yet.

Events and money, fully separated. Moving status history and settlement logs out of tabs and into a side panel over the record, so the history stays on screen while you read the thing it describes.

Tokens adopted everywhere. The formatting and colour-role tokens are defined but not yet adopted across the older pages. Until they are, the rule is a document rather than a constraint.

What I'd measure. Time to issue a settlement. Edits per invoice before it's correct. Support requests asking what an invoice status means. All three are countable, and none of them were being counted.

Closing

Thanks for reading!

Problem
Approach
Decisions made
Implementation
What's next
Closing

Payment operations

Rebuilding the back office of a B2B payments platform

Context

A payment gateway with a back office attached. Three roles hold different rights over the same money — super admin, tenant admin, tenant finance — plus the merchant.

The work spanned the dashboard, the accounting module (invoices, settlements, pricing configuration, balance reports, credit notes, fee allocation, email templates), the notification system, and the customer-facing deposit and withdrawal flows.

Why this shape of problem is specific to payments

  • Multi-currency by default. Three different dollars, plus PLN, CZK, INR. A currency symbol is ambiguous; an ISO code is not.
  • Reconciliation is regulated work. The gap between what the platform did and what a merchant is owed has to be legible, never inferred.
  • Role-split pages. The same data, different rights, without doubling what has to be maintained.

Role

Senior Product Designer — sole designer on the module

Team

CTO, backend team, internal finance users

Timeline

Nov 2025 – Jul 2026

Client

Name withheld

Problem

What was wrong

Nothing said what mattered. Row actions, system messages and amounts all carried identical weight. The page had no opinion about why you had opened it. The amount — the only number anyone came for — competed with four exposed icon buttons, a menu on every column header and an amber status dropdown.

Competing patterns. Invoices had a bespoke filter card. Settlements used merchant multi-select tags. Balance reports used a drawer. Same job, three answers, and every new feature added a fourth.

Colour carried no meaning. Purple applied filters, grey reset them, green generated an invoice. On the dashboard, red and green ran as chart series in a product where red already meant failed and green meant captured. The same status could appear in four colours across four widgets.

The numbers disagreed with each other. Currency symbols in a multi-currency platform. Two decimals in one card, none in the next. Deltas with no sign. One month-on-month figure printed as 1005.88% and nobody had blinked.

Events and money looked alike. A status change, a log line and a settled amount shared one visual voice. For people who reconcile money for a living, that ambiguity is the expensive kind.

The clearest symptom: four lines of instructions sat above the invoice table explaining which fields were editable, what the processing badge meant and how to merge PDFs. When a screen needs a manual printed on it, it has already failed.

Approach

Audit and proposals

Two patterns and one contract — not eight redesigned pages.

A. The contract: colour and numbers

Everything else depends on this, so it came first.

  • Colour has three jobs that never overlap. Functional for interface chrome. Semantic for state — error, warning, success, info. Categorical for data series, drawn from a single hue family. After the split, no data colour can be misread as a status.
  • Checked, not eyeballed. Every adjacent series pair and every text-on-fill combination tested against WCAG AA. Series stay separable in greyscale, so colour is never the only signal — deltas carry a sign and an arrow as well.
  • Numbers follow one rule everywhere. ISO code after the amount, never a symbol. Two decimals on every financial value. Tabular figures, right-aligned. Em dash for missing, explicit zero for zero — a hyphen reads as a minus.
  • Shipped as tokens. Separators, decimal places, abbreviation threshold and null display defined as tokens rather than described in prose, so the rule survives the handoff and lives in code.

B. One list, everywhere

  • One header, one order: breadcrumb, title with a live count, primary action, status tabs, search, filters, active filter chips. Always that order, so what you learn on one table works on the next.
  • The states people work in became tabs — All, Draft, Outstanding, Past Due, Paid — sitting above the table rather than inside a filter panel. The filter people reach for most often needs no filtering.
  • Four exposed icons became one row menu: view, download, status history, logs, then delete, separated and always last. Row noise dropped and the amount got the attention.
  • Columns belong to the user. A per-table control to choose and reorder columns. Sensible defaults, but a reconciliation job and a finance lead's job don't need the same six.
  • Inherited by settlements, credit notes, balance reports, fee policies and email templates.

C. The configurator

Pricing configuration decides what a merchant is charged — fees, tax, payment terms, transaction rules. Getting it wrong costs real money, so the form had to teach while it was being filled in.

  • An accordion, not a wall. Three collapsible sections — general settings, transaction fee rules, financial details — so forty fields arrive as three decisions. Required sections carry an asterisk and open by default.
  • Numbered steps that say why. Each section splits in two: the left names the step and what it's for, the right holds the fields. The reason sits beside the input permanently, not behind a tooltip.
  • Rules where the mistake happens. Anything that behaves unexpectedly gets an inline note — how dynamic invoice numbering resolves, when a setup fee is skipped, what triggers a minimum fee. Written once, at the point of use.
  • Blank means inherit. Read-only reference cards show what the system already holds for that merchant, so overriding a stored value is a visible choice rather than an accident.
  • Three exits, not two. Cancel, Save as Draft, Save and Apply — because commercial terms move over days and a half-filled config is a normal state, not an error.

D. The kit, recombined

Two later pieces of work tested whether the approach actually scaled.

Integrations. Merchants ask for the payment methods their own customers use, and every provider arrives with its own fields, verification steps and mandatory disclosure text. Each integration looked bespoke. None of them were — they're the same components rearranged: method selector, amount with currency, provider hand-off, pending, confirmation, failure, retry. Where a provider needs something extra — an IBAN, an extra consent line — it becomes a variant of a component that already exists rather than a fork in the flow. The next integration is an assembly job, not a project.

Notifications. The ask was one feature: let an admin send a message to every user. Building it exposed the real problem — notifications had accumulated in four unrelated places with no logic to which was which. I resolved it with three patterns (banner, modal, flag), a six-question rule for choosing between them, and 31 use cases specified against that rule across five categories. Severity and pattern became separate axes, because conflating them was what produced the original mess.

Decisions made

How I decided

Start from the pattern, not the page. Fixing eight pages one at a time produces eight more patterns. Fixing the pattern once and applying it is the only version that holds after you leave.

Explain in place. If a rule is non-obvious, the explanation belongs beside the field, not behind a help icon. Those four instruction lines above the old table were the right instinct in the wrong location.

Two visual registers, kept apart. Financial values are typographic — tabular, right-aligned, ISO code, never coloured for decoration. System events are chips and log rows — never right-aligned, never in the numeric style, always timestamped with a timezone. Colour on a number only ever means direction. Colour on a chip only ever means state.

Split by rights, not by hiding. Merchants read invoices; tenants manage them. Rather than conditionally hiding controls inside one page, the merchant gets a simpler top-level Invoices page — no create, no edit, no delete. Fewer permission branches to maintain, and neither audience sees a screen designed around someone else's job.

Define components by property, not by drawing them. The button resolves to 320 combinations from four properties; the input to 144 from four more. Making Link and Destructive hierarchies rather than separate components means a text-link action inherits the same sizes, icon slots and states as every other button — so it can't drift, and a new case is a selection rather than a new artboard.

Write the rule down. Patterns that live only in a Figma file get re-invented by the next feature. The number formatting guide, the colour role definitions and the notification decision rule exist as documents and tokens so the decisions outlive the handoff.

Implementation

Shipped: the dashboard rebuild, the list pattern across the accounting module, the pricing configurator, and multiple payment flows.

What worked

  • Two patterns instead of eight. The second and third table took a fraction of the time the first one did, because there was nothing left to decide.
  • Explanation beside the field. The configurator stopped being a form only its author could fill in correctly.
  • Reserving semantic colour. Once it was reserved, the dashboard and the accounting tables came out consistent without either one referencing the other. That's the test of whether a rule is real.
  • The kit held. By the time the open banking flow was built, it was assembled from existing components rather than designed from scratch — which was the whole bet.

Full notification framework, signed off and built. Three patterns, one decision rule, 31 cases. The rule matters more than the cases — it's what resolves the ones nobody has listed yet.

Events and money, fully separated. Moving status history and settlement logs out of tabs and into a side panel over the record, so the history stays on screen while you read the thing it describes.

Tokens adopted everywhere. The formatting and colour-role tokens are defined but not yet adopted across the older pages. Until they are, the rule is a document rather than a constraint.

What I'd measure. Time to issue a settlement. Edits per invoice before it's correct. Support requests asking what an invoice status means. All three are countable, and none of them were being counted.

Closing

Thanks for reading!