Machine Coding Problem

Discount Engine

macoAllcommercestacking-policyatomic-usage
Commonly Asked By:AmazonWalmartPayPal

Functional Specifications

  • Dynamic Promotion Applicability: Computes multi-level discounts including Percentage off, Flat Amount off, Free Shipping, and Buy One Get One (BOGO) deals.
  • Smart Stacking Policy Resolver: Supports resolving discounts with EXCLUSIVE rules (only best promo), ADDITIVE rules (stack all matching), and BEST_DEAL optimization.
  • Atomic Usage Safeguards: Locks global promotion pools and tracks per-user redemption limits atomically, ensuring thread safety and automatic recovery on transaction failure.

Operational Constraints

  • Thread Concurrency: Must handle thousands of checkouts per second without race conditions or negative limit overflows.
  • Atomic Payment Rollbacks: In the event of a checkout checkout failures, previously claimed usage counters must be perfectly rolled back.

Production reference implementations showcasing polymorphic discount engines, stacking resolution logic, and lock releases:

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

enum PromotionType {
    PERCENTAGE, FLAT_OFF, FREE_SHIPPING, BOGO
}

enum StackingGroup {
    CART_TOTAL, ITEM_LEVEL, SHIPPING
}

enum StackingPolicy {
    EXCLUSIVE,    // Only one exclusive promotion can be applied (usually the best one)
    ADDITIVE,     // All matching promotions can stack
    BEST_DEAL     // Automatically calculate the single highest discount combination
}

class OrderItem {
    private final String itemId;
    private final String category;
    private final double unitPrice;
    private final int quantity;

    public OrderItem(String itemId, String category, double unitPrice, int quantity) {
        this.itemId = itemId;
        this.category = category;
        this.unitPrice = unitPrice;
        this.quantity = quantity;
    }

    public String getItemId() { return itemId; }
    public String getCategory() { return category; }
    public double getUnitPrice() { return unitPrice; }
    public int getQuantity() { return quantity; }
    public double getTotalPrice() { return unitPrice * quantity; }
}

class OrderContext {
    private final String orderId;
    private final String userId;
    private final List<OrderItem> items;
    private final double shippingCost;

    public OrderContext(String orderId, String userId, List<OrderItem> items, double shippingCost) {
        this.orderId = orderId;
        this.userId = userId;
        this.items = items;
        this.shippingCost = shippingCost;
    }

    public String getOrderId() { return orderId; }
    public String getUserId() { return userId; }
    public List<OrderItem> getItems() { return items; }
    public double getShippingCost() { return shippingCost; }
    
    public double getCartSubtotal() {
        return items.stream().mapToDouble(OrderItem::getTotalPrice).sum();
    }
}

class Promotion {
    private final String id;
    private final String code;
    private final PromotionType type;
    private final double value; // Percentage rate (e.g. 0.1) or Flat amount
    private final String targetCategory; // Optional category filter
    private final double minSpendRequirement;
    private final StackingGroup stackingGroup;
    private final boolean isExclusive;
    private final AtomicInteger globalUsageLimit;

    public Promotion(String id, String code, PromotionType type, double value, 
                     String targetCategory, double minSpend, StackingGroup group, 
                     boolean isExclusive, int limit) {
        this.id = id;
        this.code = code;
        this.type = type;
        this.value = value;
        this.targetCategory = targetCategory;
        this.minSpendRequirement = minSpend;
        this.stackingGroup = group;
        this.isExclusive = isExclusive;
        this.globalUsageLimit = new AtomicInteger(limit);
    }

    public String getId() { return id; }
    public String getCode() { return code; }
    public PromotionType getType() { return type; }
    public double getValue() { return value; }
    public String getTargetCategory() { return targetCategory; }
    public double getMinSpendRequirement() { return minSpendRequirement; }
    public StackingGroup getStackingGroup() { return stackingGroup; }
    public boolean isExclusive() { return isExclusive; }
    
    public int getRemainingLimit() { return globalUsageLimit.get(); }
    
    public boolean tryAcquireUsage() {
        while (true) {
            int current = globalUsageLimit.get();
            if (current <= 0) return false;
            if (globalUsageLimit.compareAndSet(current, current - 1)) {
                return true;
            }
        }
    }

    public void releaseUsage() {
        globalUsageLimit.incrementAndGet();
    }
}

class DiscountResult {
    private final Promotion promotion;
    private final double discountAmount;
    private final String description;

