Mukul Mishra

Back to System Design Index

Financial InfrastructureSeptember 202612 min read

The Ledger That Cannot Blink

Stripe processed $1.4 trillion in payments in one year. Each request must debit and credit the ledger exactly once while networks retry, webhooks fail and clients resend. The system cannot blink. One double charge is a financial incident. Ten thousand retries are a normal Tuesday.

TL;DR: Stripe prevents double charges with idempotency keys and a double-entry ledger. At 1M RPS edge dedupe cuts the modeled origin bill 60 percent. Fix is cache-first retry handling.

Annual Volume
$1.4T
API Requests
500M+ / day
Webhook Retries
72 hr window
Invariant
Exactly once

By Mukul Kumar Mishra · Research-led architecture teardown · Updated September 7, 2026

Stripe ledger and idempotency retry illustration
Figure 1. Idempotency in one cartoon, drawn for this postmortem. One charge in. Three retries bounce off the cache. Zero double entries land on the ledger.

1. The Double Charge You Cannot Explain

A customer taps pay. The browser sends POST /v1/charges. The network stalls for four seconds. The client does the responsible thing and retries with the same intent. Somewhere two identical requests approach your system. If your code inserts a row twice then one tap becomes two charges, one refund path and a support ticket that never ends.

The retry is not an edge case. It is the expected path. Mobile networks fail mid flight, load balancers time out, webhooks get retried for seventy two hours under exponential backoff. The architecture must treat every incoming request as a potential duplicate that should return the original result without creating a second side effect. Idempotency is not a feature toggle. It is the primary correctness property for money movement.

The uncomfortable product truth: Your API looks stateless. Your ledger is not allowed to be. Every retry must return the same answer without mutating the world twice.

The network will retry exactly when you most wish it would not. That is not a bug. That is the spec.

2. Why Stripe Is the Right Autopsy

Stripe reported $1.4 trillion in total payment volume processed in 2024, up from $1 trillion in 2023 and disclosed roughly five hundred million API requests per day at peak. It publishes rate limits, idempotency guarantees, webhook delivery semantics and an explicit double entry ledger design that other fintech vendors hide.

That transparency makes Stripe a clean forensic target. The ledger is append only, entries balance to zero per transaction and reconciliation runs as a separate control plane. Idempotency keys are stored for twenty four hours and return the prior response for a duplicate request. Webhooks retry with exponential backoff for up to seventy two hours until the receiver acknowledges. Each of those guarantees reads well in docs and costs real infrastructure at platform scale.

"Idempotency keys allow you to safely retry requests without accidentally performing the same operation twice" Stripe docs note. In payments that sentence is worth more than any rate limit.

Payments infrastructure is also unforgiving about cost attribution. A double entry ledger that fans every transaction into multiple balance entries multiplies storage. A retry that reaches the origin multiplies idempotency lookups. A webhook that fires three downstream systems multiplies egress. When the unit is money, the multiplier hides in the journal.

3. Reconstructing the Machine

A client sends an API request with an idempotency key header. Edge routing validates auth, rate limits per key and looks up the idempotency store before touching the ledger. If a stored response exists and the original is complete, the edge returns the cached result without a second ledger entry. If the key is new or the original is still processing, the request proceeds to the ledger writer.

The ledger writer uses double entry bookkeeping. A $10.00 payment creates at least two balanced entries that sum to zero, plus additional entries for fees, transfers and settlement. The canonical store often mixes a strongly consistent relational core for ledger truth with Kafka backed streams for downstream processors and with Dynamo style stores for idempotency and webhook state. Webhooks enqueue per endpoint, retry independently and isolate noisy receivers so one slow URL does not block payout notifications for every merchant.

Payouts and settlement form another bounded context. Intraday authorization and capture are latency sensitive and correctness critical. Batch settlement, risk and reporting consume the same raw events but tolerate delayed consistency. The boundary between immediate ledger mutation and downstream settlement stream decides how much of the platform must be linearizable on the critical path.

