Machine Coding Problem

Coupon Management System

maco60macoAllcommercerule-enginestacking-logic
Commonly Asked By:AmazonWalmartShopify

Functional Scope (In-Scope)

  • Dynamic Coupon Rules Engine: Evaluate discount coupons (Percentage, Flat, BOGO) using composite boolean checks (AND predicates).
  • Contextual Applicability Predicates: Validate conditions such as minimum order value limits, eligible categories, and user segmentation profiles using the Specification Pattern.
  • Stacking Policy Managers: Restrict cart items to exclusive coupons or apply stacking guidelines (Additive vs Best Only).
  • Atomic Usage Decrements: Protect coupon pools with atomic, thread-safe counter decrements during concurrent checkout processes.

Explicit Boundaries (Out-of-Scope)

  • No Third-Party Banking Ledger Settlements: Excludes live payment processor balance checks or external credit card settlements.
  • No Automatic AI Fraud Detection Models: Bypasses complex pattern matching (ML models) to identify checkout bots or duplicate account usage.

Clean reference designs demonstrating atomic discount checkouts in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

enum DiscountType {
    FLAT,
    PERCENTAGE,
    BOGO
}

// Context containing order details
class OrderContext {
    private final String orderId;
    private final String userId;
    private final double totalAmount;
    private final String category;
    private final String userSegment;

    public OrderContext(String orderId, String userId, double totalAmount, String category, String userSegment) {
        this.orderId = orderId;
        this.userId = userId;
        this.totalAmount = totalAmount;
        this.category = category;
        this.userSegment = userSegment;
    }

    public String getOrderId() { return orderId; }
    public String getUserId() { return userId; }
    public double getTotalAmount() { return totalAmount; }
    public String getCategory() { return category; }
    public String getUserSegment() { return userSegment; }
}

// Specification Pattern for Applicability Rules
interface CouponSpecification {
    boolean isSatisfiedBy(OrderContext context);
}

class MinAmountSpecification implements CouponSpecification {
    private final double minAmount;
    public MinAmountSpecification(double minAmount) { this.minAmount = minAmount; }
    @Override
    public boolean isSatisfiedBy(OrderContext context) {
        return context.getTotalAmount() >= minAmount;
    }
}

class CategorySpecification implements CouponSpecification {
    private final String allowedCategory;
    public CategorySpecification(String allowedCategory) { this.allowedCategory = allowedCategory; }
    @Override
    public boolean isSatisfiedBy(OrderContext context) {
        return context.getCategory().equalsIgnoreCase(allowedCategory);
    }
}

class UserSegmentSpecification implements CouponSpecification {
    private final String requiredSegment;
    public UserSegmentSpecification(String requiredSegment) { this.requiredSegment = requiredSegment; }
    @Override
    public boolean isSatisfiedBy(OrderContext context) {
        return context.getUserSegment().equalsIgnoreCase(requiredSegment);
    }
}

class AndSpecification implements CouponSpecification {
    private final List<CouponSpecification> specs = new ArrayList<>();
    public AndSpecification(CouponSpecification... specifications) {
        specs.addAll(Arrays.asList(specifications));
    }
    @Override
    public boolean isSatisfiedBy(OrderContext context) {
        for (CouponSpecification spec : specs) {
            if (!spec.isSatisfiedBy(context)) return false;
        }
        return true;
    }
}

// Coupon Entity
class Coupon {
    private final String code;
    private final DiscountType discountType;
    private final double discountValue;
    private final CouponSpecification specification;
    private final AtomicInteger remainingUsage;
    private final int maxUsage;

    public Coupon(String code, DiscountType discountType, double discountValue, CouponSpecification specification, int maxUsage) {
        this.code = code;
        this.discountType = discountType;
        this.discountValue = discountValue;
        this.specification = specification;
        this.maxUsage = maxUsage;
        this.remainingUsage = new AtomicInteger(maxUsage);
    }

