Imagine this: an online store launches a promotion "20% off your first purchase." An hour later, the database crashes from simultaneous requests, coupons are applied multiple times, and analytics shows incorrect data. This happens when the discount system is designed hastily. We design coupon and discount services that withstand high loads and prevent abuse. A poorly designed system creates loopholes for abuse and chaos in analytics. A well-designed system is a targeted marketing tool that increases conversion by 1.5–2 times. Over 10 years of work, we've encountered dozens of such situations and know how to avoid them. Proper architecture is key to stability during peak sales when load increases tenfold.
Overview of Discount Types
Before writing code, you need to define the discount model. Main types:
- Coupon (promo code) — the user enters a code manually or a link applies it automatically. The code can be unique or reusable, tied to a discount rule.Wikipedia
- Automatic discounts — applied without a code when conditions are met: "all items in category X at 15% off on Fridays," "500 rubles off orders over 3000."
- Accumulative programs — discount depends on customer purchase history (cashback, points, loyalty levels).
- Group discounts — wholesale prices for B2B clients, employee discounts, partner terms.
| Type | Example | Mechanism |
|---|---|---|
| Coupon by code | 10% off with code WELCOME | Manual code entry |
| Automatic discount | 20% off sale items | Conditions without code |
| Accumulative | 5% points on purchases | Rules based on history |
How to Design the Data Model?
Data Model (SQL)
discount_rules ( id, name, type, -- coupon | automatic | loyalty discount_type, -- percentage | fixed_amount | free_shipping | bxgy discount_value NUMERIC, min_order_amount NUMERIC, min_qty INT, max_uses INT, -- NULL = unlimited max_uses_per_user INT, starts_at TIMESTAMPTZ, ends_at TIMESTAMPTZ, is_active BOOLEAN, stackable BOOLEAN -- can be combined with other discounts ) discount_conditions ( id, rule_id, condition_type, -- product | category | tag | user_group | first_order condition_operator, -- in | not_in | gte | lte condition_value JSONB ) coupons ( id, rule_id, code VARCHAR(32), usage_count INT DEFAULT 0, is_single_use BOOLEAN ) coupon_uses ( id, coupon_id, order_id, user_id, used_at, discount_amount NUMERIC -- amount applied at the moment of use ) Separating discount_rules and coupons allows one rule to have many codes (bulk generation for email campaigns) or one code with different restrictions. The data model consists of 4 tables and 25 columns.
Generating Coupons in Batches
For email campaigns, you need unique codes — one per recipient. Generating 100,000+ codes via INSERT with an index is 10x faster than checking EXISTS in a loop.
function generateCouponBatch(int $ruleId, int $count): array { $codes = []; while (count($codes) < $count) { $code = strtoupper(Str::random(8)); // A-Z0-9, 8 characters if (!Coupon::where('code', $code)->exists()) { $codes[] = ['rule_id' => $ruleId, 'code' => $code, 'is_single_use' => true]; } } Coupon::insert($codes); return array_column($codes, 'code'); } How to Avoid Race Conditions When Applying a Coupon?
Atomic Application
When a code is entered at checkout, you need to check: code exists and is active, start/end dates, usage limit, cart total, conditions. The check must be atomic. The solution is UPDATE ... RETURNING inside a transaction:
UPDATE coupons SET usage_count = usage_count + 1 WHERE code = :code AND usage_count < max_uses RETURNING id; -- if 0 rows — coupon already used This eliminates race conditions where two requests simultaneously apply the last available coupon. The system handles up to 100,000 requests per day at peak, with an average validation time of 50 ms.
Calculating Discount for the Cart
Discounts are calculated server-side; never trust the client. The algorithm:
- Get applied discount rules (automatic + coupon)
- For each rule, determine eligible items (considering conditions)
- Apply discounts in priority order
- If stackable=false — apply only the largest discount
- Return a breakdown: which discount applied to which item
The breakdown is important for user display and analytics.
BxGy (Buy X Get Y) — "buy 3, get the 4th free." Implemented as a separate rule type: when qty >= X, add item Y to the cart with zero price or reduce the price of the Nth unit.
What Metrics to Track in Analytics?
Without analytics, marketing flies blind. Basic set:
| Metric | SQL |
|---|---|
| Coupon uses | SELECT COUNT(*) FROM coupon_uses WHERE coupon_id = ? |
| Average discount amount | SELECT AVG(discount_amount) FROM coupon_uses WHERE ... |
| Revenue with discount | SUM(order.total) vs SUM(order.total + discount_amount) |
| Conversion with coupon vs without | Compare CR for sessions with applied_coupon and without |
For the marketer — a dashboard with filtering by period, discount type, channel. Typical indicators: conversion with coupon is 30% higher, average order value is 20% higher, return rate decreases by 10%.
Real-Time Analytics
The system collects metrics for each coupon: usage count, average order value, revenue increase. This data allows the marketer to quickly adjust campaigns. Implementing the system reduces operational costs for managing promotions by 30%.
How to Prevent Abuse?
Protection
- One coupon per order (unless stacking is allowed)
- Email verification for "new customer" discounts
- Rate limiting on the coupon application endpoint
- Alerts on sudden spikes in usage of a single coupon
Savings from abuse prevention can reach 15% of revenue. For a mid-size e-commerce store with $1M revenue, that equals $150,000 saved annually. Additionally, the system prevents up to $50,000 in direct abuse losses per year.
Marketer Dashboard
An interface for campaign management: creating rules with a visual condition builder, generating and exporting CSV batches of coupons, viewing real-time statistics, deactivating a campaign with one click.
What Does the Work Include and What Are the Timelines?
Scope of Work
- Designing data model and API
- Developing validation and atomic application
- Integrating with cart and catalog
- Analytical dashboard for marketers
- Campaign management panel
- Documentation and team training
- Post-launch support
Timelines
- Basic coupon system (promo code, percentage/amount, expiry date): 1–2 weeks
- Full system (conditions by categories/products, automatic discounts, BxGy, analytics, marketer dashboard): 3–5 weeks
- Loyalty program with points and levels: +3–4 weeks
Time to ROI: 3–4 months due to increased conversion and reduced abuse.
Our experience: 10+ years in e-commerce, 50+ projects delivered. We specialize in coupon service development and discount system analytics. Our extensive experience in coupon service development and discount system design ensures robust, scalable solutions. We guarantee transparent architecture and abuse protection. We'll assess your project in 1 day — contact us. Get a free engineer consultation.







