Coupons
Codes, immutable rule versions, caps, automatic selection, and the one-coupon-per-Order rule
A Coupon is a discount you fund: a set of terms plus one or more codes a buyer can type. It is the only way a buyer can take money off an Order, and exactly one Coupon applies per Order.
Coupons are worth the care because they interact with almost everything else: they lower the taxable base, lower the platform fee's percentage leg, hold capacity while a checkout is open, and have to be reversed exactly when someone is refunded.
Per Order
Exactly one
Never stacked, never combined
Funded by
The Organization
Ulsav's fee falls with the discount
Terms
Immutable versions
Editing publishes a new version
Discounts
The offer price
Never the list price
Discovery
Public or unlisted
Public codes can auto-apply
Caps
Uses, budget, one Account
Counted from redemptions
Three objects, not one
| Object | What it is |
|---|---|
| Coupon | The durable identity: a name, a description, and the current terms |
| CouponRuleVersion | One published set of terms, frozen the moment you save it |
| CouponCode | The string a buyer types, with its own caps and its own window |
Editing a Coupon does not overwrite anything. It publishes a new rule version, and every past redemption keeps pointing at the version it used. That is what lets you explain a two-month-old Order after three price changes.
A code is 2 to 50 characters and is stored in upper case, so fest10 and FEST10 are the same code. Codes are unique inside an Organization.
Public and unlisted
Public code
- Listed on your public coupon surface
- Eligible for automatic selection at checkout
- Can carry a campaign label for reporting
Unlisted code
- Applies only when the buyer types it
- Never auto-applies, never listed
- Can be locked to one Account for a private offer
A code also has its own on/off switch, which is separate from its dates. Switching a code off stops it immediately without deleting anything; a redemption already recorded still counts as history.
What a rule version can say
A rule version chooses one effect:
| Effect | How the discount is computed |
|---|---|
| Percentage | A rate in basis points off the eligible base, optionally capped |
| Fixed amount | A flat amount off the eligible base, never below zero |
| Fixed price | Sets the eligible unit price, so the discount is the difference |
A fixed amount applies once to the eligible total, not once per line. ₹200 off is ₹200 off, however many Passes are in the basket.
A rule can also require a minimum subtotal, a minimum quantity, or a first purchase in the Organization. It can target all eligible lines, specific Passes, the cheapest item, or the most expensive item. Per-Pass overrides let one Coupon express different rates on different Passes.
Caps count real redemptions
| Cap | What it stops |
|---|---|
| Uses per buyer | A second use by the same Account, or unlimited |
| Total redemptions | The code's overall budget of uses |
| Total discount | The sum of money the code may ever give away |
| One allowed Account | Anyone else, for a private code |
Every cap is counted from the redemption records themselves, so it cannot drift from what actually happened. A redemption is created when checkout starts, which means a live checkout holds a slot against the cap. If that checkout expires, the slot is released.
Which coupon wins
- A code the buyer typed wins if it is valid, even when a bigger public code exists.
- With no code typed, the best valid public code applies automatically by the largest saving.
- A typed code overrides the automatic choice, and the buyer can try another any time.
- An invalid typed code is an error with a reason, never a silent fall back to the automatic one.
- One Coupon per Order, enforced by the database.
Automatic selection compares the money actually saved. On a ₹900.00 basket a ₹100.00 code wins; on a ₹1,400.00 basket the 10% code wins with ₹140.00.
A worked two-line discount
The same Organization and Event as before: a Day Pass at ₹1,500 and a Workshop Add-on at ₹2,000, with a public code giving 10% off everything.
Day Pass offer price
₹1,500.00 × 1 unit
₹1,500.00
Workshop Add-on offer price
₹2,000.00 × 1 unit
₹2,000.00
Eligible base
₹3,500.00
Day Pass share of the discount
10% of ₹1,500.00
−₹150.00
Workshop share of the discount
10% of ₹2,000.00
−₹200.00
Coupon total
−₹350.00
The coupon applies to the offer price, never to the list price, so the MRP strike-through is unaffected.
The split matters. From that point on the Order carries ₹150.00 against the Day Pass line and ₹200.00 against the Workshop line, and a partial refund reverses exactly the line's share. The Coupon is never re-run to work out what a refund should be.
A discount also lowers the tax on the discounted base and the platform fee's percentage leg, because both are computed after the discount. It never lowers the fee's fixed ₹10.00 leg.
Failure paths
Every rejection carries a stable reason the checkout page can show.
| Reason | What it means |
|---|---|
invalid_code | No such code in this Organization |
inactive | The code is switched off |
not_yet_active | The code's start date has not arrived |
expired | The code's end date has passed |
not_for_account | The code is locked to another Account |
outside_scope | The code does not cover this Event or Pass |
first_purchase_only | The buyer already has a confirmed Order here |
already_used | The buyer's uses are used up |
global_limit_reached | The code's total redemptions are gone |
budget_exhausted | The code has given away its whole budget |
below_min_subtotal | The basket is under the code's minimum |
below_min_quantity | The basket has too few units |
no_eligible_items | Nothing in the basket is covered |
no_discount | The terms produce zero money off |
A typed code that fails stops the checkout with that reason. The buyer fixes the basket or removes the code; nothing is applied behind their back.
One more edge is worth knowing: a Coupon cannot make a priced Order free. If the total reaches zero, checkout refuses and asks for a smaller discount, because a paid Order always needs a real payment to confirm.