82. Design Customer Loyalty Program for Ecommerce Website

Design Customer Loyalty Program for Ecommerce Website
Design and implement an in-memory Ecommerce with Loyalty Program service that adds gamification to an e-commerce platform. The service tracks users and their transactions, supports purchases with optional points redemption (based on user level rules), awards new points on the money-paid portion, and exposes APIs to view a user's current stats.

Each point is worth ₹1 during redemption (i.e., redeeming X points reduces payable by ₹X).


Bonus: Personalized discount based on purchase history, applied after redemption and capped.

All data must be stored in-memory only (no database).

Core Concepts

1) User Levels

  • Users start at Bronze (0 points) on onboarding.
  • Level depends on current points balance:
    • Bronze: 0 to 499 points
    • Silver: 500 to 999 points
    • Gold: >= 1000 points

2) Earning Points

  • Users earn points for every purchase on the amount paid with money (i.e., after redemption and after bonus discount).
  • Points are calculated per ₹100 spent, based on current level at the time of purchase:
    • Bronze: 10 points per ₹100
    • Silver: 12.5 points per ₹100
    • Gold: 15 points per ₹100
  • Fractional points are allowed (e.g., 337.5 points).

3) Redeeming Points During Purchase

  • Users may redeem points to pay for a portion of the purchase.
  • Redemption is constrained by user level at the time of purchase:
  • Bronze
    • Maximum redemption percentage: 5% of the order amount
    • Maximum points redeemable: 200 points
  • Silver
    • Maximum redemption percentage: 10% of the order amount
    • Maximum points redeemable: 500 points
  • Gold
    • Maximum redemption percentage: 15% of the order amount
    • Maximum points redeemable: 1000 points
  • User may choose to redeem any value 0 <= pointsToRedeem that is within the eligible limit.
  • If the user requests more points than:
    • their current available points, OR
    • their level-based redemption eligibility,
    the purchase must fail.

4) Personalized Discount (Bonus)

  • Compute a personalized discount on the current purchase based on the user's past purchase history.
  • The discount is applied after points redemption.
  • Discount rules (based on history before the current purchase):
    • If user has placed more than 3 orders, apply 5% discount.
    • If user's total spending is > ₹10,000, apply 10% discount.
    • If both are true, apply 12% discount.
  • Discount cap: discountAmount <= ₹5000.
  • If a discount is applied on a purchase, start a new discount cycle after that purchase finishes, and the user can get the next discount only after meeting the eligibility conditions again using only the orders/spending made after that discounted purchase.

Methods

1) onboard

String onboard(String userName)
  • Registers a user on the platform.
  • New users start with:
    • points = 0
    • level = Bronze
    • orderCount = 0
    • totalSpent = 0
  • Return a status string:
    • ONBOARDED,<userName> if successful
    • USER_ALREADY_EXISTS,<userName> if user already exists

2) purchase

String purchase(String userName, double orderAmount, double pointsToRedeem, boolean applyDiscount)
  • Attempts a purchase of orderAmount for userName.
  • Redemption validation (level-based):
    • 0 <= pointsToRedeem
    • pointsToRedeem <= userCurrentPoints
    • pointsToRedeem <= levelMaxPointsCap
    • pointsToRedeem <= levelMaxPercent * orderAmount
  • Compute amountAfterRedemption = orderAmount - pointsToRedeem.
  • If applyDiscount is true, apply bonus discount (if eligible) on amountAfterRedemption.
  • Compute finalPayable = amountAfterRedemption - discountAmount.
  • Points earned:
    • pointsEarned = (finalPayable / 100) * earnRateByLevel
  • On success, update:
    • user points: points = points - pointsToRedeem + pointsEarned
    • orderCount: +1
    • totalSpent: +finalPayable
    • level based on updated points
  • Return a comma-separated status string on success:
    • PURCHASE_SUCCESS,<pointsRedeemed>,<discountApplied>,<pointsEarned>,<finalPayable>,<currentPoints>,<currentLevel>,<orderCount>
  • Return a comma-separated status string on failure (in below order):
    • USER_NOT_FOUND
    • INVALID_ORDER_AMOUNT
    • INVALID_REDEEM_POINTS
    • NOT_ENOUGH_POINTS
    • REDEMPTION_LIMIT_EXCEEDED

3) getUserStats