Architecture tip: Separate money truth from money notification. Ledger mutation should be strongly consistent and minimal. Webhooks, risk signals and analytics should consume an immutable stream and recover independently.

4. Idempotency: The Expensive Cache That Prevents Lawsuits

Stripe stores idempotency keys for twenty four hours and checks them at the edge before expensive work. A duplicate window of twenty four hours sounds long until you count mobile retries, webhook replays and dashboard refresh loops. A consumer retry that arrives at hour twenty three should still return the original charge object, not a fresh transaction.

The store is not a best effort cache. It must be consistent under thundering herd retries, expire predictably and return the same serialized response for the same key even as load spikes. At five hundred million daily API requests, a duplicate rate of only one tenth of one percent still creates five hundred thousand idempotency lookups per day for duplicates alone. That number grows when outages trigger client retry storms.

The second cost is lock duration. An in flight request holds the idempotency key until the ledger confirms or rolls back. A slow ledger confirmation keeps the key locked longer, which raises the chance that a retry waits on the lock rather than receiving a cached response. Faster ledger commit shortens idempotency lock windows. In payments, latency is not just user experience. It is retry rate.

5. The RPS Model: How Many Payment Events Does $1.4T Make

Convert volume to events with simple arithmetic. Assume average payment size of fifty dollars. That gives twenty eight billion payments per year or roughly eighty eight payments per second average. That average hides the useful number. Payments cluster around sales peaks, especially Black Friday. Assume a peak burst multiplier of sixty times the yearly average. Modeled peak payments per second become roughly fifty three hundred.

Each payment fans out. Count one API request, one idempotency lookup, at least four ledger entries for double entry plus fees, two downstream stream events and three webhook delivery attempts counting retries. That fan out turns fifty three hundred payments per second into roughly fifty thousand system events per second at peak before read traffic like balance queries, refund checks and dashboard loads.

Add non payment traffic. Assume balance reads, search and reporting add another twenty thousand requests per second at peak. Total modeled platform event load at burst reaches seventy thousand events per second that touch origin when cache misses, well before background risk, settlement and observability pipelines.

WorkloadAssumptionModeled result
Average payments per sec$1.4T volume at $50 avg88 per sec
Peak payments per sec60x burst multiplier5.3k per sec
Fan out per paymentidempotency plus ledger plus hooks~10 events
Payment driven events at peak5.3k times 1053k events per sec
Peak platform eventsplus reads and reporting70k per sec

6. The Cost Model: Where Money Costs Money

Model the fleet for the seventy thousand peak origin events. Assume three hundred API hosts across regions with primary plus standby, at blended six hundred thousand dollars per month for compute, routing and vault. Add Dynamo style idempotency fleet for twenty four hour retention at one hundred thousand dollars per month. Add Kafka and stream processors at one hundred forty thousand per month.

Ledger storage dominates at volume. Double entry retention is durable and auditable, so assume ledger store plus balance materialization at three hundred fifty thousand dollars per month including replicas and backups. Add webhook delivery fleet plus dead letter queues at ninety thousand per month. Add observability, reconciliation and compliance workloads at one hundred ten thousand per month. Total modeled envelope lands around one point three nine million dollars per month or roughly forty six thousand dollars per day.

Cost centerModeled monthlyWhat moves it
API and vault fleet$600kregions and host class
Idempotency store$100kwindow and duplicate rate
Streaming fabric$140kretention and throughput
Ledger storage$350kentries per payment
Webhook fleet$90kretry window and fan out
Observability and recon$110kaudit load
Total$1.39M

7. The One Million RPS Thought Experiment

Normalize to one million API requests per second for one month. A month holds 2,592,000 seconds. Assume each request averages two kilobytes inbound plus two kilobytes outbound including keys, headers and response bodies. Logical data crossing the edge is roughly four million kilobytes per second or about nine point seven petabytes per month before stream replication.

In the current shape, assume origin cost of $0.0000015 per request for idempotency lookup, ledger coordination and platform overhead. Current shaped bill is 1,000,000 times 2,592,000 times $0.0000015 equals $3.89M per month.