    public String getCode() { return code; }
    public DiscountType getDiscountType() { return discountType; }
    public double getDiscountValue() { return discountValue; }
    public CouponSpecification getSpecification() { return specification; }
    public int getRemainingUsage() { return remainingUsage.get(); }

    public boolean isApplicable(OrderContext context) {
        return specification.isSatisfiedBy(context);
    }

    public boolean tryRedeem() {
        while (true) {
            int current = remainingUsage.get();
            if (current <= 0) return false;
            if (remainingUsage.compareAndSet(current, current - 1)) {
                return true;
            }
        }
    }

    public void release() {
        while (true) {
            int current = remainingUsage.get();
            if (current >= maxUsage) return;
            if (remainingUsage.compareAndSet(current, current + 1)) {
                return;
            }
        }
    }

    public double calculateDiscount(double amount) {
        switch (discountType) {
            case FLAT:
                return Math.min(amount, discountValue);
            case PERCENTAGE:
                return amount * (discountValue / 100.0);
            case BOGO:
                // Buy One Get One style - represented as 50% discount for simplicity or custom calculation
                return amount * 0.5;
            default:
                return 0.0;
        }
    }
}

// Strategy Pattern for Coupon Stacking Rules
interface StackingStrategy {
    double calculateFinalDiscount(List<Coupon> coupons, OrderContext context);
}

class ExclusiveStackingStrategy implements StackingStrategy {
    @Override
    public double calculateFinalDiscount(List<Coupon> coupons, OrderContext context) {
        if (coupons.isEmpty()) return 0.0;
        if (coupons.size() > 1) {
            throw new IllegalArgumentException("Exclusive policy violated: multiple coupons not allowed.");
        }
        Coupon coupon = coupons.get(0);
        return coupon.isApplicable(context) ? coupon.calculateDiscount(context.getTotalAmount()) : 0.0;
    }
}

class AdditiveStackingStrategy implements StackingStrategy {
    @Override
    public double calculateFinalDiscount(List<Coupon> coupons, OrderContext context) {
        double currentTotal = context.getTotalAmount();
        double totalDiscount = 0.0;
        for (Coupon coupon : coupons) {
            if (coupon.isApplicable(context)) {
                double discount = coupon.calculateDiscount(currentTotal);
                totalDiscount += discount;
                currentTotal = Math.max(0, currentTotal - discount);
            }
        }
        return totalDiscount;
    }
}

class BestOnlyStackingStrategy implements StackingStrategy {
    @Override
    public double calculateFinalDiscount(List<Coupon> coupons, OrderContext context) {
        double bestDiscount = 0.0;
        for (Coupon coupon : coupons) {
            if (coupon.isApplicable(context)) {
                double discount = coupon.calculateDiscount(context.getTotalAmount());
                bestDiscount = Math.max(bestDiscount, discount);
            }
        }
        return bestDiscount;
    }
}

// Central Coupon Manager
class CouponManager {
    private final Map<String, Coupon> couponRegistry = new ConcurrentHashMap<>();

    public void addCoupon(Coupon coupon) {
        couponRegistry.put(coupon.getCode().toUpperCase(), coupon);
    }

    public Coupon getCoupon(String code) {
        return couponRegistry.get(code.toUpperCase());
    }

