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" ]