Merged in chore/LOY-511-Rewards-Documentation (pull request #3367)

Chore/LOY-511): Add documentation for rewards & redeem flow

* chore(LOY-511): Add documentation for the rewards system

* chore(LOY-511): move doc

* chore(LOY-511): remove old doc

* Removed AI documentation

* Remove ToC


Approved-by: Matilda Landström
This commit is contained in:
Chuma Mcphoy (We Ahead)
2026-01-07 11:01:43 +00:00
committed by Linus Flood
parent 022fb729f7
commit 5848f486f3

View File

@@ -0,0 +1,113 @@
# Rewards System
This doc provides a comprehensive guide to the rewards system for developers maintaining the codebase.
## Overview
The rewards system manages loyalty benefits for Scandic Hotels members. It combines data from two sources:
1. **Scandic Profile API** - Provides reward availability, states, and coupon codes
2. **Contentstack CMS** - Provides reward content (labels, descriptions, redeem instructions)
Rewards are tied to **membership levels** (L1-L7, called "Friends") and come in different **types** (Tier, Surprise, Campaign, Member-voucher).
````
### Data Merging Process
Rewards are matched between API and CMS using the `reward_id` field:
```typescript
// From query.ts - merging API and CMS data
const rewards: BaseReward[] = cmsRewards.map((cmsReward) => {
const apiReward = redeemableRewards.find(
({ rewardId }) => rewardId === cmsReward.reward_id
)!
return {
...apiReward, // API data: status, coupon codes, redeem location
...cmsReward, // CMS data: label, description, redeem_description
}
})
````
## Reward Types
### 1. Tier Rewards (`rewardType: "Tier"`)
Permanent benefits tied to membership level. These are always available once a member reaches the required tier.
**Characteristics:**
- Linked to `rewardTierLevel` (e.g., "L1", "L2")
- No coupon codes required for most
- Examples: 10% food discount, early check-in, late checkout
**Redeem behavior:**
- `On-site`: Show membership number at hotel
- `Non-redeemable`: Display-only benefits (e.g., earn rate bonuses)
### 2. Surprise Rewards (`rewardType: "Surprise"`)
Special rewards that appear as wrapped gifts and need to be "unwrapped" before use.
**Characteristics:**
- Part of `coupons` array in API response
- Have `unwrapped: boolean` flag on each coupon
- Must call `unwrap` mutation before becoming redeemable
- After unwrapping, appear in regular rewards list
**Flow:**
1. User sees surprise modal with wrapped gift
2. User clicks to reveal/unwrap
3. Frontend calls `rewards.unwrap` mutation
4. Surprise moves to current rewards
### 3. Campaign Rewards (`rewardType: "Campaign"`)
Promotional rewards with promo codes for online use.
**Characteristics:**
- Part of `coupons` array
- Have `couponCode` for redemption
- Typically redeemed online
- May have expiration dates
**Redeem behavior:**
- Display promo code with copy-to-clipboard functionality
### 4. Member Voucher (`rewardType: "Member-voucher"`)
Voucher-based rewards similar to campaigns.
**Characteristics:**
- Part of `coupons` array
- Have coupon codes
- Similar flow to Campaign rewards
---
## Coupon States & Redemption
### Coupon States
| State | Description | Redeemable |
| ---------- | ---------------------------------------------- | ---------- |
| `claimed` | Coupon has been claimed by user | Yes |
| `viewed` | Coupon has been viewed (schema only, not used) | Yes |
| `redeemed` | Coupon has been used | No |
**Note:** Only `redeemed` state is actively filtered out in business logic. Both `claimed` and `viewed` are treated as redeemable.
### Redeem Locations
| Location | Description | UI Behavior |
| ---------------- | --------------------------------- | ----------------------------- |
| `On-site` | Redeem at hotel (show to staff) | Shows membership number badge |
| `Online` | Redeem digitally with promo code | Shows copy-to-clipboard |
| `Non-redeemable` | Display-only (automatic benefits) | No redeem button, info only |