My proposed shape reduces origin work to forty percent through idempotency served at the edge, read cache for balance queries, stream based settlement off the critical path and webhook isolation per endpoint. Assume origin rate falls to $0.0000011 per request. Core origin work becomes 400,000 times 2,592,000 times $0.0000011 equals $1.14M per month. Add four hundred thousand dollars for edge idempotency, cache, stream buffer and webhook isolation fleet. Proposed envelope is about $1.54M per month.

At 1M RPSRetry naive shapeIdempotency first shape
Origin requests1,000,000 per sec400,000 per sec after edge dedupe
Modeled rate$0.0000015 per req$0.0000011 per req
Core origin work$3.89M$1.14M
Edge and safety layerIncluded$400k
Modeled monthly total$3.89M$1.54M
Difference$2.35M per month, about 60 percent lower
The engineering conclusion: At one million requests per second retry waste matters more than server price. Serve idempotency at the edge and the ledger pays for one write instead of three.
Edge idempotency24 hr dedupe
return cached response
API fleetauth and rate limit
before ledger
Ledger writerdouble entry
balance zero
Idempotency lockhold until commit
short lock wins
Kafka streamdownstream only
ledger is truth
Webhook queueper endpoint retries
72 hr backoff
Balance materializedcached reads
off ledger
Recon control planedaily balance check
final word

This figure appears after the cost model on purpose. First price the duplicate. Then decide where to stop it.

8. How I Would Cut the Bill Without Cutting Correctness

Serve idempotency at the edge. Cache idempotency responses in a globally replicated store close to the client with a strongly consistent origin fallback. A duplicate that gets answered at the edge costs a lookup, not a ledger lock. At payments scale the edge is a financial control.

Make ledger entries compact. Represent fees, transfers and settlement as separate streams derived from the canonical payment event rather than writing every derived balance inline on the payment transaction. Keep per transaction ledger work minimal and fan out downstream

Isolate webhook retries per endpoint. Queue each webhook URL independently, apply circuit breaking per merchant and drop or back off noisy receivers without holding global delivery threads. One merchant webhook that never acks should not raise p99 for every other merchant.

Cache balance reads aggressively. Most GET /balance and dashboard queries do not need fresh ledger read after commit. Serve balance from a materialized view with a freshness token and route only payout or risk critical reads to strong consistency. The cheapest ledger read is the one you did not do. My Supabase teardown proved the same point for RLS reads and my Lovable teardown proved it for preview polling.

Commit fast to shrink the idempotency window. Keep ledger transaction time minimal by separating settlement processing from synchronous payment write path. A faster synchronous ledger trims idempotency lock duration which trims duplicate collisions which trims origin load in a loop.

Treat reconciliation as a product. Reconciliation should run continuously, compare ledger sum against downstream views and emit cost and correctness metrics per merchant and region. A ledger that cannot prove it balances is not a ledger. It is a hopeful log file.

Ops tip: Alert on duplicate rate, idempotency hit ratio and webhook queue depth before you alert on CPU. By the time API CPU moves at payments scale the retry storm already doubled origin load.

9. The Verdict

Stripe looks boring in the best way. The API is predictable, the errors are explicit and the ledger never argues about arithmetic. The postmortem is not that Stripe charges too much or scales poorly. It is that financial infrastructure pays a correctness premium on every retry that other domains dismiss as noise.

Idempotency turns retries into cached responses. Double entry turns one user action into multiple auditable entries. Webhook semantics turn one payment into many delivery attempts. Each guarantee is worth the cost because the alternative is a double charge, a mismatched balance or a silent settlement failure. The win is not removing the guarantees. It is moving them to the cheapest place where they still hold.

A ledger cannot blink. Your edge can answer before the ledger even looks.

Sources and Method

Stripe product facts plus idempotency and webhook notes come from Stripe docs and disclosures linked below. The RPS and cost model is my own scenario math, not Stripe telemetry. Use production measurements before capacity decisions.