80. Design Flipkart Buy Now Pay Later

Design Flipkart Buy Now Pay Later
Flipkart is known for its innovative products and making shopping easier for customers. One such innovation is Buy Now Pay Later (BNPL), where a customer can buy the product instantly and pay the dues within 30 days. Each user has a credit limit associated with them initially.

Implement an in-memory system that manages users, inventory, orders, and BNPL dues. The system should expose the APIs below.

Global Rules

  • All data is stored in-memory only; no database usage.
  • Date representation for all APIs: All date inputs are provided as an integer day count since 01-Jan-1970 (Unix epoch day).
    • 0 means 01-Jan-1970
    • 1 means 02-Jan-1970
    • and so on
  • BNPL window: dueDay = purchaseDay + 30.
  • Due status as-of a day:
    • PENDING if asOfDay <= dueDay
    • DELAYED if asOfDay > dueDay
  • Credit semantics:
    • creditLimit is the maximum BNPL limit set at registration.
    • availableCredit = creditLimit - sum(pendingAmount of all BNPL orders not yet cleared)
    • On successful purchase, pendingAmount increases by orderTotal, so availableCredit decreases.
    • On clearing dues, pendingAmount becomes 0 for those orders, so availableCredit increases (up to creditLimit).
  • Atomic buy: if any item is invalid/unavailable/insufficient, the entire purchase fails and no state changes.

Methods

1) seedInventory

void seedInventory(List<String> inventoryLines)
  • Add products to inventory and store in memory.
  • Each string line represents one product entry and is comma-separated: name,count,price (e.g., Shoes,5,200).
  • Validation:
    • count >= 0, price > 0, name is a non-empty string.
    • All lines will be in valid format.
  • Duplicates:
    • If the same name appears again, merge counts by adding to existing count.
    • Price is overwritten with the latest provided price.

2) viewInventory

List<String> viewInventory()
  • Return a view of inventory at any point.
  • Return a list of strings in the format: name,count,price.
  • Ordering: return is sorted by 'name' in lexicographically ascending order.

3) registerUser

void registerUser(String user, int creditLimit)
  • Register a user with initial BNPL credit limit.
  • creditLimit >= 0
  • user (name) acts as the user identifier (unique).
  • If the user already exists, you do nothing and simply return.

4) buy

boolean buy(String orderId, String user, List<String> itemsWithQuantity, int purchaseDay)
  • A customer can buy products available in inventory using BNPL only.
  • itemsWithQuantity is a list of strings like ["Shoes,2","Watch,1"] (format: itemName,quantity).
  • Validation :
    • User must be registered, and purchaseDay >= 0.
    • All quantities must be integers with quantity > 0.
    • If the same item appears multiple times inside itemsWithQuantity, treat it as merged (sum quantities).
    • All requested items must exist in inventory and have sufficient count; otherwise the buy fails.
    • If orderId already exists, buy returns false and makes no changes.
  • BNPL purchase rules:
    • Before placing the order, check isBlacklisted(user, purchaseDay); if true, reject the purchase.
    • Compute orderTotal. If orderTotal > availableCredit, reject the purchase.
    • On success: create the order, create a due (pendingAmount = orderTotal), reduce inventory, and availableCredit decreases accordingly.
  • Return:
    • On success: return true. On failure: return false.

5) clearDues

void clearDues(String user, List<String> orderIdsToClear, int clearingDay)
  • A user can pay dues partially or completely.
  • orderIdsToClear is a list of orderIds (e.g., ["OD_123","OD_124"]).
  • Only BNPL orders have dues. Clearing an order sets its pending amount to 0.
  • If an orderId is invalid, belongs to another user, or already cleared, ignore it (no-op).
  • clearingDay >= 0
  • Clearing dues restores available BNPL credit accordingly (up to creditLimit).

6) accountSummary

