Designing a Coupon & Promo Code Abuse Detection System

Designing a Coupon & Promo Code Abuse Detection System

Designing a Coupon & Promo Code Abuse Detection System

How to build a real-time fraud detection platform that stops users from farming one-time discounts through multi-accounting, device spoofing, and referral abuse — without punishing your honest customers — using layered blocklists, deterministic rules, streaming velocity checks, graph-based entity resolution, and machine learning working together.

01

Introduction and History

Why “just give everyone a code” turned into one of the hardest fraud problems in e-commerce.

Imagine a small bakery that puts up a sign: “First-time customers get one free cupcake.” The owner expects new people to walk in, feel welcomed, and hopefully become regulars. But then a group of teenagers realises they can walk in, claim to be “first-time customers” every day, wearing a different hat each time, and the owner has no way to tell they are the same three kids. Within a month, the bakery has given away five hundred cupcakes to fifteen actual new customers and everyone else was the same group of teenagers in different hats.

This is, almost exactly, what happens on the internet at a much larger scale. A “promo code” or “coupon code” is a string of characters — like WELCOME50 or FREESHIP — that a business gives out so a specific action (like paying for something) becomes cheaper or free. The idea is simple and old: printed paper coupons have existed since Coca-Cola handed out the first known coupon in 1887, offering a free glass of Coke. What changed with the internet is not the concept of a coupon — it is the cost of creating a fake identity to redeem it again. Cutting out a hundred paper coupons from newspapers requires buying a hundred newspapers. Creating a hundred fake email accounts to redeem a hundred “first order” discounts costs almost nothing and can be scripted in an afternoon.

As e-commerce, food delivery, and ride-sharing companies grew in the 2010s, promotional codes became one of the cheapest ways to acquire new users — a referral code that gives “$10 off your first ride” is far cheaper than a television ad, and it grows virally because existing users share it to earn a reward too. But the same mechanic that makes referral codes cheap and viral also makes them a magnet for abuse. Companies like Uber, DoorDash, and PayPal all report, in various public disclosures and engineering blog posts, having lost significant sums to organised “coupon farming” — groups of people (sometimes automated bots, sometimes low-cost human labour in click-farms) who create hundreds or thousands of fake accounts purely to harvest one-time signup bonuses.

This tutorial walks through, from first principles, how to design a Coupon & Promo Code Abuse Detection System — a piece of backend infrastructure whose job is to sit between “a coupon code exists” and “a coupon code gets applied,” and to make an intelligent, fast, and fair decision about whether this particular redemption attempt looks like a genuine new customer or like the fifteenth account created by the same laptop this week.

1.1 A Short History of Promo Abuse and Defences

1

1887 — The First Modern Coupon

Coca-Cola’s paper coupon for a free glass of Coke. Redemption was limited naturally by the cost and effort of printing and physically distributing paper, which put an organic economic ceiling on how much abuse was possible in any given campaign.

2

1990s — Digital Coupon Codes Arrive

Early e-commerce sites introduce alphanumeric codes at checkout. Abuse is limited because internet access itself is scarce and IP addresses are relatively unique per household, so “one redemption per IP” was still a reasonable first-line defence.

3

2010–2014 — The Referral-Growth Boom

Uber, Dropbox, PayPal, and others popularise “give $10, get $10” viral loops. Growth hacking becomes a discipline. Fraud rings notice the profit opportunity almost immediately and start scripting fake account creation at scale.

4

2015–2018 — Click Farms and Device Emulators

Cheap Android emulators (like Genymotion or custom-rooted farms) let a single operator run hundreds of “unique” virtual devices, defeating simple device-ID checks and pushing the industry toward richer behavioural and network signals.

5

2019–2022 — ML-Based Risk Scoring Becomes Standard

Companies move from static rules (“max 1 account per email”) to real-time machine learning risk engines that combine dozens of behavioural and device signals into a single graded score rather than a binary allow-or-deny outcome.

6

2023–Present — Residential Proxies & GenAI-Assisted Fraud

Fraud rings rent real residential IP addresses and use AI to generate more human-like signup behaviour, pushing defenders toward graph-based and consortium-data approaches that look at relationships between accounts, not just any single account’s attributes.

Analogy

Think of a nightclub bouncer checking IDs at the door for a “ladies get in free before 10pm” promotion. A simple bouncer just glances at a name. A smart bouncer notices the same face walk back through a side door in a different wig ten times a night. Our system is that smart bouncer, applied to millions of “faces” (accounts, devices, and behaviours) per day.

02

Problem and Motivation

What actually goes wrong when coupon abuse is left unchecked, and why naive fixes fail.

Let’s define the problem precisely. A business creates a coupon, for example NEWUSER20, meant to be redeemed once per unique real person. The business’s real intention, in plain English, is: “give this discount to genuinely new customers, one time each, to encourage them to try our product.” The system only sees requests like “account ID 88213 wants to redeem coupon NEWUSER20 on order #4471.” It has no direct way to know if account 88213 is a real, distinct human who has never used this offer before, or whether it is the 400th throwaway account created by the same person or bot farm in the last hour.

2.1 The Core Tension

Any coupon abuse system lives inside a tug-of-war between two costs:

Cost of Being Too Loose

  • Direct financial loss: giving away discounts, free items, or cash-back to fraudsters instead of real customers.
  • Distorted growth metrics: marketing teams believe a campaign is acquiring 10,000 new users when 6,000 are fake accounts, leading to bad future budget decisions.
  • Secondary fraud: farmed accounts are often resold or used later for other abuse (fake reviews, chargebacks, inventory hoarding during flash sales).

Cost of Being Too Strict

  • False positives: real new customers get blocked or flagged, creating a terrible first impression exactly when the business is trying to win them over.
  • Support burden: legitimate users flooding customer support with “why was my discount rejected” tickets, eating into margins in a different, less visible way.
  • Reputational damage: aggressive fraud rules disproportionately affecting users who share a household Wi-Fi, a university network, or a low-cost Android phone model common in fraud rings but also common among honest budget-conscious shoppers.

A naive engineer’s first instinct is usually: “just limit one redemption per email address” or “one redemption per registered phone number.” Both fail quickly in practice:

  • Email is nearly free to generate. Gmail allows “dot tricks” (j.doe@gmail.com and jdoe@gmail.com deliver to the same inbox but look different to a naive string check) and “plus addressing” (jdoe+1@gmail.com). Disposable email providers hand out fresh addresses instantly and for free.
  • Phone numbers can be rented. SMS-verification-bypass services rent out real phone numbers for a few cents per OTP (one-time password), specifically marketed toward defeating “one account per phone” rules.
  • IP addresses are shared or rotated. Many honest users share an IP (an office, a university dorm, a mobile carrier’s NAT gateway), so blocking by IP alone causes false positives, while fraud rings simply rotate through residential proxy networks that rent out thousands of real home IP addresses.
“Coupon abuse is not a single problem you solve once — it is an ongoing arms race between your risk engine and an adversary who is economically motivated and constantly adapting.”

2.2 What “Good” Looks Like

<1%
false positive rate on genuine new users
>90%
catch rate on known abuse patterns
<100ms
added latency at checkout

These targets are aspirational rather than universal, but they capture the shape of the trade-off: a system that hits any one of them alone (say, catching 100% of abuse by blocking everyone) is worthless; a system that hits all three simultaneously, plus offline batch re-scoring for slower-forming abuse patterns, is what a mature production fraud team is aiming at.

i
What an Interviewer May Ask

“Why can’t you just rate-limit by IP address or block disposable emails outright?” Be ready to explain the false-positive costs above, and pivot to a layered, risk-scored approach rather than a single hard rule — this signals you understand fraud systems are about balancing precision and recall, not building a wall.

2.3 Why This Is Fundamentally an Economics Problem, Not Just an Engineering Problem

It’s worth pausing on why coupon abuse persists at all despite obvious detection efforts — the answer is that it is, at its core, driven by simple economics rather than technical cleverness. If a $15 signup bonus costs a fraudster less than $15 in time, rented phone numbers, proxy IPs, and device costs to obtain, the attack is profitable, and profitable attacks scale automatically because rational actors (and automated tooling built by rational actors) will keep running them as long as the math works. This reframes the engineering goal precisely: the job of the fraud detection system is not to make abuse theoretically impossible — that goal is unreachable against a determined, well-resourced adversary — but to raise the marginal cost of each successful fraudulent redemption above its value, at which point the economics stop making sense and the attacker moves on to an easier target elsewhere. This is the same underlying logic used in most real-world security engineering: a lock does not make a house unbreakable, it makes it not worth the burglar’s time compared to the neighbour’s house without a lock.

03

Core Concepts

The vocabulary and building blocks you need before looking at architecture.

3.1 Types of Coupon and Promo Abuse

