A promo code race condition happens when two or more requests apply the same one time code before the first one is recorded as used. The fix is to make the check and the update a single atomic step on the server, not two separate steps.
How the flaw works
PortSwigger's Web Security Academy describes race conditions as flaws that occur when sites process requests concurrently without adequate safeguards. The collision window is called the race window. Its example is an online store with a one time discount code. The application checks that you have not used the code, applies the discount, then updates the record to show it was used.
If a user who has never used the code sends two requests at almost the same time, both can pass the first check before either reaches the update. The application passes through a temporary sub state, and during that small window the discount can be claimed repeatedly. The same Academy page lists variations such as redeeming a gift card multiple times.
Where stores are exposed
- Single use coupons and referral credits
- Gift card balances and store credit
- Limited stock items in a flash sale
- Loyalty point redemption
- Refund or return actions on one order
None of these need a software bug in the usual sense. The code does what it says, in the wrong order, under concurrent load. OWASP's page on business logic vulnerabilities lists race conditions and discount abuse among related attacks, and suggests state validation on the backend and transaction auditing as controls.
Fixes that work
- Make redemption atomic. Use a database operation that checks and marks the code used in one statement, or a unique constraint that rejects a second redemption.
- Lock or serialize on the resource, such as the coupon ID and customer, when a single statement is not possible.
- Validate state on the server at the final step, not only at the cart.
- Add rate limiting and anti automation controls, as OWASP suggests for business logic abuse.
- Reconcile: compare redemptions to issued codes and alert on codes used more than once.
How to test your own store
Use a staging copy. Apply a single use code with several parallel requests and see whether more than one succeeds. Do this only against systems you own. If more than one works, move the check into the atomic step above and test again.
Quick check
Does a caching layer or CDN stop this?
No. The collision happens on the server when the same state is read and written by overlapping requests.
Is a CAPTCHA enough?
It may slow automation, but it does not fix the ordering flaw. Make the redemption atomic.
Sources
Safeguard's pull request gates and code scanning can help review checkout changes, and the free scan is at gold.safeguard.sh/scan.
Last reviewed: 19 October 2026.