Aashish Solanki · May 29, 2026 · 9 min read

Building a Scalable Payment Gateway Architecture in 2026

What a production payment gateway looks like in 2026 — the orchestration layer, ledger, retries, and the boring decisions that decide whether your platform scales.

Layered payment gateway architecture with API surface, orchestration, processor adapters, ledger, and reconciliation worker

I have lost more nights of sleep to payment systems than I would like to admit. Most of them on the same kind of problem: a request that times out, a webhook that fires twice, a settlement total that drifts by 7 cents over a quarter. Each of those is a symptom of the same thing — a payment gateway architecture that works on the happy path and falls apart everywhere else.

So this is what we actually ship at Dashhold when a team asks us to build a payment platform. Not a reference architecture from a 2018 blog post. Not a vendor’s marketing diagram. The shape of a production payment gateway in 2026, with the trade-offs I have made in real engagements over the last four years.

What a payment gateway actually has to do

A payment gateway is not “the thing that talks to Stripe.” That is one of its jobs. The gateway is the system that owns the lifecycle of money moving through your platform — every charge, every payout, every refund, every dispute, every reconciliation, every webhook re-delivery, every retry. The processor (Stripe, Adyen, Marqeta, Worldpay) handles the card-network round-trip. Everything else is yours.

The shape I keep coming back to has five layers. Top down: an API surface your product code talks to, an orchestration layer that picks a processor and handles retries, a ledger service that records every value movement, a processor-adapter layer that hides each vendor’s API, and a reconciliation worker that compares your books against what the processors say settled.

If you skip any one of those layers, you end up rebuilding it under pressure six months later. I have watched this play out at three different fintechs.

The API surface

The API your product talks to should pretend processors do not exist. Code in the checkout flow, in the wallet, in the payout dashboard — none of it should know whether you are on Stripe or Adyen on a given route. The gateway exposes one shape:

interface ChargeRequest {
  idempotencyKey: string;
  amountMinor: number;       // cents, paise, kobo — never floats
  currency: string;          // ISO 4217
  customerId: string;
  description: string;
  countryCode: string;       // drives processor routing
  metadata: Record<string, string>;
}

Every write request carries an idempotency key. Without it, every retry is a coin flip on whether your customer gets charged twice. With it, retries are free.

A small but important rule: the idempotency check runs before any side effect. Not after the database write, not after the processor call. First. I have inherited code where it was checked after, and the trail of duplicate charges took two weeks to clean up.

The orchestration layer

Orchestration is where almost all the production complexity lives. It takes a ChargeRequest, picks a processor, executes the charge, handles the response, retries on transient failures, falls over to a backup processor if the primary is in an outage, and writes the result to the ledger.

The mental model is a state machine. A charge is in one of pending, processor_attempt, succeeded, failed, or requires_action. Each transition writes an event to an append-only ledger so we can replay the history if anything ever drifts.

In 2024 we standardized on Temporal for this layer. The reason is durability. A workflow that has been running for six hours waiting on a customer to complete 3D Secure does not care if the worker process crashes — Temporal picks it up where it left off. Before Temporal we ran this on Kafka with hand-rolled retry logic, and it worked, but every weird edge case (delayed webhooks, processor returning a strange status, customer disputes mid-flight) was a custom handler. With Temporal those become workflow continuations.

Processor adapters: the part that pays for itself

Every processor has its own SDK, its own webhook payload shape, its own idempotency semantics, its own quirks around partial captures and refunds. If you pepper Stripe SDK calls across the codebase, swapping or adding a processor becomes a multi-quarter project. I have lived this. Once.

The pattern that pays for itself: one interface, one adapter per processor.

interface ProcessorAdapter {
  charge(req: NormalizedRequest): Promise<NormalizedResult>;
  refund(processorRef: string, amount: number): Promise<NormalizedResult>;
  parseWebhook(body: string, sig: string): NormalizedEvent;
}

You implement StripeAdapter, AdyenAdapter, MarqetaAdapter. The orchestration layer talks to the interface, never the SDK. Adding a new processor becomes a one-week task — write the adapter, update the routing rules, ship.

This is also what enables multi-processor routing. Most platforms start single-processor and graduate around $30M GMV/year, when the routing payoff exceeds the engineering cost. Routing tends to be country-based first (Stripe in the US, Adyen in Europe), then BIN-based for cost optimization, then risk-based for high-value transactions.

For a deeper side-by-side on the most common processor decision platforms make, see our Stripe vs Adyen for Platforms comparison.

The ledger service

I have written a longer field guide on designing fintech ledgers, but here is the short version: every value movement on your platform is two paired entries on a double-entry ledger, the entries are append-only, and the ledger is the source of truth. Not Stripe. Not your wallet table. The ledger.

The reason this matters specifically for a gateway: every charge writes a pair of entries (clearing account → customer wallet), every payout writes another pair (wallet → bank rail), every fee writes another pair, every refund writes a compensating pair. By the end of the day, the sum of all entries against processor-clearing-stripe should equal what Stripe says they settled to your bank.