Type

Multi-accounting

One real person creates many fake accounts (different emails/phones) purely to repeatedly claim “new user” offers.

Type

Referral Fraud

Fraudster refers themselves using a second fake account to earn both the “referrer” and “referee” bonus in a single closed loop.

Type

Coupon Stacking / Combo Abuse

Exploiting a bug that lets multiple discount codes apply on top of each other beyond intended limits, effectively compounding a promotion the merchant never authorised.

Type

Coupon Code Leakage

A code meant for a small, targeted audience (e.g. “loyal customers”) leaks publicly on a coupon-aggregator site and gets mass-redeemed by strangers.

Type

Bot / Scripted Abuse

Automated scripts hitting the checkout API directly, bypassing the normal UI, to redeem codes at high speed against any weakness in server-side validation.

Type

Device Farming

Physical or emulated device farms each appearing as a “new device” to defeat device-based limits, sometimes reaching thousands of virtual instances per operator.

Type

Return / Cash-back Abuse

Using a discount to buy an item cheaply, then returning it for a full refund at original price, pocketing the difference — a particularly damaging pattern for physical goods.

Type

Collusion Rings

Groups of real people coordinating (often via social media or Telegram groups) to repeatedly exploit a single promo across many genuine accounts — hard to spot because every individual signal looks legitimate.

3.2 Key Signals Used to Detect Abuse

A risk engine is only as good as the signals it can see. Below are the main signal families used in production fraud systems, roughly ordered from “cheap and easy to spoof” to “expensive and hard to spoof.”

Signal FamilyExampleSpoof Difficulty
Identity fieldsEmail, phone number, nameLow — free or cheap to generate
Network signalsIP address, ASN, proxy/VPN detectionLow–Medium — residential proxies are cheap to rent
Device fingerprintBrowser canvas hash, device model, installed fonts, screen sizeMedium — emulators can partially spoof this
Behavioural biometricsTyping cadence, mouse movement, scroll patternsHigh — hard to script convincingly at scale
Payment signalsCard BIN, card fingerprint, billing address reuseHigh — real payment instruments are the scarcest resource
Graph / network signalsShared device, shared address, shared payment method across accountsVery High — requires knowing the whole ecosystem, not just one account
Historical velocitySignups per device/IP/hour, redemptions per dayMedium — rate-limitable but requires tracking state
Beginner Example

Suppose three “different” accounts sign up within two minutes of each other, all from the same device fingerprint, all use email addresses that are variations of “johndoe123,” and all immediately try to redeem the same welcome coupon. No single signal proves fraud, but the combination — timing, device match, email similarity, and identical coupon usage — is a very strong pattern that any reasonable rule set or model should flag.

3.3 Risk Score and Decisioning

Rather than a binary “allow or block,” modern systems compute a risk score — typically a number from 0 to 100, or a probability between 0 and 1 — representing “how likely is this redemption attempt to be abusive.” The score then maps to an action through configurable thresholds:

  • Low risk (0–30): Approve automatically, no friction.
  • Medium risk (31–70): Add friction — require phone/OTP verification, a CAPTCHA, or delay the reward until after a return-safe window.
  • High risk (71–100): Block the redemption, and optionally flag the account for manual review or silently give a “shadow” experience (the user thinks the coupon worked, but no real discount is applied — this avoids alerting sophisticated fraud rings that they’ve been caught).
i
What an Interviewer May Ask

“Why use a graded risk score instead of a hard allow/deny rule?” A good answer: fraud signals are probabilistic, not certain — a graded score lets you apply proportional friction, minimising harm to genuine users while still raising the cost of attack for fraudsters, and it gives you tunable thresholds you can adjust without redeploying code.

3.4 Velocity Checks

A “velocity check” measures how often something happens within a time window — for example, “how many accounts have been created from this device fingerprint in the last 24 hours?” Velocity checks are one of the cheapest and most effective tools because legitimate humans are naturally rate-limited (a person cannot sign up for 50 accounts per minute without automation), while fraud rings, almost by definition, need high throughput to make the economics work.

3.5 Device Fingerprinting

Device fingerprinting collects dozens of small, mostly non-identifying attributes about a browser or mobile device (screen resolution, timezone, installed fonts, GPU rendering quirks, OS version, sensor data) and combines them into a single hash that is stable across sessions but hard to fake convincingly at scale. It is not perfect — privacy-focused browsers and careful fraudsters can reduce fingerprint uniqueness — but it substantially raises the cost of running a device farm.

3.6 CAP Theorem and Consistency Trade-offs in Fraud Systems

The CAP theorem states that a distributed data store can only guarantee two of three properties at once during a network partition: Consistency (every read sees the most recent write), Availability (every request gets a response, even if it might be stale), and Partition tolerance (the system keeps working despite network failures between nodes). Since network partitions are a fact of life in any distributed system running across multiple servers or regions, the real-world choice is almost always between consistency and availability when a partition occurs.

This theorem shows up directly in the coupon abuse system in a very practical way. Consider the “maximum redemptions per coupon” counter. If two data centres briefly cannot talk to each other, do you:

  • Choose consistency (CP): Refuse to process new redemptions in the isolated data centre until connectivity is restored, guaranteeing the global counter is never wrong, at the cost of rejecting legitimate purchases during the outage.
  • Choose availability (AP): Let each data centre keep accepting redemptions independently using its local view of the counter, risking that the coupon is slightly over-redeemed globally once both sides reconcile, but never blocking a legitimate sale.

For most coupon systems, the pragmatic answer is AP with reconciliation: allow local writes to continue (availability wins), accept a small, bounded risk of over-redemption during rare partition events, and run a reconciliation job afterward that identifies and, if needed, claws back any redemptions that exceeded the true global limit. This mirrors the general industry lesson that for promotional and marketing systems — as opposed to core payment ledgers, where strict consistency is usually non-negotiable — a slightly relaxed consistency model is an acceptable, even preferable, trade-off.

i
What an Interviewer May Ask

“Would you choose strong or eventual consistency for the redemption counter, and why?” A strong answer distinguishes between the financial ledger (redemption records, which need strong consistency and durability) and the velocity/limit counters (which can tolerate eventual consistency because the cost of a rare over-redemption is far lower than the cost of rejecting real sales during a network blip).

3.7 Data Structures and Algorithms Under the Hood

A few specific data structures and algorithms show up repeatedly in production fraud systems because they solve the “very high volume, needs to be fast, approximate answers are acceptable” constraint extremely well.

Structure

Bloom Filter

A space-efficient probabilistic set membership structure — perfect for a first-pass “have we possibly seen this device fingerprint / email / card before” check across billions of entries using only a few bits per entry, with zero false negatives and a small, tunable false-positive rate. A positive result triggers a more expensive, precise lookup; a negative result short-circuits instantly, saving enormous database load.

Structure

HyperLogLog

An algorithm for approximating the count of distinct elements in a huge set (e.g. “roughly how many unique devices redeemed coupons today”) using a fixed, tiny amount of memory regardless of the true cardinality — ideal for dashboards and coarse velocity signals where exact precision isn’t required.

Structure

LRU Cache

Least-Recently-Used eviction policy used inside the device fingerprint and risk-score caches, keeping the most recently and frequently accessed entities in fast memory while naturally aging out stale, rarely-accessed ones.

Structure

Union-Find (Disjoint Set)

An efficient algorithm for grouping entities into connected clusters incrementally — as new shared-device or shared-payment edges are discovered, union-find merges clusters in near-constant time, which is how the entity graph’s “high risk cluster” grouping can be maintained efficiently at scale rather than recomputing full graph traversals on every update.

Structure

Sliding Window Counters

Rather than a single fixed daily bucket, many velocity checks use a sliding time window (e.g. “requests in the last 60 minutes, continuously”) implemented with a small ring buffer of sub-window counts, giving smoother, more precise rate limiting than coarse fixed buckets that reset abruptly at midnight.

Structure

Min-hash / Locality-Sensitive Hashing

Used to detect near-duplicate patterns cheaply — for example, flagging email addresses or shipping addresses that are suspiciously similar (but not identical) to previously flagged ones, catching simple evasion tricks like adding a digit to an email local-part.

Software Example

A blocklist of ten million known-bad device fingerprints stored as a Bloom filter might occupy only a few megabytes of memory and answer “is this fingerprint blocked” in constant time, compared to a multi-gigabyte exact hash set or a network round-trip to a database — at the cost of an intentionally tiny, tunable rate of false positives that get resolved by the more precise secondary check.

04

Architecture and Components

The full request path, box by box, from a client tapping “Apply Coupon” to a decision being returned.

Every box in the diagram below plays a specific role. We are deliberately explicit about infrastructure components like the API Gateway and Load Balancer because, in a real interview or real production system, glossing over them is one of the most common mistakes junior designers make — a fraud detection system does not float in isolation, it sits inside a normal, horizontally scaled microservice architecture.