    public DiscountResult(Promotion promotion, double discountAmount, String description) {
        this.promotion = promotion;
        this.discountAmount = discountAmount;
        this.description = description;
    }

    public Promotion getPromotion() { return promotion; }
    public double getDiscountAmount() { return discountAmount; }
    public String getDescription() { return description; }
}

interface DiscountStrategy {
    DiscountResult calculateDiscount(OrderContext order, Promotion promo, double currentSubtotal);
}

class PercentageDiscountStrategy implements DiscountStrategy {
    @Override
    public DiscountResult calculateDiscount(OrderContext order, Promotion promo, double currentSubtotal) {
        if (currentSubtotal < promo.getMinSpendRequirement()) {
            return new DiscountResult(promo, 0.0, "Min spend requirement not met");
        }
        
        double discount = 0.0;
        if (promo.getTargetCategory() != null) {
            double categoryTotal = order.getItems().stream()
                .filter(i -> i.getCategory().equalsIgnoreCase(promo.getTargetCategory()))
                .mapToDouble(OrderItem::getTotalPrice).sum();
            discount = categoryTotal * promo.getValue();
        } else {
            discount = currentSubtotal * promo.getValue();
        }
        return new DiscountResult(promo, discount, String.format("%.0f%% discount applied", promo.getValue() * 100));
    }
}

class FlatDiscountStrategy implements DiscountStrategy {
    @Override
    public DiscountResult calculateDiscount(OrderContext order, Promotion promo, double currentSubtotal) {
        if (currentSubtotal < promo.getMinSpendRequirement()) {
            return new DiscountResult(promo, 0.0, "Min spend requirement not met");
        }
        double discount = Math.min(currentSubtotal, promo.getValue());
        return new DiscountResult(promo, discount, String.format("Flat $%.2f off total", promo.getValue()));
    }
}

class FreeShippingStrategy implements DiscountStrategy {
    @Override
    public DiscountResult calculateDiscount(OrderContext order, Promotion promo, double currentSubtotal) {
        if (currentSubtotal < promo.getMinSpendRequirement()) {
            return new DiscountResult(promo, 0.0, "Min spend requirement not met");
        }
        return new DiscountResult(promo, order.getShippingCost(), "Free Shipping Applied");
    }
}

class BogoStrategy implements DiscountStrategy {
    @Override
    public DiscountResult calculateDiscount(OrderContext order, Promotion promo, double currentSubtotal) {
        double discount = 0.0;
        for (OrderItem item : order.getItems()) {
            if (promo.getTargetCategory() == null || item.getCategory().equalsIgnoreCase(promo.getTargetCategory())) {
                int freePairs = item.getQuantity() / 2;
                discount += freePairs * item.getUnitPrice();
            }
        }
        return new DiscountResult(promo, discount, "BOGO Category Promotion Applied");
    }
}

class UsageGuard {
    private final Map<String, Set<String>> userPromoRedemptions = new ConcurrentHashMap<>();

    public boolean checkAndTrackUserLimit(String userId, String promoId) {
        Set<String> redeemed = userPromoRedemptions.computeIfAbsent(userId, k -> ConcurrentHashMap.newKeySet());
        return redeemed.add(promoId);
    }

    public void releaseUserRedemption(String userId, String promoId) {
        Set<String> redeemed = userPromoRedemptions.get(userId);
        if (redeemed != null) {
            redeemed.remove(promoId);
        }
    }
}

class DiscountEngine {
    private final Map<PromotionType, DiscountStrategy> strategies = new HashMap<>();
    private final UsageGuard usageGuard = new UsageGuard();

    public DiscountEngine() {
        strategies.put(PromotionType.PERCENTAGE, new PercentageDiscountStrategy());
        strategies.put(PromotionType.FLAT_OFF, new FlatDiscountStrategy());
        strategies.put(PromotionType.FREE_SHIPPING, new FreeShippingStrategy());
        strategies.put(PromotionType.BOGO, new BogoStrategy());
    }

