Discount codes that don't leak
A discount code is a promise about money made to a stranger, distributed by email, and then screenshotted onto a deals forum by somebody you have never met. The ways it escapes are well understood, and every one of them has cost somebody real revenue. Here is what the controls actually do and which one you need.
Two different limits, and they are not interchangeable
There are two caps, and the difference between them is the entire subject.
- A total usage cap answers "how many times may this code be redeemed at all, by anybody". Set it to 100 and the 101st redemption fails, whoever attempts it.
- A per-buyer cap answers "how many times by the same person".
Only having the first is how a code intended as one-per-customer gets redeemed a hundred times by one person with a hundred throwaway email addresses. Your total cap was respected perfectly. Your intention was not. If your code is meant as a personal offer — a member rate, an apology, a partner discount — the per-buyer cap is the control you actually need, and the total cap is only a backstop against the whole thing being posted publicly.
Expiry is inclusive, on purpose
A code that expires on 22 July works for the whole of 22 July, in your event's own timezone. This sounds too obvious to state and it is a classic off-by-one: a date-only expiry, compared naively against the current time, cuts the code off at midnight at the start of the 22nd and silently kills your final day.
That bug is invisible in testing, because whoever tests it does so on a day that is not the last one. It shows up as a quiet drop in conversion on the day you were counting on most, and as a handful of emails from people saying a code you promised does not work.
Currency has to match
A fixed-amount code carries a currency. A "€10 off" code cannot discount a yen-priced event by 10, because that is not a translation of the offer — it is a hundredfold error in the wrong direction, and it is the kind that only becomes visible on the bank statement.
So: fixed-amount codes apply only to events priced in their own currency. Percentage codes travel freely, because a percentage means the same thing everywhere. If you sell in more than one currency and want one campaign across all of them, use a percentage code — it is the only kind that is safely portable.
Case does not matter to the shopper
Codes are stored and compared in one canonical form, so somebody typing save20 gets the same result as SAVE20, and a code copied out of a PDF with a stray capital still works.
This is not a nicety. Comparing codes case-sensitively against what a shopper typed is a real bug that has shipped on real platforms, and its symptom is the worst possible one: a valid code reported as invalid, at the moment of payment, to a customer who is now certain you are trying to cheat them. They do not file a bug report. They close the tab.
What a refused code says
When a code is refused, the shopper is told it is not valid — not why. That is deliberate. "You have already used this code" confirms that the limit is tracked per identity, which is the first thing anybody trying to game it needs to learn. "This code has run out" tells a forum that the campaign is dead and stops the traffic you were paying for.
The trade-off is honest: a legitimate customer with a genuine problem gets a slightly less helpful message, and has to ask you. That is a support email you can answer. The alternative is a public, self-service oracle for probing your promotion rules.
Choosing your settings
A short guide to which combination fits which campaign:
- Public campaign (posted on social, printed on a flyer): percentage discount, generous per-buyer cap, firm total cap. The total cap is your budget.
- Personal offer (a member rate, a goodwill gesture): per-buyer cap of one, and an expiry date. Assume it will be shared, and make sharing pointless rather than trying to prevent it.
- Partner or press allocation: separate codes per partner, not one shared code. You then know which partner delivered, which one code can never tell you.
- Early-bird pricing: do not use a code at all. Use a time-based pricing rule — it needs no distribution, cannot leak, and shows the right price on the event page instead of hiding it behind a box the buyer has to know about.
Last thing: discounts apply to bundle prices too. The bundle price is worked out first and the discount comes off that, rather than off the sum of the individual tickets — a different, larger, and wrong number.