4.1 Component Roles at a Glance

ComponentResponsibilityWhy It Exists
CDN + API Gateway + WAFTerminate TLS, authenticate, rate-limit, block obvious bot traffic before it hits application servers.Absorb attack volume cheaply; enforce cross-cutting concerns in one place.
Load BalancerDistribute traffic across healthy service instances.Enables horizontal scaling and self-healing.
Coupon ServiceOwns the coupon catalogue, redemption records, and the atomic check-and-increment on limited-quantity coupons.Single source of truth for “is this code valid and available?”
Fraud Detection ServiceOrchestrates a risk decision by calling sub-services in parallel and combining the results.Isolates the fraud-decision brain from business logic, letting each team ship independently.
Device Fingerprint ServiceTurns raw client signals into a stable device identifier and reports how many accounts share it.Detects multi-accounting cheaply.
Rules EngineEvaluates human-authored deterministic rules against the request context.Fast, explainable, easy for analysts to tune.
ML Risk ModelScores the request with a trained model over dozens of features.Catches subtle patterns rules never encode.
Graph ServiceTraverses relationships between entities (devices, cards, accounts) to surface hidden clusters.Exposes coordinated abuse rings a single-account view can’t see.
Feature StoreRead-optimised aggregates (velocity, ratios) pre-computed by streaming jobs.Keeps the hot path fast; avoids scanning raw data at request time.
RedisSub-millisecond counters, rate limits, hot fingerprint cache.Handles the request-per-second scale the primary DB cannot at low cost.
Primary DBDurable coupon definitions, redemption records, workflow state.Financial-adjacent data needs strong consistency and auditability.
Kafka BusImmutable, ordered event stream of every risk decision and lifecycle transition.Decouples producers/consumers; enables replay, retraining, and audit.
Async WorkersRetroactive re-scoring, clawback issuance, manual-review queue population.Catches fraud only visible with hindsight or wider context.
Data WarehouseLong-term historical storage for analytics and ML training.Cheap large-scan queries the OLTP DB can’t serve efficiently.
Notification ServiceSends OTPs, appeal-flow emails, blocked-user messages.Isolates delivery concerns from the risk-decision hot path.
Observability StackMetrics, structured logs, distributed traces.You cannot debug or improve what you can’t measure.
05

Internal Working

What actually happens, step by step, when a user taps “Apply Coupon.”

Let’s trace a single request end to end. A user, freshly registered thirty seconds ago, enters the code WELCOME20 at checkout on the mobile app.

5.1 Combining Signals Into One Decision

The Fraud Detection Service does not simply average all the signals. A common, production-tested approach layers three tiers:

  1. Hard blocklist checks (fastest, deterministic): Is this device fingerprint, email domain, phone number, or IP on a known-bad list? If yes, reject immediately without waiting for the ML model — this handles known repeat offenders extremely cheaply.
  2. Rules engine (fast, deterministic, human-readable): Velocity limits like “more than 3 signups from this device in 24 hours” or “this exact card number used on more than 2 accounts this month.” These rules are simple if/then logic a fraud analyst can write and tune without a data scientist.
  3. ML risk model (slower, probabilistic): For requests that pass the first two tiers, a trained model scores the request using dozens of numerical features, producing a fine-grained probability that captures subtle patterns humans wouldn’t think to write as explicit rules.

The final score is typically a weighted combination, with hard rule violations able to override the model entirely (a hard blocklist hit should never be overturned by a low ML score).

Java — simplified risk decision orchestration
public class FraudDecisionOrchestrator {

    private final BlocklistService blocklistService;
    private final RulesEngine rulesEngine;
    private final MlRiskClient mlRiskClient;

    public RiskDecision evaluate(RedemptionRequest request) {
        if (blocklistService.isBlocked(request.getDeviceFingerprint(),
                                        request.getIpAddress(),
                                        request.getEmail())) {
            return RiskDecision.block("BLOCKLIST_MATCH");
        }
        RuleResult ruleResult = rulesEngine.evaluate(request);
        if (ruleResult.isHardFail()) {
            return RiskDecision.block(ruleResult.getReasonCode());
        }
        double mlScore;
        try {
            mlScore = mlRiskClient.scoreWithTimeout(request, Duration.ofMillis(80));
        } catch (TimeoutException | ServiceUnavailableException e) {
            mlScore = ruleResult.hasSoftFlags() ? 0.65 : 0.35;
        }
        double combinedScore = combine(ruleResult.getSoftScore(), mlScore);
        return RiskDecision.fromScore(combinedScore);
    }

    private double combine(double ruleScore, double mlScore) {
        return Math.max(ruleScore, (0.3 * ruleScore) + (0.7 * mlScore));
    }
}

5.2 Feature Engineering for the ML Model

Feature quality matters more than model architecture for this problem. Typical features fed into the model include:

Feature

Account Age

Seconds/minutes since account creation at the time of redemption — new accounts redeeming immediately is a strong signal.

Feature

Device Signup Velocity

Number of distinct accounts created on this exact device fingerprint in the last 1h/24h/7d.

Feature

IP/ASN Reputation

Whether the IP belongs to a known VPN/proxy/hosting provider ASN versus a residential ISP.

Feature

Email Pattern Entropy

Statistical similarity of the email local-part to a known pattern (e.g. “name123”, “name456” sequences).

Feature

Payment Instrument Reuse

Number of distinct accounts that have used this same card fingerprint or billing address.

Feature

Behavioural Timing

Time between screens (signup → checkout in 4 seconds is inhuman; 4 minutes is normal).

Feature

Graph Centrality

How many “hops” this account is from a confirmed fraud account in the entity graph.

Feature

Historical Coupon Usage

Has this device/IP/payment combo redeemed this exact coupon category before under a different account?

5.3 Concurrency Control for Limited-Quantity Coupons

Some coupons have a hard global cap — “first 500 customers only.” This introduces a classic concurrency problem: under heavy simultaneous load, dozens of requests can each read “current count = 499, limit = 500” at nearly the same instant, all believe they are the winning redemption, and all proceed to apply the discount, blowing past the intended cap. This is a textbook race condition, and it needs to be solved with the same rigor as an inventory oversell problem in an e-commerce flash sale.

The safest fix is to make the check-and-increment operation atomic — a single indivisible step rather than two separate steps (read, then write) that another request could interleave with. Two common implementations:

  • Database-level atomic update with a conditional clause: An SQL statement like UPDATE coupons SET redeemed_count = redeemed_count + 1 WHERE code = ? AND redeemed_count < max_redemptions either succeeds (and the row count returned tells you whether it actually incremented) or affects zero rows if the cap was already reached — the database’s own locking guarantees correctness without an explicit application-level lock.
  • Redis atomic Lua script or INCR-then-check: For very high throughput scenarios, a small Lua script executed atomically inside Redis (Redis guarantees scripts run without interleaving) checks and increments the counter in one round trip, avoiding both database load and race conditions.
Java — atomic conditional increment against the database
@Transactional
public boolean tryReserveCouponSlot(String couponCode) {
    int rowsUpdated = jdbcTemplate.update(
        "UPDATE coupons " +
        "SET redeemed_count = redeemed_count + 1 " +
        "WHERE code = ? AND redeemed_count < max_redemptions",
        couponCode
    );
    return rowsUpdated == 1;
}

Notice this pattern avoids taking an explicit application-level lock (e.g. a synchronized block or a distributed lock service), which would not even work correctly across multiple service instances anyway. Instead, it pushes the atomicity guarantee down into the data store, which is specifically designed to handle concurrent conditional writes safely and efficiently — a pattern that generalises well beyond coupons to any “limited quantity” resource allocation problem, including flash-sale inventory and seat reservations.

06

Data Flow and Lifecycle

Following a coupon from creation to redemption to potential retroactive clawback.

6.1 Why Retroactive Review Matters

Not all abuse is visible at the moment of redemption. A device fingerprint might look “clean” the first time it’s ever seen — the fraud only becomes obvious once the fifth, tenth, or fiftieth linked account appears. This is why the architecture includes both a synchronous, low-latency path (block obvious fraud immediately) and an asynchronous, batch path (re-score past redemptions as new graph connections and patterns emerge, and issue clawbacks — reversing the discount, charging back the difference, or banning the account — when confidence crosses a threshold).

6.2 Data Retention and Lifecycle of Signals

DataStorageTypical Retention
Real-time velocity countersRedis, TTL-basedMinutes to a few days
Redemption recordsSharded relational DBYears (financial/audit requirement)
Device fingerprint graph edgesGraph DB / graph serviceMonths, rolled up over time
Raw event streamKafka topic, then data warehouseKafka: days; warehouse: years for model training
ML training featuresFeature storeRolling window, e.g. 90 days hot, older archived

