Idempotent Payment Endpoints: Lessons from Production
A customer clicks "Pay." The request times out. Their browser retries. Your server processes the same charge twice. The customer sees two deductions. Your support team gets a ticket. Your reconciliation report shows a discrepancy. All because the endpoint lacked idempotency.
I have debugged this scenario in production payment systems. The fix is not disabling retries. Retries are a fact of distributed systems: network timeouts, load balancer failovers, client-side connection drops all trigger them. The fix is designing endpoints that handle duplicate requests without creating duplicate side effects. That mechanism is the idempotency key.
What Idempotency Means in Payments
An operation is idempotent if executing it once produces the same result as executing it multiple times. In payment APIs, idempotency works by attaching a unique key to each request. The server remembers: "I processed a request with this key, here is the original response."
The Implementation Pattern
Three parts of this implementation matter: the cache check (look up the idempotency key before processing), the lock (prevent concurrent requests with the same key), and the TTL (keys expire after 24-48 hours).
Storage Options
Redis is ideal for high-throughput services: SET idempotency:{key} {response} EX 172800. A database table with a request_hash column works for lower traffic. The request hash catches the bug of a client reusing an idempotency key with a different request body.
When to Require Idempotency Keys
Any endpoint that creates a side effect involving money gets idempotency keys: charge, refund, transfer, and subscription creation endpoints.
Architecture is about trade-offs, not silver bullets. The idempotency key pattern trades a small amount of infrastructure complexity for a strong guarantee that retries do not become duplicate transactions.
$ /related
Building a Reconciliation Engine for Currency Mismatch
Multi-currency transactions break naive reconciliation. I built an engine with threshold-based matching that handles FX rate gaps and rounding without drowning in false positives.
Why I Use Transactional Outbox in Fintech APIs
Payment events that vanish between your database and message broker cost real money. The outbox pattern closes that gap without distributed transactions.
Why Your REST API Needs a Proper Error Taxonomy
Standard HTTP status codes are not enough. A structured error taxonomy with error codes, severity levels, and machine-readable payloads transforms debugging from guesswork into systematic root cause analysis.