When those two numbers disagree, you have a reconciliation discrepancy. The ledger lets you find it in minutes instead of weeks, because every entry has a transaction id, an idempotency key, an occurred-at timestamp, and a reason code.

Skipping the ledger is the most common mistake I see. People treat Stripe’s dashboard as the source of truth. It works until your first audit, your first chargeback dispute, or your first multi-processor migration. Then it becomes a nightmare.

Reconciliation, not hope

Reconciliation is the part most teams discover too late. It is also the cheapest insurance you can buy against silent drift.

The pattern: a daily job pulls settlement files from every processor, compares the per-day total against the sum of entries in your processor-clearing-* accounts for the same day, and opens a ticket if they disagree. The job runs in under a minute even on platforms processing $100M+/year, because it is one aggregation query.

Important detail: handle settlement timing. Stripe might process a charge today and settle it tomorrow. Your ledger has the entries today; Stripe’s settlement file has them tomorrow. Get the date math wrong and you create false positives every morning. The fix is to compare on processor-reported settlement date, not your ledger’s occurred_at.

Webhooks: at-least-once, every time

Webhooks are how processors tell you what happened asynchronously — a 3DS challenge completed, a payout cleared, a chargeback was filed. Every processor delivers webhooks at-least-once. Sometimes more.

If your webhook handler is not idempotent, you will double-write the ledger the first time a processor retries. The pattern is the same as request idempotency: hash the webhook event id, store it for 48 hours, return the cached result on a duplicate.

A rule I have to repeat on every engagement: verify webhook signatures before doing anything else. Not after the deserialize, not after the lookup. Before. A spoofed webhook with no signature check can write fake entries to your ledger, and the cleanup is awful.

What 2026 is actually different about

A few things have genuinely changed in the last two years that affect how I design these systems now.

Real-time payment rails are mainstream. RTP in the US, FedNow, instant SEPA in Europe, UPI in India. If you are designing a payouts product, you have to assume settlement happens in seconds, not days. That changes how you design held-balance logic, dispute windows, and operator surfaces.

Card-on-file tokenization is required, not optional. Network tokenization (VTS, MDES) is now the default in many regions. Storing PANs at all is expensive and risky.

AI-driven fraud is real on both sides. Fraudsters have better tooling. Defenders do too. We are integrating Sift, Unit21, and increasingly custom ML models into platforms in ways that were research projects in 2023.

Regulators care about your audit trail more than ever. Especially in the EU after PSD3 and in the UK after the FCA’s renewed focus on operational resilience. The append-only ledger pattern, the audit-grade reason codes, the structured AML screening — these are not “nice to have” anymore.

What I would tell you to do first

If you are designing a payment gateway right now, the order I would build in is:

  1. Ledger first. Get the double-entry ledger primitives in place before you wire a single processor. Postgres is enough.
  2. One processor adapter, behind an interface. Pick the processor that fits your geography. Build the adapter as if you will swap it next year.
  3. Idempotency middleware on every write route. Single layer of code, applied to every endpoint.
  4. Orchestration layer with retries and a state machine. Temporal if you can, hand-rolled with Kafka if you cannot.
  5. Reconciliation job from day one. It will catch bugs nothing else catches.
  6. Webhook handlers that verify signatures and run idempotently. Same shape as the request handlers.

Do not skip steps. I have seen every order of operations, and the teams that ship the ledger first ship the rest faster, because they always know where the money is.

Frequently asked questions

How long does a production-grade payment gateway take to build?

A focused MVP — one processor, one currency, basic ledger, basic webhooks — takes 8 to 12 weeks with a senior engineering pod. A production-grade gateway with multi-processor routing, full reconciliation, dispute handling, and operator tooling typically takes 4 to 6 months.

Should I build the gateway myself or use a hosted service?

If you only need to charge cards, use Stripe directly. If you are running a marketplace, a B2B platform, or anything where money flows between multiple parties, you will end up with gateway-shaped logic regardless. The question is not whether you build it, it is whether you build it deliberately.

How do you handle PCI scope?

Use the processor’s hosted payment elements (Stripe Elements, Adyen Drop-in) so card data never touches your servers. Network tokenization for stored cards. The platform’s PCI scope ends up at SAQ A or SAQ A-EP, not the full PCI DSS Level 1.

Closing thought

The payment gateway you build in 2026 is the system you will live with for five years. The decisions you make in the first month — ledger structure, idempotency boundaries, processor abstraction — are the ones that decide whether scaling to 10x volume is a project or a Tuesday.

If you want this thinking applied to your platform, our fintech development team has built payment gateways for marketplaces, neobanks, and B2B SaaS platforms across three continents. A 30-minute strategy call is the fastest way to figure out which trade-offs apply to your situation.

Written by

Aashish Solanki

Founder & Principal Engineer

Aashish is the founder of Dashhold. Four years across payments, ledgers, and CRM platforms before starting the studio. Led platform engineering at fintechs through Series B and C, with hands-on experience scaling production systems through PCI DSS and SOC 2 audits.

Let's build it together

Want this thinking applied to your roadmap?

The articles are the public version. The custom analysis happens on the strategy call.