6.3 Lifecycle States

A coupon redemption moves through a well-defined lifecycle: Created (coupon is defined but not yet active) → Active (available for redemption) → Validated (code entered by a user) → RiskAssessed (fraud engine has scored it) → then branches to Approved (low score, discount applied), Friction (medium score, step-up verification required), or Blocked (high score, hard denied). Approved and passed-friction redemptions land in Redeemed. A separate async pipeline can later move a Redeemed record to ManualReview, then to Confirmed (legitimate) or Clawback (retroactively identified as fraud). Coupons that hit their end date without redemption transition to Expired.

07

Advantages, Disadvantages and Trade-offs

No fraud system is free. Understand what you are trading away.

Advantages

  • Directly protects marketing/promo budget from being drained by fraud rings.
  • Improves the accuracy of growth and acquisition metrics used for business decisions.
  • Layered design allows fast deterministic blocking plus nuanced ML scoring, balancing speed and accuracy.
  • Async re-scoring catches sophisticated fraud that wasn’t visible at first sight.
  • Reusable across many abuse types beyond coupons — the same architecture protects against fake reviews, referral fraud, and account takeover.

Disadvantages / Costs

  • Added latency and infrastructure cost on every checkout request, even legitimate ones.
  • False positives create real customer friction and support burden.
  • Significant engineering and data science investment to build and maintain the ML pipeline.
  • Constant adversarial pressure — rules and models decay as fraudsters adapt, requiring ongoing tuning.
  • Privacy and compliance overhead — device fingerprinting and behavioural tracking must respect regulations like GDPR.

7.1 Key Trade-off: Precision vs. Recall

Every fraud system has to choose an operating point on the precision/recall curve. High precision (few false positives, but you miss some real fraud) protects the customer experience. High recall (catch almost all fraud, but flag more legitimate users) protects the promo budget more aggressively. Most mature systems don’t pick one point — they use the graded risk-score-plus-friction approach from Chapter 3 so that only the highest-confidence cases get hard blocked, while medium-confidence cases get extra verification instead of an outright rejection.

i
What an Interviewer May Ask

“How would you decide where to set the risk threshold?” Talk about running the model in shadow mode first (scoring but not blocking), measuring the precision/recall trade-off against a labelled dataset of confirmed fraud, and involving the business (finance/growth teams) to translate the trade-off into dollars — the “right” threshold is a business decision informed by data, not a purely technical one.

08

Performance and Scalability

Keeping the risk decision fast even at millions of redemptions per day.

The single hardest performance constraint in this system is that fraud detection sits in the critical path of checkout. Adding 500 ms to every purchase would meaningfully hurt conversion rates industry-wide, so the target latency budget for the entire fraud decision (device lookup + rules + ML inference) is typically kept under 80–100 ms at the 99th percentile.

8.1 Techniques Used to Hit the Latency Budget

  • Parallel fan-out: The Fraud Detection Service calls the device fingerprint lookup, velocity counter fetch, and rules engine concurrently rather than sequentially, since they don’t depend on each other.
  • Feature store pre-computation: Expensive aggregates (e.g. “accounts per device in last 7 days”) are computed continuously by streaming jobs and simply read at request time, rather than computed on-the-fly from raw data.
  • In-memory caching: Redis holds hot counters and recent fingerprint lookups with sub-millisecond access, avoiding database round trips for the common case.
  • Model complexity budget: The real-time model is often a lightweight gradient-boosted tree (e.g. XGBoost/LightGBM) rather than a large neural network, because tree inference is extremely fast (microseconds) at the cost of somewhat lower accuracy than deep learning; heavier models run only in the async batch path.
  • Circuit breakers and timeouts: Every downstream call has a strict timeout with a safe fallback value, so one slow dependency cannot cascade into checkout-wide latency spikes.

8.2 Scaling the Write Path — Counters

A subtle scaling challenge: incrementing “redemptions today for coupon X” as an atomic counter under high concurrency (a flash sale might see thousands of redemption attempts per second for one popular code) can become a hotspot if implemented naively as a single database row update. Common solutions:

Technique

Redis Atomic INCR

Use Redis’s single-threaded atomic increment (INCR) for the hot counter, which can sustain hundreds of thousands of ops/sec on a single node.

Technique

Counter Sharding

Split one logical counter into N physical shards (e.g. per hash of user ID) and sum them when reading, reducing contention on any single key.

Technique

Approximate Limits

For very high-traffic, low-value coupons, accept slight overshoot (a few extra redemptions past the cap) in exchange for much higher throughput, reconciling exactly in the async batch job.

Technique

Token Bucket at the Gateway

Rate-limit the redemption endpoint itself at the API Gateway level to smooth out sudden traffic spikes before they even reach the Coupon Service.

8.3 Scaling the ML Inference Layer

The ML Risk Scoring Service is typically deployed as a horizontally scaled, stateless fleet behind the Load Balancer, with model artefacts loaded into memory on each instance to avoid network calls during inference. For very large models, teams use dedicated inference accelerators or batch multiple concurrent scoring requests together (micro-batching) to improve GPU/CPU utilisation, though for coupon fraud the models are usually small enough that CPU inference is sufficient.

8.4 Capacity Planning and Cost Optimisation

Fraud detection infrastructure has a spiky, promotion-driven traffic profile rather than a smooth, predictable one — a single viral marketing campaign or a major flash sale can multiply normal traffic by 10x or more within minutes. Capacity planning for this system therefore leans heavily on elastic, horizontal auto-scaling rather than provisioning for permanent peak load, which would waste money the other 350 days of the year.

  • Predictive pre-scaling for known events: When marketing schedules a major promotional campaign, infrastructure teams pre-warm the Coupon Service, Fraud Detection Service, and Redis cluster ahead of the announced start time, since reactive auto-scaling alone can lag behind a sudden traffic cliff by a minute or two.
  • Right-sizing the ML inference fleet: Horizontally scaling many small, cheap instances is more cost-effective than fewer large ones, and enables finer-grained auto-scaling response to load.
  • Tiered storage for historical data: Hot data (last few days) stays in fast, more expensive storage (Redis, feature store); older data used for model retraining and analytics moves to cheaper warehouse storage.
  • Sampling for non-critical logging: At very high volume, sample “boring” approved traffic for detailed logs while always fully logging any request that triggered a rule, flag, or block.
  • Reserved capacity for baseline, spot/on-demand for bursts: Combine reserved instances for steady traffic with on-demand or spot instances for unpredictable promotional spikes.
Common Mistake

Sizing the fraud detection fleet only for average daily traffic. The very moments a fraud system matters most — viral promotions and flash sales — are exactly when both legitimate and fraudulent traffic spike together, and an under-provisioned system either falls back to unsafe defaults or, worse, becomes the bottleneck that takes down checkout entirely.

09

High Availability and Reliability

What happens when a component fails, and how the system degrades gracefully instead of breaking checkout entirely.

9.1 Fail-Open vs. Fail-Closed

A critical design decision: if the Fraud Detection Service is completely unreachable, should the Coupon Service fail open (allow the redemption anyway) or fail closed (block all redemptions until fraud checks are restored)? There is no universally correct answer — it depends on the coupon’s financial exposure:

Fail-Open Makes Sense When…

  • The coupon has low financial value (e.g. free shipping vs. 90% off).
  • Checkout availability matters more than perfect fraud prevention for a brief outage window.
  • You can retroactively claw back abuse detected once systems recover.

Fail-Closed Makes Sense When…

  • The coupon is high value or the promotion is actively being targeted by known fraud rings.
  • Clawback after the fact is difficult (e.g. the “discount” was a cash payout, not a product return).
  • Regulatory or financial controls require a fraud check before any high-risk transaction completes.

Many production systems implement a hybrid: fail open with a default medium-risk score (adds friction like an OTP check) rather than a full open/closed binary, minimising both fraud exposure and checkout disruption.

9.2 Redundancy Across the Stack

  • Multi-AZ / multi-region deployment: All stateless services run across multiple availability zones behind the load balancer, so a single zone failure doesn’t take down the system.
  • Database replication: The primary database uses synchronous replication within a region and asynchronous replication cross-region for disaster recovery, with automated failover to a replica if the primary becomes unhealthy.
  • Redis high availability: Deployed as a cluster with replicas per shard, so counter data survives a single node failure (accepting that a brief window of counter data might be lost during failover — acceptable for rate-limiting use cases).
  • Kafka replication: Topics configured with replication factor 3, ensuring event data survives broker failures without loss.
i
What an Interviewer May Ask

“What happens if Redis goes down mid-flash-sale?” Good answer: velocity checks temporarily fall back to the rules engine’s database-backed counters (slower but durable), or the system briefly widens risk thresholds toward “medium risk plus friction” instead of hard blocking, trading some fraud risk for continued availability, while alerting on-call engineers immediately.