List<String> accountSummary(String user, int asOfDay)
  • Combined view of:
    • current available BNPL credit
    • order history (all orders)
    • if an order has payment due as-of asOfDay, show its due details right after that order
  • Credit line:
    • First element must be: CREDIT_AVAILABLE,<amount>
  • Order sorting (deterministic):
    • Show all orders in descending order of purchaseDay.
    • Tie-break: ascending by orderId.
  • Order line format:
    • Each order is printed as: ORDER,orderId,purchaseDay,totalAmount,items where items is Item1:Q1|Item2:Q2 are also sorted lexicographically in ascending order by item name.
  • Show due details only when payment is due:
    • After an ORDER,... line, print a due line only if: pendingAmount > 0 and purchaseDay <= asOfDay.
    • Due status is computed as-of asOfDay:
      • PENDING if asOfDay <= dueDay
      • DELAYED if asOfDay > dueDay
    • Due line format (immediately after its corresponding order line): DUE,orderId,purchaseDay,dueDay,pendingAmount,status,items
    • If an order is cleared (pendingAmount == 0) or purchaseDay > asOfDay, then do not print any DUE,... line for that order.
  • Overall output ordering:
    • 1st line: CREDIT_AVAILABLE,<amount>
    • Then for each order (sorted by purchaseDay descending): ORDER,... line, followed immediately by an optional DUE,... line (only if payment is due).

7) isBlacklisted

boolean isBlacklisted(String user, int asOfDay)
  • If a customer defaults payment for 3 or more orders, they are blacklisted and cannot place new BNPL orders.
  • Default: A BNPL order counts as default if its pending amount is > 0 and it is DELAYED as-of asOfDay.
  • Blacklisting check should be used inside buy for BNPL purchases with purchaseDay as the as-of day.
  • They are removed from blacklist only after they clear all their pending BNPL dues.

Constraints

  • All data must be stored in-memory; no database usage.
  • Inventory count must not become negative after any purchase.
  • Inventory line format: name,count,price.
  • Inventory view format: name,count,price.
  • All day values (purchaseDay, asOfDay, clearingDay) are integers with >= 0.
  • For purchases, orderTotal <= availableCredit, otherwise reject.
  • Due status is PENDING if asOfDay <= dueDay; otherwise DELAYED.
  • Orders returned inside accountSummary(user, asOfDay) must be sorted by descending purchaseDay.
  • Due lines inside accountSummary(user, asOfDay) are printed immediately after their corresponding order line (if applicable).

Examples

Only basic details are listed in examples. You can take in more details while designing.

Notes:
  • Day values are epoch-day integers (days since 01-Jan-1970).
  • In examples, orderIds (e.g., OD_1, OD_2) are explicitly passed to buy as per the updated signature.

Example 1: Seed inventory, register user, buy using BNPL, view combined summary

  • seedInventory(inventoryLines = List<String>["Shoes,5,200", "Watch,10,1000", "T-Shirt,14,2000"])
    Output: (no return)
    Explanation: Adds three products to inventory.
  • registerUser(user = "Akshay", creditLimit = 5000)
    Output: (no return)
    Explanation: Registers Akshay with creditLimit 5000 (availableCredit starts at 5000).
  • buy(orderId = "OD_1", user = "Akshay", itemsWithQuantity = List<String>["Shoes,2", "Watch,1"], purchaseDay = 19000)
    Output: true
    Explanation: orderTotal = (2 * 200) + (1 * 1000) = 1400 <= availableCredit 5000, user is not blacklisted at day 19000, so BNPL order succeeds; dueDay = 19030.
  • viewInventory()
    Output: List<String>["Shoes,3,200", "T-Shirt,14,2000", "Watch,9,1000"]
    Explanation: Inventory reduces after purchase and is returned sorted by name.
  • accountSummary(user = "Akshay", asOfDay = 19010)
    Output: List<String>[ "CREDIT_AVAILABLE,3600", "ORDER,OD_1,19000,1400,Shoes:2|Watch:1", "DUE,OD_1,19000,19030,1400,PENDING,Shoes:2|Watch:1" ]
    Explanation: availableCredit = 5000 - 1400 = 3600; since pendingAmount > 0 and purchaseDay 19000 <= asOfDay 19010, due line is shown; asOfDay <= dueDay so status is PENDING.