    public List<DiscountResult> calculateBestDiscounts(OrderContext order, List<Promotion> activePromotions, StackingPolicy policy) {
        List<DiscountResult> applicable = new ArrayList<>();
        double subtotal = order.getCartSubtotal();

        for (Promotion promo : activePromotions) {
            if (promo.getRemainingLimit() <= 0) continue;
            DiscountStrategy strategy = strategies.get(promo.getType());
            if (strategy != null) {
                DiscountResult res = strategy.calculateDiscount(order, promo, subtotal);
                if (res.getDiscountAmount() > 0) {
                    applicable.add(res);
                }
            }
        }

        if (applicable.isEmpty()) return Collections.emptyList();

        List<DiscountResult> finalSelection = new ArrayList<>();

        if (policy == StackingPolicy.EXCLUSIVE) {
            applicable.stream()
                .max(Comparator.comparingDouble(DiscountResult::getDiscountAmount))
                .ifPresent(finalSelection::add);
        } 
        else if (policy == StackingPolicy.BEST_DEAL) {
            // Greedily select non-overlapping/compatible strategies or pick best single exclusive vs. stacked
            applicable.sort((a, b) -> Double.compare(b.getDiscountAmount(), a.getDiscountAmount()));
            for (DiscountResult res : applicable) {
                if (res.getPromotion().isExclusive() && finalSelection.isEmpty()) {
                    finalSelection.add(res);
                    break;
                }
                if (!res.getPromotion().isExclusive()) {
                    finalSelection.add(res);
                }
            }
        } 
        else if (policy == StackingPolicy.ADDITIVE) {
            // Apply all non-exclusives, or if exclusive exists, pick highest exclusive or stacked
            boolean hasExclusive = applicable.stream().anyMatch(r -> r.getPromotion().isExclusive());
            if (hasExclusive) {
                applicable.stream().filter(r -> !r.getPromotion().isExclusive()).forEach(finalSelection::add);
            } else {
                finalSelection.addAll(applicable);
            }
        }

        return finalSelection;
    }

    public boolean checkout(OrderContext order, List<DiscountResult> appliedDiscounts) {
        List<Promotion> acquiredPromos = new ArrayList<>();
        boolean success = true;

        try {
            for (DiscountResult res : appliedDiscounts) {
                Promotion promo = res.getPromotion();
                if (!usageGuard.checkAndTrackUserLimit(order.getUserId(), promo.getId())) {
                    success = false;
                    break;
                }
                if (!promo.tryAcquireUsage()) {
                    usageGuard.releaseUserRedemption(order.getUserId(), promo.getId());
                    success = false;
                    break;
                }
                acquiredPromos.add(promo);
            }

            if (!success) {
                rollback(order.getUserId(), acquiredPromos);
                return false;
            }

            boolean paymentSuccess = processPayment(order);
            if (!paymentSuccess) {
                throw new RuntimeException("Payment Failed");
            }
            return true;

        } catch (Exception e) {
            rollback(order.getUserId(), acquiredPromos);
            return false;
        }
    }

    private void rollback(String userId, List<Promotion> acquiredPromos) {
        for (Promotion acquired : acquiredPromos) {
            acquired.releaseUsage();
            usageGuard.releaseUserRedemption(userId, acquired.getId());
        }
    }

    private boolean processPayment(OrderContext order) {
        return true; 
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== JAVA DISCOUNT ENGINE SIMULATION ===");
        DiscountEngine engine = new DiscountEngine();

        List<OrderItem> items = Arrays.asList(
            new OrderItem("item1", "Electronics", 200.0, 1),
            new OrderItem("item2", "Apparel", 50.0, 2)
        );
        OrderContext order = new OrderContext("ORD123", "USER_A", items, 15.0);

        List<Promotion> promos = Arrays.asList(
            new Promotion("P1", "ELEC10", PromotionType.PERCENTAGE, 0.1, "Electronics", 100.0, StackingGroup.ITEM_LEVEL, false, 5),
            new Promotion("P2", "FLAT20", PromotionType.FLAT_OFF, 20.0, null, 150.0, StackingGroup.CART_TOTAL, false, 10),
            new Promotion("P3", "FREESHIP", PromotionType.FREE_SHIPPING, 0.0, null, 50.0, StackingGroup.SHIPPING, false, 2)
        );

        System.out.println("Calculating best discounts under StackingPolicy.BEST_DEAL...");
        List<DiscountResult> applied = engine.calculateBestDiscounts(order, promos, StackingPolicy.BEST_DEAL);

        for (DiscountResult discount : applied) {
            System.out.println(" - Applied promo: " + discount.getPromotion().getCode() + " | Discount: $" + discount.getDiscountAmount() + " (" + discount.getDescription() + ")");
        }

        boolean checkoutSuccess = engine.checkout(order, applied);
        System.out.println("Checkout status: " + (checkoutSuccess ? "SUCCESS" : "FAILED"));
        System.out.println("Remaining limits: P1=" + promos.get(0).getRemainingLimit() + ", P2=" + promos.get(1).getRemainingLimit());
        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}