9.3 Consensus and Leader Election

Several components in this architecture need to agree on “who is currently in charge” — for example, only one instance of the async batch re-scoring job should own a particular partition of the Kafka event stream at a time, to avoid duplicate processing. This is solved using a consensus protocol, typically implemented through a coordination service like Apache ZooKeeper or etcd, both of which internally use consensus algorithms (ZooKeeper’s ZAB, or Raft in etcd) to reliably elect a leader and detect failures even when some nodes are slow or unreachable. Kafka consumer groups use a similar mechanism internally to assign partitions to consumer instances and rebalance automatically when an instance joins or leaves the group.

9.4 Disaster Recovery and Backup Strategy

DR ConcernStrategy
Recovery Point Objective (RPO)Continuous cross-region database replication keeps RPO in the range of seconds to a few minutes for redemption records; velocity counters in Redis can tolerate a larger RPO since they are not financial records.
Recovery Time Objective (RTO)Automated failover with health-check-driven DNS or load balancer re-routing targets an RTO of a few minutes for a full regional failover.
Backup cadenceAutomated daily full database snapshots plus continuous write-ahead-log (WAL) shipping for point-in-time recovery to any second within the retention window.
Backup testingRegular, scheduled restore drills — a backup that has never been restored in practice is not a reliable backup, only an assumption.
RunbooksDocumented, rehearsed failover procedures so an on-call engineer under pressure at 3am is following a tested checklist, not improvising.
10

Security

Protecting the fraud system itself, not just what it protects.

A subtle but important point: the fraud detection system is itself a high-value attack target. If an attacker learns your exact rules and thresholds, they can craft requests that stay just under them. Security here has two dimensions — protecting the coupon/checkout flow from abuse (the system’s purpose) and protecting the fraud system’s internals from being reverse-engineered or tampered with.

10.1 Protecting the Redemption Endpoint

Control

Strong Authentication

Every redemption request must carry a valid, short-lived session token (JWT) verified at the API Gateway; anonymous redemption attempts are rejected outright.

Control

Rate Limiting at Multiple Layers

Per-IP and per-account rate limits at the gateway, in addition to the business-logic velocity checks inside the fraud engine, to blunt brute-force coupon guessing.

Control

Coupon Code Entropy

Generate codes with sufficient randomness (e.g. 10+ alphanumeric characters) so they cannot be brute-forced or guessed; avoid sequential or predictable codes.

Control

Server-Side Validation Only

Never trust client-reported discount amounts or eligibility flags — all validation and price calculation happens server-side.

Control

TLS Everywhere

All traffic, including internal service-to-service calls, encrypted in transit to prevent interception of device fingerprints or session tokens.

Control

Input Validation

Sanitise and validate the coupon code format before it ever reaches the database layer, preventing injection attacks.

10.2 Protecting the Fraud System’s Confidentiality

Exact rule thresholds (e.g. “block if more than 3 signups per device per day”) should never be exposed in client-side code, API error messages, or public documentation. A rejected redemption should return a generic message like “This code could not be applied” rather than “Blocked: velocity threshold exceeded (4/3),” which would hand attackers a precise map of your defences. This is an application of security-by-obscurity used correctly — not as your only defence, but as one layer that raises the cost of reverse engineering.

Common Mistake

Returning overly descriptive error codes (“Error: device linked to 5 other accounts”) straight to the client. Fraudsters A/B test against your own system for free if you leak this information, learning exactly how to stay under your thresholds.

10.3 Privacy and Compliance

  • Store fingerprints as one-way hashes, not raw identifying data, wherever possible.
  • Provide a documented lawful basis for fraud-prevention data processing (typically “legitimate interest” under GDPR), and disclose it in the privacy policy.
  • Apply data minimisation — collect only the signals genuinely needed for the risk decision, not everything technically possible.
  • Honour data subject deletion requests by having a clear process to purge or anonymise a user’s fingerprint/graph data on request, balanced against legitimate fraud-prevention retention needs.

10.4 Access Control Internally

The manual review dashboard used by fraud analysts is itself sensitive — it exposes cross-account linkage data that could be misused. Apply role-based access control (RBAC), full audit logging of every lookup an analyst performs, and the principle of least privilege so analysts only see what’s necessary for their queue.

10.5 API Key Management and Secure Service-to-Service Design

  • Mutual TLS (mTLS) between internal services, so each side cryptographically verifies the other’s identity, typically managed automatically by a service mesh (Istio, Linkerd).
  • Short-lived service credentials issued by a central identity provider or secrets manager (Vault, cloud IAM) rather than long-lived static API keys, dramatically shrinking the blast radius if a credential ever leaks.
  • Least-privilege service accounts — the ML Risk Scoring Service, for example, should have read-only access to the feature store and no direct access to the primary transactional database at all.
  • External-facing API keys for partner integrations should be scoped narrowly, rotated regularly, and rate-limited independently of internal traffic.

10.6 Multi-Factor and Step-Up Verification

For medium-risk redemption attempts, the system’s “friction” response commonly takes the shape of step-up verification rather than an all-or-nothing decision: a one-time password (OTP) sent to a phone number, an email confirmation link, or occasionally a full multi-factor authentication (MFA) challenge if the account also holds a stored payment method. This graded approach mirrors zero-trust security principles applied to fraud prevention — verify proportionally to the risk observed, and make the verification bar adapt dynamically.

11

Monitoring and Observability

Detecting problems, drift and attacks in real time.

11.1 System Health Metrics (Am I Up?)

MetricWhy It MattersTypical Alert Trigger
Fraud Service p99 latencyImpacts checkout latency directly> 100 ms sustained over 5 min
Fraud Service error rateIndicates downstream failure or bug> 1% over 5 min
Redis counter service availabilityLoss forces fallback / degraded modeAny node down / replication lag
Kafka consumer lagAsync re-scoring falling behind means late fraud detectionLag > 5 min sustained
ML inference timeoutsModel or infra issue triggering fallback path> 2% requests hitting fallback

11.2 Business Metrics (Am I Working?)

  • Block rate: % of redemption attempts blocked. Sudden spikes suggest either an active attack or a broken rule/model.
  • False positive rate: % of blocked users who successfully complete a legitimate purchase after appealing / after the block.
  • Estimated fraud loss: Modelled promo-budget loss attributable to abuse that got through, informed by later-detected fraud in the async pipeline.
  • Coupon-to-conversion ratio: If a heavy uptick in coupon usage doesn’t translate to matching new-customer purchase volume, that gap is often abuse.

11.3 Model Health Metrics (Am I Still Accurate?)

  • Feature distribution drift: If the statistical distribution of a feature shifts significantly, the model may be scoring on out-of-distribution data.
  • Prediction distribution drift: If the histogram of risk scores changes shape suddenly, something upstream has changed.
  • Post-hoc accuracy: When ground truth becomes available, continuously measure precision, recall, and AUC of the model’s live predictions vs. eventual reality.
  • Feature freshness: Age of the newest data point in each real-time feature — if a feature stops updating, its predictive power collapses silently.
Best Practice

Log every risk decision with the full feature vector, model version, rule flags fired, and final decision. This audit trail is essential both for debugging and for retraining data.

11.4 Distributed Tracing and Structured Logging

  • Propagate a trace ID (and span IDs) at the API Gateway on every incoming request, typically via OpenTelemetry, so every downstream service carries it on every log line and outbound call.
  • Structured, machine-readable logs (JSON with well-defined fields) rather than free-text logs, indexable and aggregatable in a log platform.
  • Distributed tracing UIs (Jaeger, Tempo, Zipkin) let you visually inspect the entire fan-out for a single request.
  • Log sampling for cost control: keep 100% of blocked or flagged decisions, plus a small percentage of clean approves.

11.5 SLOs, Error Budgets, and Anomaly Detection

Rather than manually eyeballing dashboards, mature teams define explicit Service Level Objectives (SLOs) — e.g. “p99 fraud decision latency under 100 ms, 99.95% of the time” — and build error budgets on top. Unsupervised anomaly detection (statistical control charts on block rates, seasonal decomposition on redemption volume, sudden-change detection on model score distributions) flags patterns humans wouldn’t spot on a normal dashboard, catching both silent regressions and coordinated fraud campaigns earlier than static threshold alerts alone would.

12

Deployment and Cloud

Rolling changes safely into a system where a bad deploy might mean either “block every legitimate customer” or “let every fraudster through.”

12.1 Deployment Topology

  • Consistent runtime environments from local development to production via containers.
  • Horizontal auto-scaling based on CPU/memory or custom metrics.
  • Rolling updates with automatic rollback if health checks start failing.
  • Self-healing — a crashed container is restarted, an unhealthy node is drained.