List<String> getUserStats(String userName)
  • Returns current stats of the user as a list of strings (fixed order):
  • [ "USER,<userName>", "POINTS,<points>", "LEVEL,<level>", "ORDERS,<orderCount>", "TOTAL_SPENT,<totalSpent>" ]
  • If user does not exist, return:
    • ["USER_NOT_FOUND,<userName>"]

Constraints

  • All double balues will be rounded till two decimal places.
    Math.round(num * 100.0) / 100.0 or
    ((int)((num + 0.005) * 100)) / 100.0
  • 1 <= userName.length() <= 50
  • 0 <= orderAmount <= 1_000_000
  • 0 <= pointsToRedeem
  • applyDiscount is either true or false
  • At most 10^5 total API calls.
  • All calculations should be done in a consistent manner; fractional points are allowed.
  • In-memory only; no external storage.

Examples

Example 1: Onboard and Bronze Purchase

  • Call: onboard(userName="user1")
  • Output: ONBOARDED,user1
  • Call: purchase(userName="user1", orderAmount=800.00, pointsToRedeem=0.00, applyDiscount=false)
  • Reasoning: Bronze earn rate = 10 points/₹100. Final payable = 800.00. Points earned = (800/100)*10 = 80.00. Points become 80.00, level remains Bronze, orders become 1.
  • Output: PURCHASE_SUCCESS,0.00,0.00,80.00,800.00,80.00,Bronze,1

Example 2: Redemption Failure (Not Enough Points)

  • Call: purchase(userName="user1", orderAmount=4200.00, pointsToRedeem=100.00, applyDiscount=false)
  • Reasoning: user1 has only 80.00 points, cannot redeem 100.00.
  • Output: NOT_ENOUGH_POINTS

Example 3: Reach Silver and Redeem as Silver

  • Call: purchase(userName="user1", orderAmount=4200.00, pointsToRedeem=0.00, applyDiscount=false)
  • Reasoning: Bronze earn rate = 10. Points earned = (4200/100)*10 = 420.00. Points become 80.00 + 420.00 = 500.00, so level becomes Silver. Orders become 2.
  • Output: PURCHASE_SUCCESS,0.00,0.00,420.00,4200.00,500.00,Silver,2
  • Call: purchase(userName="user1", orderAmount=3000.00, pointsToRedeem=300.00, applyDiscount=false)
  • Reasoning: user1 is Silver. Redemption caps: max points 500, and max 10% of 3000 = 300. So 300 is allowed. Payable after redemption = 2700.00. Earn rate = 12.5. Points earned = (2700/100)*12.5 = 337.50. New points = 500.00 - 300.00 + 337.50 = 537.50, level stays Silver. Orders become 3.
  • Output: PURCHASE_SUCCESS,300.00,0.00,337.50,2700.00,537.50,Silver,3

Example 4: Reach Gold and Apply Bonus Discount

  • Call: purchase(userName="user1", orderAmount=5000.00, pointsToRedeem=0.00, applyDiscount=false)
  • Reasoning: Silver earn rate = 12.5. Points earned = (5000/100)*12.5 = 625.00. New points = 537.50 + 625.00 = 1162.50, so level becomes Gold. Orders become 4.
  • Output: PURCHASE_SUCCESS,0.00,0.00,625.00,5000.00,1162.50,Gold,4
  • Call: purchase(userName="user1", orderAmount=12000.00, pointsToRedeem=800.00, applyDiscount=true)
  • Reasoning: user1 is Gold. Redemption caps: max points 1000, and max 15% of 12000 = 1800, so 800 is allowed. Amount after redemption = 11200.00. Before this purchase, orders > 3 (4 orders) and totalSpent > 10000 (12700.00), so discount = 12%. Discount = 11200 * 0.12 = 1344.00 (below ₹5000 cap). Final payable = 11200 - 1344 = 9856.00. Earn rate = 15. Points earned = (9856/100)*15 = 1478.40. New points = 1162.50 - 800.00 + 1478.40 = 1840.90. Orders become 5.
  • Output: PURCHASE_SUCCESS,800.00,1344.00,1478.40,9856.00,1840.90,Gold,5

Example 5: Get User Stats

  • Call: getUserStats(userName="user1")
  • Output: [ "USER,user1", "POINTS,1840.90", "LEVEL,Gold", "ORDERS,5", "TOTAL_SPENT,22556.00" ]




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