Shipped

A Coupon That Can't Be Passed Around

TL;DR

GoCushy coupons are percentage or fixed amount, never both. Fixed-amount codes carry their own currency and are refused on an offer priced in another one. Caps and expiries are enforced on the server at quote time and again at payment-intent time, the redemption count is derived from paid orders rather than stored in a counter, and the cap also reserves pending checkouts from the last 30 minutes so a one-use code cannot be redeemed five times in the same second.

A discount code is a small piece of software that hands out your money

That is the whole reason this took longer than it looks. Creating a coupon is trivial. Deciding what stops it is the actual product. Every coupon you publish is a tiny program running in public, and the only interesting questions are the ones about its limits: who can use it, on what, in which currency, how many times, and until when. If any of those answers live in the browser, they are not answers.

So here is exactly what shipped, and where the sharp edge was.

One kind of discount, and one currency

A GoCushy coupon is either a percentage off or a fixed amount off. Never both. There is no stacking of the two on a single code, because "20% and $10" is a support ticket waiting to happen and nobody can predict the order of operations from the outside.

Fixed-amount coupons carry their own currency, and a fixed-amount coupon is refused outright on an offer priced in a different currency. This is the failure I most wanted to make impossible. A "$100 off" code quietly becoming "€100 off" is not a rounding problem, it is a real gap between what you meant to give away and what you gave away, repeated on every sale until someone notices in a payout report weeks later. Rather than convert behind your back, GoCushy declines and says why.

Scope, caps, and an expiry that isn't theatre

A coupon can be scoped to one specific offer or left open to apply across all of them. It can carry a redemption cap. It can carry an expiry.

When you set an expiry, the checkout shows a live countdown. When you do not set one, the checkout shows nothing at all. There is no evergreen timer that resets when the page reloads, because a fake countdown teaches buyers that your deadlines are decoration, and then your real deadlines stop working too. If the clock is on screen, the coupon genuinely dies at the end of it.

The count is derived, not stored

The redemption count is calculated from paid orders. There is no counter column being incremented somewhere and hoped over. Counters drift: a webhook retries, two workers process the same event, a refund lands, and the number on your dashboard slowly stops describing reality. Deriving the count from the orders themselves means a retry is idempotent by construction, not by discipline.

The race that made this interesting

Now the part I actually want to talk about, because it is the part a feature list would skip.

Imagine a coupon capped at one use. Five people have it, and they all hit checkout inside the same few seconds. Each of their sessions asks the same question: has this coupon been fully redeemed? At that instant, no paid order exists yet, because nobody has finished paying. So all five pass the check. All five get a payment intent carrying the discount. All five pay. You intended to give away one discount and you gave away five, and every one of those charges is legitimate as far as the processor is concerned.

The fix is a reservation window. The cap counts paid orders plus pending checkouts created in the last 30 minutes. Starting a checkout with a coupon provisionally consumes a slot, so the second buyer in that stampede is refused while the first is still typing their card number. The window expires on its own, which matters just as much: abandoned checkouts release their slot after 30 minutes instead of locking a live coupon forever because someone opened a tab and went to lunch.

That is not a hypothetical race. It is the normal shape of a launch, where an affiliate posts the code and traffic arrives in a spike rather than a trickle.

The server decides, twice

Every rule above is enforced server-side at two separate moments: when the checkout quotes a price, and again when the payment intent is created. The browser never decides whether a discount applies or what it is worth. It only displays what it was told. A discount is also clamped so the final charge can never fall below the processor's minimum, because a card cannot be charged nothing and a coupon should not be able to produce an unchargeable order.

When a coupon is refused, the reason is specific rather than a shrug:

"Invalid code" is the worst message in commerce. It makes the buyer suspect themselves, then email you, and you cannot answer without going and reading records. A specific refusal ends the conversation at the checkout.

Where this sits

Money moves on your own Stripe, PayPal or Airwallex account. GoCushy is never the merchant of record, so a discount is a rule applied to your charge on your rail, not an adjustment made inside somebody else's ledger. The details of the flow are in the docs, and the comparison I get asked about most is on the GoCushy vs ThriveCart page.

Lifetime access is $295 one-time, with 0% platform fees on that plan. If you want to know why I build in this order, that is on the founder page.