12.2 Safe Rollout Patterns

Pattern

Shadow Mode

New rules or models score every request in parallel with the current production rules, but their decisions are only logged, not enforced. Compare outcomes for days or weeks before promoting.

Pattern

Canary Deployment

Route a small percentage (1–5%) of traffic to the new version. If block rates, latency, and error rates stay within expected bands, gradually widen the split; otherwise roll back automatically.

Pattern

Feature Flags

Wrap new rule logic behind a feature flag so you can instantly disable it (without a full redeploy) if it misbehaves in production.

Pattern

Blue/Green

Deploy the new version alongside the old, keeping the old alive as an instant fallback until the new version has proven itself for a full traffic cycle.

12.3 CI/CD and Model Deployment Pipeline

  1. Data validation: schema and distribution checks on incoming training data.
  2. Model training: automated retraining on a schedule (e.g. weekly) with the latest labelled fraud data.
  3. Offline evaluation: measure precision/recall/AUC on a held-out test set and compare against the current production model.
  4. Shadow-mode deployment: run the candidate model in parallel with production, log differences.
  5. Manual approval gate: a data scientist reviews the metrics and shadow-mode differences before promoting.
  6. Canary promotion: gradually roll out to real traffic.
  7. Automated rollback: revert to the prior model version if health metrics degrade.
Common Mistake

Deploying a new fraud rule directly to 100% traffic on a Friday afternoon because “it’s just a small change.” A single misconfigured threshold can silently block millions in legitimate revenue over the weekend.

12.4 Environment Parity and Configuration Management

  • Infrastructure as Code (Terraform, Pulumi, CloudFormation) so both environments are provisioned from the same declarative definitions.
  • Configuration stored separately from code in a versioned config service or feature-flag platform, so promoting a config change follows the same audit-and-rollback discipline as promoting code.
  • Production-like data volume in load tests replaying realistic traffic patterns (redemption bursts, fraud campaigns) against staging before major releases.
13

Data Layer — Databases and Storage

Choosing the right store for each kind of fraud-relevant data.

A common design mistake is trying to keep every kind of fraud data in one general-purpose database. The workload profiles are very different — ultra-fast counters, transactional redemption records, high-fan-in graph queries, streaming event data — and each is best served by a store specialised for its access pattern.

13.1 Store-by-Store Breakdown

Data CategoryChosen StoreWhy It Fits
Real-time counters (per user/device/IP)Redis (in-memory KV, TTL)Sub-millisecond reads/writes, atomic INCR, natural TTL expiry
Coupon definitions & redemption recordsSharded relational DB (PostgreSQL/MySQL)Strong consistency, joins, transactional guarantees for financial-adjacent data
Device / account entity graphGraph DB (Neo4j, JanusGraph) or purpose-built graph serviceEfficient multi-hop traversals like “accounts within 2 hops of a fraud account”
Streaming eventsKafka (log-structured broker)Durable, ordered, high-throughput fan-out to multiple consumers
ML feature storeDedicated feature store (Feast) or Redis + ParquetRead-optimised for inference; write-optimised for streaming aggregation
Historical / warehouse dataColumnar warehouse (BigQuery, Snowflake, Redshift)Cheap long-term storage, efficient large scans for model training and analytics
Manual review workflow stateSame relational DB, separate schemaHuman workflow needs strong consistency and easy joins with redemption data
Java — velocity check using Redis pipelined atomic reads
public class VelocityChecker {

    private final RedisTemplate<String, Long> redis;

    public VelocitySnapshot snapshot(String deviceFp, String ipHash) {
        List<Object> results = redis.executePipelined((RedisCallback<Object>) conn -> {
            conn.stringCommands().get(("velocity:dev:" + deviceFp + ":24h").getBytes());
            conn.stringCommands().get(("velocity:dev:" + deviceFp + ":7d").getBytes());
            conn.stringCommands().get(("velocity:ip:"  + ipHash   + ":1h").getBytes());
            conn.stringCommands().get(("velocity:ip:"  + ipHash   + ":24h").getBytes());
            return null;
        });

        return new VelocitySnapshot(
            parseLong(results.get(0)),
            parseLong(results.get(1)),
            parseLong(results.get(2)),
            parseLong(results.get(3))
        );
    }

    private long parseLong(Object o) { return o == null ? 0L : Long.parseLong(o.toString()); }
}

13.2 Sharding Strategy for the Redemption Table

  • Time-based partitioning (e.g. one partition per month) — excellent for expiry / archival of old data, natural for time-window queries.
  • Hash-based sharding on user ID — distributes load evenly across shards; queries for “all redemptions by user X” are single-shard.
  • Composite: hash on user ID, then time-partition within each shard — combines both benefits at the cost of query planning complexity.

13.3 Consistency and CAP Trade-offs

  • Redemption records = strong consistency (financial data, can’t double-apply a discount, better to briefly fail closed than to allow a duplicate) — CP by CAP terms.
  • Velocity counters = eventual consistency acceptable (a brief window of stale counter data is much better than blocking every request during a network partition) — AP by CAP terms.
  • Entity graph = eventual consistency acceptable (new edges appearing slightly late is a minor precision loss, not a correctness violation).
  • ML features = eventual consistency, snapshotted (features used for training must be reproducible; features used for inference can lag by seconds).
14

APIs, Microservices and Contracts

The service boundaries, protocols and message shapes that make the system operable.

14.1 Why Microservices Here

The Coupon Service, Fraud Detection Service, Device Fingerprint Service, Rules Engine, and ML Risk Service each have different scaling characteristics, deployment cadences, and skill-set ownership. Splitting them into independently deployable services allows each team to move at its own pace without coordination overhead, at the cost of network calls and the need for clear contracts.

14.2 The Primary Public Contract

HTTP request — risk assessment call
POST /v1/fraud/risk-assessment
Content-Type: application/json
Authorization: Bearer <service-jwt>

{
  "requestId":       "rq_2f8a5b3c9d",
  "userId":          "u_9182736",
  "accountAgeSec":   35,
  "couponCode":      "WELCOME20",
  "couponValueCents": 2000,
  "deviceFingerprint": "fp_abc123def456",
  "ipAddressHash":   "sha256:7e1c...",
  "orderTotalCents": 4599,
  "paymentInstrumentHash": "sha256:9b2a...",
  "timestamp":       "2025-10-14T09:12:31Z"
}
HTTP response — risk decision
200 OK
Content-Type: application/json

{
  "requestId":    "rq_2f8a5b3c9d",
  "decision":     "APPROVE",
  "riskScore":    0.14,
  "reasonCodes":  ["low_velocity", "clean_device"],
  "modelVersion": "risk_gbm_v37",
  "ttlMs":        250
}
Java — Spring Boot controller exposing the assessment endpoint
@RestController
@RequestMapping("/v1/fraud")
public class RiskAssessmentController {

    private final FraudDecisionOrchestrator orchestrator;
    private final RiskDecisionMetrics metrics;

    @PostMapping("/risk-assessment")
    public ResponseEntity<RiskDecisionResponse> assess(@Valid @RequestBody RiskRequest req) {
        long start = System.nanoTime();
        try {
            RiskDecision decision = orchestrator.evaluate(RedemptionRequest.from(req));
            metrics.recordDecision(decision, System.nanoTime() - start);
            return ResponseEntity.ok(RiskDecisionResponse.from(req.getRequestId(), decision));
        } catch (RuntimeException ex) {
            metrics.recordFailure(System.nanoTime() - start);
            return ResponseEntity.ok(RiskDecisionResponse.stepUp(req.getRequestId(),
                    "orchestrator_error"));
        }
    }
}

14.3 API Design Principles Applied

  • Idempotency: the requestId is client-supplied and stable across retries so the same physical request retried after a timeout won’t create duplicate downstream side effects.
  • Versioning: /v1/ in the path makes future breaking changes safe to roll out alongside existing consumers.
  • Explicit contracts: fields have documented types and units (e.g. couponValueCents, not ambiguous couponValue).
  • Reason codes over free-text: reasonCodes is a defined enum, so upstream systems can programmatically react without brittle string parsing.
  • Explicit TTL: ttlMs lets the caller safely cache a decision for a very brief window during retries.

14.4 Protocol Choice — REST vs. gRPC

For external client APIs (mobile, web), JSON over HTTPS (REST) remains standard due to universal client tooling and cache-friendliness. For internal, high-volume, latency-sensitive service-to-service calls (Coupon ↔ Fraud, Fraud ↔ ML Risk), gRPC is a very common upgrade — strongly-typed protobuf contracts, binary-encoded payloads, HTTP/2 multiplexing, and bidirectional streaming all reduce serialisation overhead and add strong compile-time contract checking, at the cost of poorer human debuggability than JSON.

