Promo codes need redemption limits (max_uses / single-use) #32

Open
opened 2026-06-20 08:58:59 +00:00 by padreug · 0 comments
Owner

Surfaced while reviewing the free-ticket work (#31).

PromoCode is currently just { code, discount_percent, active }no usage cap, no single-use. Every promo code is effectively unlimited-redemption.

This becomes acute with 100%-off promos: #31 routes any final-charge-0 purchase (incl. a 100%-off promo on an otherwise-paid event) through the free-ticket path — no invoice, ticket issued already-paid. So a leaked 100%-off code = unlimited free comps on a paying event, minted through the no-friction free path. (Even a leaked 90%-off code is unlimited discounted sales today — the 100% case is just the most damaging.)

Shape

  • Add max_uses: int | None (and/or single_use: bool) to PromoCode.
  • Track redemption count per code; reject once exhausted at api_ticket_create.
  • Decide interaction with multi-ticket quantity (does quantity=3 consume 3 redemptions?) and with forfeit/release (#28) — does releasing a comped seat refund the redemption?
  • #29 (abuse / identity limits on free claims) — the per-identity cap must cover promo-derived-free, not just free events. These two together close the "uncapped free issuance" gap.
  • #31 (free-ticket issuance) makes 100%-off promos functional, which is what exposes this.
Surfaced while reviewing the free-ticket work (#31). `PromoCode` is currently just `{ code, discount_percent, active }` — **no usage cap, no single-use**. Every promo code is effectively unlimited-redemption. This becomes acute with **100%-off promos**: #31 routes any final-charge-0 purchase (incl. a 100%-off promo on an otherwise-paid event) through the free-ticket path — no invoice, ticket issued already-paid. So a **leaked 100%-off code = unlimited free comps on a paying event**, minted through the no-friction free path. (Even a leaked 90%-off code is unlimited discounted sales today — the 100% case is just the most damaging.) ## Shape - Add `max_uses: int | None` (and/or `single_use: bool`) to `PromoCode`. - Track redemption count per code; reject once exhausted at `api_ticket_create`. - Decide interaction with multi-ticket quantity (does `quantity=3` consume 3 redemptions?) and with forfeit/release (#28) — does releasing a comped seat refund the redemption? ## Related - #29 (abuse / identity limits on free claims) — the per-identity cap must cover **promo-derived-free**, not just free events. These two together close the "uncapped free issuance" gap. - #31 (free-ticket issuance) makes 100%-off promos functional, which is what exposes this.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/events#32
No description provided.