# Preventing coupon QR code fraud

> Assume any static coupon QR code will be screenshotted and shared: a code is copied in one tap and forwarded to thousands. Either cap the exposure so spreading is acceptable, or issue single-use server-validated tokens that burn on redemption. Expiry windows and location checks close the remaining gaps.

Source: https://useqr.app/docs/ecommerce/preventing-coupon-qr-fraud · Last reviewed 2026-08-21 · UseQR is free forever, no signup.

---

## The economics of a screenshot

Start from the attacker's cost, because it explains everything: copying a QR coupon is
**one screenshot and one forward**. A "20% off" code printed on a flyer reaches a coupon-
sharing group chat, a deals forum or a voucher site within hours of someone deciding it
should, and from there redemption is limited only by your till process. This is not
hypothetical malice: most spreading is customers being helpful to each other. The
design question is never "will it spread" but "what happens when it does".

Two coherent answers exist. Everything else is wishful printing.

## Answer one: make spreading acceptable

A **static code**: same payload on every flyer, generated once with the
[coupon tool](/coupon-qr-code), is fine when the maths survives worst-case spread:

- The discount is one you would happily give any customer ("10% off your first visit").
- Redemption requires a purchase whose margin absorbs the discount.
- The till applies it once per transaction and staff can decline obvious abuse.

Here the "fraud" is just distribution you did not pay for, and plenty of businesses
treat a leaked coupon as accidental marketing. The mistake is using a static code for an
offer that *cannot* absorb spread (a free product, a high-value voucher, a
new-customer-only discount), and being surprised.

## Answer two: single-use tokens

For anything with real value, the coupon becomes a **server-validated token**:

```
example.com/c/8h2n4v7q   →  server checks: issued? unredeemed? in window?  →  burn
```

Each customer receives a unique code: issued on screen, by email, into a
[wallet pass](/docs/ecommerce/qr-code-for-loyalty-programs), and redemption marks the
token spent atomically, so the race of two tills redeeming the same screenshot resolves
to one winner. Generate unique codes at scale with [the bulk generator](/bulk) or
`POST /api/v1/qr/batch` (**up to 1,000 per request**). Tokens must be **random and
sparse** (8+ characters from a large alphabet), never sequential, or attackers simply
enumerate your voucher space. The same opaque-serial logic underpins
[product authentication](/docs/ecommerce/qr-code-for-product-authentication).

Layer the cheap additional checks where the offer justifies them:

- **Expiry windows**, short validity caps how long a leaked token circulates.
- **Geo or till checks**: redeemable only at the issuing branch, or only on a POS
  session, defeating remote redemption of shared screenshots.
- **Identity binding**, token tied to the loyalty account that received it. Strongest,
  most friction.
- **Signed payloads** stop *forged* coupons offline: see
  [signed QR codes](/docs/security/signed-qr-codes-and-authenticity), though not shared
  genuine ones.

## The fraud-versus-friction table

Every control taxes the honest customer. Choose deliberately:

| Control | Fraud stopped | Friction added | Use when |
|---|---|---|---|
| Static code, capped offer | None, by design | None | Offer survives infinite spread |
| Expiry window | Long-tail circulation | Low | Always: it is nearly free |
| Single-use token | Screenshot sharing | Low (unique delivery needed) | Any real-value offer |
| Branch/till binding | Remote redemption | Medium | Local offers, franchises |
| Account binding | Nearly all sharing | High, requires sign-in | High-value, member offers |

The classic failure is mismatching column one and column four: account-binding a 10%
coupon (killing redemption for nothing), or static-coding a free-product voucher
(a giveaway to the internet). Match the control to the value at risk, and
[verify redemption flows](/validate) end-to-end (including the double-redemption race),
before printing anything.

## FAQ

### How do people abuse coupon QR codes?
Mostly by sharing screenshots, a static code forwarded to group chats or deal sites redeems unlimited times unless the server or till stops it. Sequential voucher codes also get enumerated. Deliberate forgery is rarer than simple over-sharing.

### What is a single-use QR coupon?
A unique random token per customer, checked and atomically marked spent by your server at redemption. A shared screenshot then fails for everyone after the first use. Tokens must be random and sparse so valid ones cannot be guessed.

### Can I just use one static discount code for a campaign?
Yes, if the offer survives worst-case spread, a modest percentage requiring a purchase, applied once per transaction. Never use a static code for free products or high-value vouchers, because it will circulate.

### Do expiry dates actually reduce coupon fraud?
Yes, cheaply. A short redemption window caps how long a leaked code stays valuable and shrinks the audience a shared screenshot can reach in time. Combine expiry with single-use tokens for offers with real value.

## Try it

- https://useqr.app/coupon
- https://useqr.app/url
- https://useqr.app/validate