14.5 Rate Limiting and Throttling as First-Class Contracts

  • Per-user token bucket at the API Gateway for the checkout / redemption endpoint, sized to comfortably accommodate legitimate retry behaviour but tight enough to blunt brute-force coupon guessing.
  • Per-IP sliding window limits for signup and login endpoints.
  • Standard rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After) on every response.
  • Distinct error semantics for 429 vs. 403 so legitimate clients see “too many requests” whereas blocked clients see a generic authorisation-style error.
Java — simple in-memory token-bucket rate limiter (per-key)
public class TokenBucketRateLimiter {

    private final int capacity;
    private final double refillPerSecond;
    private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();

    public boolean tryAcquire(String key) {
        Bucket b = buckets.computeIfAbsent(key, k -> new Bucket(capacity));
        synchronized (b) {
            long now = System.nanoTime();
            double refill = ((now - b.lastRefillNs) / 1_000_000_000.0) * refillPerSecond;
            b.tokens = Math.min(capacity, b.tokens + refill);
            b.lastRefillNs = now;
            if (b.tokens >= 1.0) {
                b.tokens -= 1.0;
                return true;
            }
            return false;
        }
    }

    public TokenBucketRateLimiter(int capacity, double refillPerSecond) {
        this.capacity = capacity;
        this.refillPerSecond = refillPerSecond;
    }

    private static class Bucket {
        double tokens; long lastRefillNs;
        Bucket(int cap) { this.tokens = cap; this.lastRefillNs = System.nanoTime(); }
    }
}

In production, a distributed rate limiter backed by Redis (using atomic Lua scripts to update per-key counters across all API Gateway instances) is preferred over the in-memory version above so that limits apply globally to a user, not just per gateway instance the request happens to hit.

15

Patterns and Anti-patterns

The design shapes that repeatedly work, and the ones that repeatedly bite.

15.1 Patterns That Work

Pattern

Layered Defence

Cheap deterministic checks first (blocklist, rules), expensive probabilistic checks (ML) only for what survives. Keeps average latency and cost low.

Pattern

Sync + Async Twin Path

Fast decision on the hot path; deeper, graph-based, retroactive analysis on the async path. Neither alone catches everything.

Pattern

Fail-Open With Friction

When systems degrade, add friction (OTP) rather than binary block/allow. Preserves availability without blindly permitting fraud.

Pattern

Event Sourcing for Audit

Every risk decision emitted to Kafka as an immutable event, enabling replay, retraining, and audit without instrumenting each service manually.

Pattern

Circuit Breakers Everywhere

Any downstream can fail; every caller has a timeout, retry budget, and a safe fallback score to avoid cascade failures.

Pattern

Shadow-Mode Deployments

New rules and models run silently against real traffic before enforcement, catching regressions before they impact customers.

15.2 Anti-patterns to Avoid

Anti-patterns

  • Only rules, no model: brittle, easily gamed by fraudsters who probe rule thresholds systematically.
  • Only ML, no rules: opaque to fraud analysts, slow to react to a novel attack that’s obvious to a human but unseen by the model.
  • Fully synchronous fraud decision blocking checkout: any latency in the fraud service becomes checkout latency; no fallback path.
  • Storing everything in one big relational DB: velocity counters, graph edges, and events all in Postgres tables — works until scale then collapses hard.
  • Leaking rule internals to clients: verbose error messages hand fraudsters a free test harness.
  • “It works in staging”: staging fraud rules rarely match production reality; without shadow-mode + canary you’re rolling dice.
  • No feedback loop from labels to retraining: model degrades over weeks and months as fraud tactics evolve.
  • Manual review as the only line of defence: fraud volume scales faster than headcount; without ML pre-filtering, human reviewers drown.
Real-World Pitfall

Teams sometimes build an elaborate ML pipeline for fraud detection but never close the label loop — confirmed fraud from manual reviews and chargebacks never makes it back into training data. The model quietly gets worse every month as attackers adapt, and no one notices until block rates or losses spike.

16

Best Practices and Common Mistakes

The habits mature teams build up, and the traps they still occasionally step into.

16.1 Best Practices

  • Instrument every decision with the full feature vector. When a legitimate customer complains, you must be able to reconstruct exactly why the system decided as it did.
  • Define an explicit false-positive budget. “We accept up to X% false positives to catch Y% of fraud” is a business decision, not an ML metric — put a number on it and track it.
  • Build the appeal flow before you build strict blocking. Blocked customers need a fast, humane path back in; without it, false positives become churn and support cost.
  • Rate-limit the appeal flow itself. Otherwise it becomes another abuse vector.
  • Version everything. Rules, thresholds, ML models, feature schemas — every deploy should be traceable to a specific version.
  • Practise failing over. Regular game days where you deliberately kill Redis, ML inference, or a database and verify the system degrades gracefully rather than catastrophically.
  • Treat fraud data as first-class product. Datasets, labels, and dashboards deserve the same engineering rigour as customer-facing features.

16.2 Common Mistakes

MistakeConsequenceBetter Approach
No shadow mode before enforcementSilent regressions blocking real customersRun new rules in parallel, log-only, for at least a week
Client-side coupon validationTrivially bypassable via API tamperingAll validation server-side, treating client as adversarial
Reusing sequential coupon codesAttackers script through the whole spaceHigh-entropy random codes with rate limits on attempts
Overly verbose block error messagesAttackers learn thresholds for freeGeneric user-facing errors, detailed internal logs
No graph feature at allMiss coordinated multi-account abuse ringsAt minimum, count linked accounts per device / card / IP
Ignoring device rotation costFraudsters trivially bypass simple UA/IP checksBehavioural + device fingerprinting + payment-instrument reuse
No async re-scoring pipelineFraud only visible with hindsight goes uncaughtKafka-driven batch job that re-scores every N hours

16.3 Sharing Signals — Cross-Company Consortiums

One of the most cost-effective defence multipliers is participation in a fraud-signal consortium — an industry-shared pool where members contribute hashes of confirmed-fraud device fingerprints, payment instruments, or accounts, and can query in real time whether a new signal has been previously flagged elsewhere. This turns local, siloed fraud detection into a network effect: a fraud ring that hits a competitor last week can be blocked at your door today.

  • Signals shared are typically hashed identifiers plus a confidence label, not personally identifiable data.
  • Reciprocity is enforced — you contribute in order to consume — which encourages honest, high-quality signal contribution.
  • Consortiums are complementary to, not a replacement for, your own internal detection; they are strongest for known-fraud lookups and weakest for novel attacks unique to your platform.
Practical Wisdom

Track two independent numbers on your dashboard forever: “fraud we caught” and “fraud we missed but discovered later.” The ratio between them — and its trend over time — is the single most honest measure of whether the system is genuinely improving or just appearing to.

17

Industry Examples and Composite Walkthrough

How major platforms have publicly discussed their approach, and one end-to-end walk-through combining the lessons.

17.1 Industry Perspectives

  • Ride-sharing referral fraud programs have publicly discussed rules + ML hybrid systems built on real-time streaming, with device fingerprinting and graph analysis to detect fake driver-rider referral loops that inflate signup bonuses.
  • Food delivery platforms have written about handling promotional code abuse during marketing campaigns, using velocity checks + ML scoring in the critical path, plus async batch review of suspicious redemptions.
  • E-commerce marketplaces have publicly detailed their fraud engines combining rules, ML, and human review, with shared model infrastructure across many fraud types (payment, refund, review, coupon abuse) rather than one system per fraud type.
  • Streaming and subscription platforms have described free-trial abuse patterns very similar in structure to coupon abuse — new-account velocity, device linkage, payment reuse — solved with essentially the same architecture.
i
Common Thread

Across companies and industries, the winning shape is always the same: a fast, layered, hybrid rules-plus-ML decision engine on the hot path, a durable event stream feeding an async graph-based re-scoring pipeline, and a tight feedback loop from labelled outcomes back into retraining.

17.2 Composite Walkthrough — A Single Attack Story

1

Setup

An attacker in a data centre spins up 200 emulated Android devices, each with a fresh randomised device fingerprint, connected via a rotating residential-proxy service. They plan to farm 200 × \$20 = \$4,000 of “WELCOME20” first-order discounts.

2

Signup 1

The first emulated device signs up. Nothing is on the blocklist yet, velocity counters are all zero for this fingerprint. Rules pass. The ML model gives a moderate score (0.42) — hosting-provider IP + brand new device + immediate coupon redemption is suspicious but not obviously fraudulent alone. Decision: step-up, request OTP.

3

The Attacker Adapts

OTP defeats the emulator setup because the attacker only controls disposable virtual numbers, some of which fail delivery. Success rate on this path is only ~40%.

4

Volume Fingerprint Emerges

