When network jitter causes a mobile client to retry a $250 payment, a standard API charges the customer twice. Idempotency keys in Redis protect against double billing by caching the original response and returning X-Idempotent-Replay: true.
LIVE · Replay Cache 5m · Idempotency Keys
Ledger Balance: $1000.00
Idempotency Gate
Header: Idempotency-Key: pay_intent_m59w1ny
// Bank Transaction Ledger
Click "Pay $250.00" or "Simulate Rapid Double-Click" to inspect ledger updates.
On the live site, requests hit real Spring Boot endpoints evaluating Idempotency-Key headers backed by Redis with atomic expiration.
Idempotent Request Lifecycle
First request generates a UUID idempotency key. Subsequent retries return the cached outcome with X-Idempotent-Replay: true without re-charging the credit card.
Browser Client
Double click 'Pay'
Idempotency-Key: uuid
Idempotency Gate
Redis check key
If new, charge card
Payment Gateway
Stripe / Banking rail
If duplicate, replay response
Customer Balance
Account ledger
Double Click without Key: $250 x 2 = $500 charged! | With Idempotency Key: Exactly $250 charged once.
Idempotency Key Handler
public PaymentChargeResult processPayment(PaymentChargeRequest req) {
String redisKey = "idempotency:" + req.idempotencyKey();
// 1. Check if key was already processed
String cached = redisTemplate.opsForValue().get(redisKey);
if (cached != null) {
PaymentChargeResult prev = parse(cached);
return prev.withReplayed(true); // Return cached response, 0 double-charge
}
// 2. Fresh transaction: charge external payment gateway
PaymentChargeResult result = stripeClient.charge(req.amountCents());
// 3. Store result with 5-minute TTL
redisTemplate.opsForValue().set(redisKey, toJson(result), Duration.ofMinutes(5));
return result;
}
In-Flight Locking (SETNX PENDING) vs Post-Processing Cache
If two requests with the same key arrive simultaneously, setting a PENDING lock prevents both threads from charging the card while the first is still awaiting the bank gateway.
Payload Fingerprinting vs Key Only
Hashing the request body alongside the key guarantees a client cannot reuse an old idempotency key with different parameters (e.g., changing amount from $10 to $1,000).
Key Retention Window
A 24-hour TTL in production covers virtually all network retries, webhook re-deliveries, and accidental user double-submits without growing Redis memory unboundedly.