Machine Coding Problem

Shopping Cart (Guest Merge)

macoAllcommerceguest-vs-logged-in-merge
Commonly Asked By:AmazonWalmartShopify

Functional Specifications (In-Scope)

  • Guest vs Logged-In Modes: Decouples anonymous session carts stored in-memory from user account carts stored in the user profile registry.
  • Pluggable Conflict Resolution: Configurable merge policies (KEEP_GUEST, KEEP_USER, SUM_QUANTITIES, PREFER_HIGHER_QTY).
  • Real-Time Inventory Validation: Verifies stock balances on merge, automatically downsizing or deleting out-of-stock items.
  • User Alert Dispatcher: Captures stock discrepancy modifications to push notification alerts to logged-in users.

Out-of-Scope Boundaries

  • Payment Integration Gateways: Excludes checkout transactions to focus strictly on session and inventory reconciliations.
  • Persistent Cookie Store TTLs: Relies on simple React states to represent short-term cookie memory.

Production reference implementations demonstrating conflict solvers, inventory integrations, and clean cleanup mechanisms in Java and Python:

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

enum MergePolicy {
    KEEP_GUEST,
    KEEP_USER,
    SUM_QUANTITIES,
    PREFER_HIGHER_QTY
}

class CartItem {
    private final String productId;
    private int quantity;
    private final double unitPrice;
    private final long addedAt;

    public CartItem(String productId, int quantity, double unitPrice) {
        this.productId = productId;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
        this.addedAt = System.currentTimeMillis();
    }

    public String getProductId() { return productId; }
    public int getQuantity() { return quantity; }
    public double getUnitPrice() { return unitPrice; }
    public long getAddedAt() { return addedAt; }

    public void setQuantity(int quantity) { this.quantity = quantity; }
}

class Cart {
    private final String cartId; // sessionId or userId
    private final Map<String, CartItem> items = new ConcurrentHashMap<>();
    private long lastUpdated;

    public Cart(String cartId) {
        this.cartId = cartId;
        this.lastUpdated = System.currentTimeMillis();
    }

    public String getCartId() { return cartId; }
    public Map<String, CartItem> getItems() { return items; }
    public long getLastUpdated() { return lastUpdated; }

    public void addItem(String productId, int qty, double price) {
        items.merge(productId, new CartItem(productId, qty, price), (oldVal, newVal) -> {
            oldVal.setQuantity(oldVal.getQuantity() + qty);
            return oldVal;
        });
        lastUpdated = System.currentTimeMillis();
    }

    public void clear() {
        items.clear();
        lastUpdated = System.currentTimeMillis();
    }
}

class InventoryService {
    private final Map<String, Integer> stockRegistry = new ConcurrentHashMap<>();

    public void setStock(String productId, int qty) {
        stockRegistry.put(productId, qty);
    }

    public int getAvailableStock(String productId) {
        return stockRegistry.getOrDefault(productId, 0);
    }
}

class MergeResult {
    private final Cart mergedCart;
    private final List<String> notifications;

    public MergeResult(Cart mergedCart, List<String> notifications) {
        this.mergedCart = mergedCart;
        this.notifications = notifications;
    }

    public Cart getMergedCart() { return mergedCart; }
    public List<String> getNotifications() { return notifications; }
}

class CartMergeService {
    private final Map<String, Cart> guestCarts = new ConcurrentHashMap<>();
    private final Map<String, Cart> userCarts = new ConcurrentHashMap<>();
    private final InventoryService inventoryService;

    public CartMergeService(InventoryService inventoryService) {
        this.inventoryService = inventoryService;
    }

    public Cart getGuestCart(String sessionId) {
        return guestCarts.computeIfAbsent(sessionId, Cart::new);
    }

    public Cart getUserCart(String userId) {
        return userCarts.computeIfAbsent(userId, Cart::new);
    }

    public MergeResult merge(String sessionId, String userId, MergePolicy policy) {
        Cart guestCart = guestCarts.get(sessionId);
        Cart userCart = getUserCart(userId);
        List<String> notifications = new ArrayList<>();

        if (guestCart == null || guestCart.getItems().isEmpty()) {
            return new MergeResult(userCart, notifications);
        }

        // Apply merge logic
        for (CartItem guestItem : guestCart.getItems().values()) {
            String prodId = guestItem.getProductId();
            CartItem userItem = userCart.getItems().get(prodId);

            int targetQty = 0;
            if (userItem == null) {
                targetQty = guestItem.getQuantity();
            } else {
                switch (policy) {
                    case KEEP_GUEST:
                        targetQty = guestItem.getQuantity();
                        break;
                    case KEEP_USER:
                        targetQty = userItem.getQuantity();
                        break;
                    case SUM_QUANTITIES:
                        targetQty = userItem.getQuantity() + guestItem.getQuantity();
                        break;
                    case PREFER_HIGHER_QTY:
                        targetQty = Math.max(userItem.getQuantity(), guestItem.getQuantity());
                        break;
                }
            }

            // Inventory Validation Check
            int availableStock = inventoryService.getAvailableStock(prodId);
            if (availableStock <= 0) {
                notifications.add("Removed product: " + prodId + " (Out of Stock)");
                userCart.getItems().remove(prodId);
            } else if (targetQty > availableStock) {
                notifications.add("Reduced qty of: " + prodId + " from " + targetQty + " to " + availableStock + " due to limited stock.");
                if (userItem != null) {
                    userItem.setQuantity(availableStock);
                } else {
                    userCart.addItem(prodId, availableStock, guestItem.getUnitPrice());
                }
            } else {
                if (userItem != null) {
                    userItem.setQuantity(targetQty);
                } else {
                    userCart.addItem(prodId, targetQty, guestItem.getUnitPrice());
                }
            }
        }

        // Clean up Session Guest Cart
        guestCarts.remove(sessionId);

        return new MergeResult(userCart, notifications);

    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== JAVA SHOPPING CART MERGE SIMULATION ===");
        InventoryService inventory = new InventoryService();
        inventory.setStock("laptop", 2);
        inventory.setStock("headphones", 0);
        inventory.setStock("keyboard", 5);

        CartMergeService service = new CartMergeService(inventory);

        // Guest session adding items
        String sessionId = "guest-session-999";
        Cart guestCart = service.getGuestCart(sessionId);
        guestCart.addItem("laptop", 3, 1200.0);
        guestCart.addItem("headphones", 1, 250.0);

        // Authenticated user cart has some existing items
        String userId = "user-alice";
        Cart userCart = service.getUserCart(userId);
        userCart.addItem("laptop", 1, 1200.0);
        userCart.addItem("keyboard", 1, 150.0);

        System.out.println("Guest Cart Qty of laptop: " + guestCart.getItems().get("laptop").getQuantity());
        System.out.println("User Cart Qty of laptop: " + userCart.getItems().get("laptop").getQuantity());

        // Perform merge
        System.out.println("\nLogging in guest to merge carts with PREFER_HIGHER_QTY policy...");
        MergeResult result = service.merge(sessionId, userId, MergePolicy.PREFER_HIGHER_QTY);

        System.out.println("Merge complete. Notifications:");
        for (String note : result.getNotifications()) {
            System.out.println(" - " + note);
        }

        System.out.println("\nFinal Merged User Cart Items:");
        for (CartItem item : result.getMergedCart().getItems().values()) {
            System.out.println("Product: " + item.getProductId() + ", Qty: " + item.getQuantity() + ", Price: $" + item.getUnitPrice());
        }
        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}