Over the next 30 minutes, 80 successful signups reach the async re-scoring pipeline. Kafka consumers update the entity graph. The graph service now sees a cluster of 80 “new” accounts sharing very similar behavioural patterns and 3 payment card fingerprints, all through the same ASN range.

5

Feature Drift Detected

The monitoring system flags a sudden spike in the “WELCOME20 redemption rate” business metric and an unusual distribution shift in the ASN feature. An on-call analyst is paged.

6

Rules Engine Update

The analyst pushes a new rule via the feature-flag system: “Any signup with fingerprint entropy < X and ASN in this hosting range = auto-BLOCK.” The rule rolls out globally in seconds, no redeploy required.

7

Clawback

The async graph re-scoring job flags the 80 already-completed redemptions as high-confidence fraud (cluster score 0.94). Marketing budget attribution is corrected, the accounts are frozen, and the coupon usage counters are adjusted so the “first 500 winners” cap isn’t artificially inflated by the fraud attempts.

8

Feedback Loop

All 80 confirmed-fraud redemptions become labelled positive examples in the next nightly retraining dataset. The next model version scores this exact attacker signature 0.91 out of the box, not 0.42.

9

Attacker Retries

The attacker returns two days later with rotated fingerprints. New signup: ASN + behavioural + payment-instrument reuse patterns still resemble the labelled fraud cluster. Model score 0.87. Rules engine hard-block. Attack neutralised without human intervention.

40%Attacker OTP success
80Fraud accounts clawed back
< 30 minTime to rule deployment
0.87Next-cycle model score

This is what “defence in depth” actually looks like in production: no single component (rules, model, graph, monitoring, human) catches the attack alone, but together they contain financial damage to a small first wave, learn from that wave, and are strictly better prepared for the second attempt.

18

Frequently Asked Questions

The questions engineers, product managers and interviewers most often ask about this system.

Q1. Isn’t all this overkill for a small business running a few coupons a year?

For a very small operation, absolutely — a couple of well-chosen rules (“one code per email address”, “expire after 30 days”) plus manual review of anomalies is enough. This full architecture becomes justified once promotional spend is large enough that a few percent of leakage is a real dollar figure and traffic is large enough that manual review can’t keep up. You can start with just the rules engine and Redis counters, and add the ML and graph pieces incrementally as scale grows.

Q2. Doesn’t adding friction (OTP, review) hurt conversion more than fraud costs?

It can, if applied too broadly. The right approach is to reserve friction for actually risky requests — ideally low single-digit percentages of traffic, not blanket friction — using the risk score to target it. A well-tuned system typically improves overall margin because it reduces both direct fraud losses and the noise in growth metrics that comes from fake signups.

Q3. How do you handle legitimate power users who look statistically like fraudsters?

This is why graded risk scoring plus friction (not hard-block) matters. A well-known frequent buyer redeeming a lot of coupons is probably safe by prior history and stable device/payment identity. When in doubt, friction (OTP) is nearly free for a real customer and painful for a fraud farm.

Q4. How often should the ML model be retrained?

Depends on how quickly fraud patterns shift, but a common cadence is weekly for the primary model, with continuous online monitoring for drift so an unusual pattern can trigger an ad-hoc retrain sooner. Some teams use online learning approaches that update model weights incrementally in near-real-time, at higher operational complexity.

Q5. What if the ML risk service is down entirely?

The Fraud Detection Service uses a circuit breaker on the ML client. When it trips, requests fall back to a rules-only decision path with a documented, slightly widened threshold that leans toward friction rather than allow, and alerts fire immediately.

Q6. How is a “device fingerprint” not just an IP address plus User-Agent?

Naively yes, but IPs rotate and UAs are trivial to spoof. A production fingerprint typically combines many stable-ish signals: canvas rendering quirks, WebGL / audio hardware quirks, installed fonts, screen resolution, timezone, subtle timing behaviours — each individually not unique, but collectively surprisingly identifying.

Q7. What’s the difference between fraud detection and anti-fraud rules that everyone already has?

“Rules” refers to the deterministic, human-authored layer — “if X and Y then block.” A full fraud detection system is that plus streaming signals (device, velocity, graph), an ML risk model, async re-scoring, feedback loops from labels, and observability.

Q8. Is the same architecture reusable for other fraud types?

Very much so — the majority of the design (streaming event bus, feature store, risk scoring service, entity graph, async re-scoring) generalises. What changes per fraud type is the specific rules, model features, and thresholds.

Q9. How do you handle the “cold start” problem for new users?

Lean more heavily on device and payment-instrument signals (which don’t need account history), use account-registration signals (email pattern, phone number reputation, referral source quality), and set default risk slightly higher for cold-start users but resolve it toward friction rather than block.

Q10. How do you avoid discriminating against legitimate customers based on protected attributes?

Never feed race, gender, national origin, or other protected attributes directly into the model. Regularly test the model’s decision distribution across proxies for protected groups (e.g. postcode-based) and adjust features and thresholds when disparate impact is found.

Q11. How do you tell fraud from a genuine customer service edge case?

You can’t always, in the moment. That’s why the appeal flow matters — giving customers a fast, cheap path to human review turns most false positives from lost customers into a customer service ticket and (crucially) into labelled training data marking that specific pattern as “not fraud.”

Q12. How is this different from payment fraud detection?

The architectural pattern is nearly identical — streaming events, layered rules-plus-ML scoring, async re-scoring, feedback loops. Coupon abuse tends to focus more on new-account velocity and device linkage; payment fraud focuses more on card-present-vs-not-present signals, address-verification results, and chargeback history.

Q13. Should we buy a third-party fraud service instead of building this?

Depends on scale, differentiation, and data flow constraints. Third-party services get you 70–80% of the way with far less engineering effort. Large companies with distinctive user behaviour and enough data to train specialised models often outperform generic third parties on their own turf. Many mature teams use both: a third-party service as one input signal alongside their own detection.

Q14. How is bot detection different from coupon abuse detection?

Bot detection is a broader class of “is this a script or a human” problem, and it’s an input to coupon abuse detection: knowing a request is likely a bot is a strong feature in the fraud model.

Q15. How do we prevent our own employees from abusing the coupon system?

Same core defences (rules, ML, review) apply, but with two additions: employee accounts often have distinct signals (corporate email domains, VPN egress IPs, staff device fingerprints) that the model can learn as either “always allow” or “always require additional scrutiny,” and separate audit logging with periodic offline analysis catches slow-drip abuse over months.

19

Summary and Key Takeaways

The distilled essentials to walk away with.

Designing a coupon and promo code abuse detection system isn’t a single ML model, or a set of clever rules, or a graph analysis job — it’s the deliberate composition of all of them, wrapped in careful attention to latency budgets, fail-open vs. fail-closed decisions, and the continuous feedback loop that keeps the whole system improving as adversaries evolve.

“Fraud detection is a moving target: every model, rule, and threshold decays the moment it ships. The system that wins isn’t the smartest today — it’s the one designed to learn fastest tomorrow.”

Key Takeaways

  • Layer your defence: cheap deterministic rules first, expensive ML models second, retroactive graph analysis third. Never rely on a single technique.
  • Fraud detection lives on the checkout critical path. Budget under ~100 ms at p99, use parallel fan-out, always have circuit-breaker fallbacks so a downstream failure never blocks a legitimate purchase.
  • Synchronous + asynchronous is not optional. The sync path stops the obvious; the async path catches what only becomes obvious in aggregate, over time, across accounts.
  • Choose the right store per data type. Redis for hot counters, sharded relational for redemption records, graph DB for entity relationships, Kafka for streaming events, warehouse for training data.
  • Fail-open with friction beats binary fail-open or fail-closed. When systems degrade, add OTP-style verification rather than either blocking every user or permitting every user.
  • Instrument every decision. Feature vectors, model versions, rule flags, and reason codes must be reconstructable months later, both for customer support and for retraining data.
  • Close the feedback loop. Confirmed-fraud labels from manual review and chargebacks must flow back into training, or the model degrades in silence.
  • Deploy with shadow mode, canary, feature flags, and automated rollback. A single bad rule can block millions in revenue before anyone notices — risk-graduated rollout is not optional at real scale.
  • Design for adversarial evolution. Rules and thresholds are secrets, not documentation. Verbose error messages hand attackers a free test harness.
  • The same architecture generalises across coupon abuse, referral fraud, refund abuse, review farming, and free-trial abuse. Build a fraud platform, not a fraud silo.

Whether you are preparing for a system design interview, sizing a real fraud program, or evaluating a third-party service, the mental model that matters is: fast layered decisions on the hot path, patient graph-based re-analysis on the async path, and a tight, honest feedback loop between them. Everything else — the specific storage tech, cloud provider, model architecture — is negotiable detail on top of that unchanging shape.

Leave a Reply

Your email address will not be published. Required fields are marked *