    public boolean redeemCoupons(List<String> codes, OrderContext context, StackingStrategy strategy) {
        List<Coupon> couponsToApply = new ArrayList<>();
        for (String code : codes) {
            Coupon coupon = getCoupon(code);
            if (coupon == null || !coupon.isApplicable(context)) {
                System.out.println("Coupon " + code + " is either invalid or not applicable for Order " + context.getOrderId());
                return false;
            }
            couponsToApply.add(coupon);
        }

        // Validate final discount computation succeeds under current strategy
        try {
            double finalDiscount = strategy.calculateFinalDiscount(couponsToApply, context);
            System.out.println("Computed final discount: $" + String.format("%.2f", finalDiscount) + " for Order " + context.getOrderId());
        } catch (IllegalArgumentException e) {
            System.out.println("Checkout failed coupon stacking check: " + e.getMessage());
            return false;
        }

        // Try atomic redemption
        List<Coupon> successfullyRedeemed = new ArrayList<>();
        boolean success = true;

        for (Coupon coupon : couponsToApply) {
            if (coupon.tryRedeem()) {
                successfullyRedeemed.add(coupon);
            } else {
                success = false;
                break;
            }
        }

        // Rollback if any redemption failed
        if (!success) {
            System.out.println("Checkout failed: One or more coupons exceeded usage limit. Rolling back.");
            for (Coupon coupon : successfullyRedeemed) {
                coupon.release();
            }
            return false;
        }

        System.out.println("Order " + context.getOrderId() + " successfully redeemed coupons: " + codes);
        return true;
    }

    public void releaseCoupons(List<String> codes) {
        for (String code : codes) {
            Coupon coupon = getCoupon(code);
            if (coupon != null) {
                coupon.release();
                System.out.println("Released usage lock for coupon: " + code);
            }
        }
    }
}

// Execution Test Driver
public class Main {
    public static void main(String[] args) throws InterruptedException {
        CouponManager manager = new CouponManager();

        // 1. Register diverse coupons with complex Specifications
        Coupon specCoupon1 = new Coupon("SAVE20", DiscountType.PERCENTAGE, 20.0,
            new AndSpecification(
                new MinAmountSpecification(100.0),
                new UserSegmentSpecification("VIP")
            ), 5); // Max usage 5

        Coupon specCoupon2 = new Coupon("FLAT15", DiscountType.FLAT, 15.0,
            new CategorySpecification("Electronics"), 10);

        Coupon specCoupon3 = new Coupon("EXCLUSIVE50", DiscountType.PERCENTAGE, 50.0,
            new MinAmountSpecification(200.0), 1); // Flash sale single coupon limit

        manager.addCoupon(specCoupon1);
        manager.addCoupon(specCoupon2);
        manager.addCoupon(specCoupon3);

        // 2. Validate Order Applicability
        OrderContext context1 = new OrderContext("O-100", "U-VIP", 150.0, "Electronics", "VIP");
        System.out.println("Is SAVE20 applicable for VIP purchasing Electronics ($150)? " + specCoupon1.isApplicable(context1));
        System.out.println("Is FLAT15 applicable for VIP purchasing Electronics ($150)? " + specCoupon2.isApplicable(context1));

        // 3. Stacking Policy Application
        List<Coupon> currentApplied = Arrays.asList(specCoupon1, specCoupon2);
        StackingStrategy additive = new AdditiveStackingStrategy();
        double discountVal = additive.calculateFinalDiscount(currentApplied, context1);
        System.out.println("Combined Additive discount (SAVE20 + FLAT15): $" + discountVal);

        // 4. Concurrent Redemption Test simulation
        ExecutorService executor = Executors.newFixedThreadPool(4);
        CountDownLatch latch = new CountDownLatch(4);

        System.out.println("\n--- Simulating 4 concurrent checkouts for a single limit exclusive coupon (EXCLUSIVE50) ---");
        for (int i = 0; i < 4; i++) {
            final int id = i;
            executor.submit(() -> {
                OrderContext order = new OrderContext("O-CONC-" + id, "U-" + id, 300.0, "Electronics", "Regular");
                boolean status = manager.redeemCoupons(Collections.singletonList("EXCLUSIVE50"), order, new ExclusiveStackingStrategy());
                System.out.println("Checkout " + id + " success: " + status);
                latch.countDown();
            });
        }

        latch.await();
        executor.shutdown();

        System.out.println("Final EXCLUSIVE50 remaining usage count: " + specCoupon3.getRemainingUsage());
    }
}