Example 2: BNPL buy rejected due to insufficient credit, summary has only credit line

  • seedInventory(inventoryLines = List<String>["Phone,1,6000"])
    Output: (no return)
    Explanation: Adds Phone with count 1 and price 6000.
  • registerUser(user = "Riya", creditLimit = 5000)
    Output: (no return)
    Explanation: Registers Riya with availableCredit 5000.
  • buy(orderId = "OD_1", user = "Riya", itemsWithQuantity = List<String>["Phone,1"], purchaseDay = 19005)
    Output: false
    Explanation: orderTotal = 6000 > availableCredit 5000, so buy fails atomically; inventory and dues remain unchanged.
  • viewInventory()
    Output: List<String>["Phone,1,6000"]
    Explanation: Inventory remains unchanged because the buy failed atomically.
  • accountSummary(user = "Riya", asOfDay = 19005)
    Output: List<String>["CREDIT_AVAILABLE,5000"]
    Explanation: No orders exist, so only the credit line is returned.

Example 3: Orders shown newest-first; delayed dues shown only for orders on/before asOfDay; blacklist and clearing

  • seedInventory(inventoryLines = List<String>["Book,10,100"])
    Output: (no return)
    Explanation: Adds Book with enough stock.
  • registerUser(user = "Neha", creditLimit = 1000)
    Output: (no return)
    Explanation: Neha starts with availableCredit 1000.
  • buy(orderId = "OD_1", user = "Neha", itemsWithQuantity = List<String>["Book,1"], purchaseDay = 100)
    Output: true
    Explanation: orderTotal = 100, dueDay = 130, and credit is sufficient, so order succeeds.
  • buy(orderId = "OD_2", user = "Neha", itemsWithQuantity = List<String>["Book,1"], purchaseDay = 101)
    Output: true
    Explanation: orderTotal = 100, dueDay = 131, and credit is sufficient, so order succeeds.
  • buy(orderId = "OD_3", user = "Neha", itemsWithQuantity = List<String>["Book,1"], purchaseDay = 102)
    Output: true
    Explanation: orderTotal = 100, dueDay = 132, and credit is sufficient, so order succeeds.
  • accountSummary(user = "Neha", asOfDay = 140)
    Output: List<String>[ "CREDIT_AVAILABLE,700", "ORDER,OD_3,102,100,Book:1", "DUE,OD_3,102,132,100,DELAYED,Book:1", "ORDER,OD_2,101,100,Book:1", "DUE,OD_2,101,131,100,DELAYED,Book:1", "ORDER,OD_1,100,100,Book:1", "DUE,OD_1,100,130,100,DELAYED,Book:1" ]
    Explanation: Orders are newest-first (102, 101, 100). Each has pendingAmount > 0 and purchaseDay <= 140, so due lines are shown. Since 140 > dueDay for each, status is DELAYED. availableCredit = 1000 - 300 = 700.
  • isBlacklisted(user = "Neha", asOfDay = 140)
    Output: true
    Explanation: DefaultCount = 3 (all three orders are unpaid and DELAYED as-of day 140), so Neha is blacklisted.
  • buy(orderId = "OD_4", user = "Neha", itemsWithQuantity = List<String>["Book,1"], purchaseDay = 140)
    Output: false
    Explanation: Buy is rejected because isBlacklisted("Neha", 140) is true.
  • clearDues(user = "Neha", orderIdsToClear = List<String>["OD_1","OD_2","OD_3"], clearingDay = 140)
    Output: (no return)
    Explanation: Clears all dues; pendingAmount becomes 0 for each cleared order, restoring available credit (up to creditLimit).
  • isBlacklisted(user = "Neha", asOfDay = 140)
    Output: false
    Explanation: User is removed from blacklist only after all pending BNPL dues are cleared i.e. total pending across all orders becomes 0.now defaultCount is 0.
  • accountSummary(user = "Neha", asOfDay = 140)
    Output: List<String>[ "CREDIT_AVAILABLE,1000", "ORDER,OD_3,102,100,Book:1", "ORDER,OD_2,101,100,Book:1", "ORDER,OD_1,100,100,Book:1" ]
    Explanation: All dues are cleared so credit returns to 1000. Due lines are not shown because pendingAmount == 0. Orders remain sorted newest-first.




Please use Laptop/Desktop or any other large screen to add/edit code.