Move subscription create/edit from store detail (broken endpoint) to merchant detail page with proper modal UI. Seed 4 subscription tiers (Essential, Professional, Business, Enterprise) in init_production.py. Also includes cross-module dependency declarations, store domain platform_id migration, platform context middleware, CMS route fixes, and migration backups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
610 lines
27 KiB
Markdown
610 lines
27 KiB
Markdown
# Loyalty Module - User Journeys
|
|
|
|
## Personas
|
|
|
|
| # | Persona | Role / Auth | Description |
|
|
|---|---------|-------------|-------------|
|
|
| 1 | **Platform Admin** | `admin` role | Oversees all merchants' loyalty programs, views platform-wide stats, manages merchant settings |
|
|
| 2 | **Merchant Owner** | `store` role + owns merchant | Manages their merchant-wide loyalty program via the store interface. There is **no separate merchant owner UI** - loyalty programs are merchant-scoped but managed through any of the merchant's stores |
|
|
| 3 | **Store Staff / Team Member** | `store` role + store membership | Operates the POS terminal - scans cards, adds stamps/points, redeems rewards |
|
|
| 4 | **Customer (authenticated)** | Customer login | Views their loyalty card, balance, and transaction history |
|
|
| 5 | **Customer (anonymous)** | No auth | Browses program info, self-enrolls, downloads wallet passes |
|
|
|
|
!!! note "Merchant Owner vs Store Staff"
|
|
The loyalty module does **not** have a dedicated merchant owner interface. The merchant owner
|
|
accesses loyalty through the **store interface** (`/store/{store_code}/loyalty/...`). Since the
|
|
loyalty program is scoped at the merchant level (one program shared by all stores), the owner
|
|
can manage it from any of their stores. The difference is only in **permissions** - owners have
|
|
full access, team members have role-based access.
|
|
|
|
---
|
|
|
|
## Current Dev Database State
|
|
|
|
### Merchants & Stores
|
|
|
|
| Merchant | Owner | Stores |
|
|
|----------|-------|--------|
|
|
| WizaCorp Ltd. (id=1) | john.owner@wizacorp.com | WIZAMART, WIZAGADGETS, WIZAHOME |
|
|
| Fashion Group S.A. (id=2) | jane.owner@fashiongroup.com | FASHIONHUB, FASHIONOUTLET |
|
|
| BookWorld Publishing (id=3) | bob.owner@bookworld.com | BOOKSTORE, BOOKDIGITAL |
|
|
|
|
### Users
|
|
|
|
| Email | Role | Type |
|
|
|-------|------|------|
|
|
| admin@wizamart.com | admin | Platform admin |
|
|
| samir.boulahtit@gmail.com | admin | Platform admin |
|
|
| john.owner@wizacorp.com | store | Owner of WizaCorp (merchant 1) |
|
|
| jane.owner@fashiongroup.com | store | Owner of Fashion Group (merchant 2) |
|
|
| bob.owner@bookworld.com | store | Owner of BookWorld (merchant 3) |
|
|
| alice.manager@wizacorp.com | store | Team member (stores 1, 2) |
|
|
| charlie.staff@wizacorp.com | store | Team member (store 3) |
|
|
| diana.stylist@fashiongroup.com | store | Team member (stores 4, 5) |
|
|
| eric.sales@fashiongroup.com | store | Team member (store 5) |
|
|
| fiona.editor@bookworld.com | store | Team member (stores 6, 7) |
|
|
|
|
### Loyalty Data Status
|
|
|
|
| Table | Rows |
|
|
|-------|------|
|
|
| loyalty_programs | 0 |
|
|
| loyalty_cards | 0 |
|
|
| loyalty_transactions | 0 |
|
|
| merchant_loyalty_settings | 0 |
|
|
| staff_pins | 0 |
|
|
| merchant_subscriptions | 0 |
|
|
|
|
!!! warning "No loyalty programs exist yet"
|
|
All loyalty tables are empty. The first step in testing is to create a loyalty program
|
|
via the store interface. There are also **no subscriptions** set up, which may gate access
|
|
to the loyalty module depending on feature-gating configuration.
|
|
|
|
---
|
|
|
|
## Dev URLs (localhost:9999)
|
|
|
|
The dev server uses path-based platform routing: `http://localhost:9999/platforms/loyalty/...`
|
|
|
|
### 1. Platform Admin Pages
|
|
|
|
Login as: `admin@wizamart.com` or `samir.boulahtit@gmail.com`
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Programs Dashboard | `http://localhost:9999/platforms/loyalty/admin/loyalty/programs` |
|
|
| Analytics | `http://localhost:9999/platforms/loyalty/admin/loyalty/analytics` |
|
|
| WizaCorp Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1` |
|
|
| WizaCorp Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings` |
|
|
| Fashion Group Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2` |
|
|
| Fashion Group Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2/settings` |
|
|
| BookWorld Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/3` |
|
|
| BookWorld Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/3/settings` |
|
|
|
|
### 2. Merchant Owner / Store Pages
|
|
|
|
Login as the store owner, then navigate to any of their stores.
|
|
|
|
**WizaCorp (john.owner@wizacorp.com):**
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Terminal | `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/terminal` |
|
|
| Cards | `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/cards` |
|
|
| Settings | `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/settings` |
|
|
| Stats | `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/stats` |
|
|
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/enroll` |
|
|
|
|
**Fashion Group (jane.owner@fashiongroup.com):**
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Terminal | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/terminal` |
|
|
| Cards | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/cards` |
|
|
| Settings | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/settings` |
|
|
| Stats | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/stats` |
|
|
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/enroll` |
|
|
|
|
**BookWorld (bob.owner@bookworld.com):**
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Terminal | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/terminal` |
|
|
| Cards | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/cards` |
|
|
| Settings | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/settings` |
|
|
| Stats | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/stats` |
|
|
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/enroll` |
|
|
|
|
### 3. Customer Storefront Pages
|
|
|
|
Login as a customer (e.g., `customer1@wizamart.example.com`).
|
|
|
|
!!! note "Store domain required"
|
|
Storefront pages require a store domain context. Only WIZAMART (`wizamart.shop`)
|
|
and FASHIONHUB (`fashionhub.store`) have domains configured. In dev, storefront
|
|
routes may need to be accessed through the store's domain or platform path.
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Loyalty Dashboard | `http://localhost:9999/platforms/loyalty/account/loyalty` |
|
|
| Transaction History | `http://localhost:9999/platforms/loyalty/account/loyalty/history` |
|
|
|
|
### 4. Public Pages (No Auth)
|
|
|
|
| Page | Dev URL |
|
|
|------|---------|
|
|
| Self-Enrollment | `http://localhost:9999/platforms/loyalty/loyalty/join` |
|
|
| Enrollment Success | `http://localhost:9999/platforms/loyalty/loyalty/join/success` |
|
|
|
|
### 5. API Endpoints
|
|
|
|
**Admin API** (prefix: `/platforms/loyalty/api/admin/loyalty/`):
|
|
|
|
| Method | Dev URL |
|
|
|--------|---------|
|
|
| GET | `http://localhost:9999/platforms/loyalty/api/admin/loyalty/programs` |
|
|
| GET | `http://localhost:9999/platforms/loyalty/api/admin/loyalty/stats` |
|
|
|
|
**Store API** (prefix: `/platforms/loyalty/api/store/loyalty/`):
|
|
|
|
| Method | Endpoint | Dev URL |
|
|
|--------|----------|---------|
|
|
| GET | program | `http://localhost:9999/platforms/loyalty/api/store/loyalty/program` |
|
|
| POST | program | `http://localhost:9999/platforms/loyalty/api/store/loyalty/program` |
|
|
| POST | stamp | `http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp` |
|
|
| POST | points | `http://localhost:9999/platforms/loyalty/api/store/loyalty/points` |
|
|
| POST | enroll | `http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/enroll` |
|
|
| POST | lookup | `http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup` |
|
|
|
|
**Storefront API** (prefix: `/platforms/loyalty/api/storefront/`):
|
|
|
|
| Method | Endpoint | Dev URL |
|
|
|--------|----------|---------|
|
|
| GET | program | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/program` |
|
|
| POST | enroll | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/enroll` |
|
|
| GET | card | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/card` |
|
|
| GET | transactions | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/transactions` |
|
|
|
|
**Public API** (prefix: `/platforms/loyalty/api/loyalty/`):
|
|
|
|
| Method | Endpoint | Dev URL |
|
|
|--------|----------|---------|
|
|
| GET | program | `http://localhost:9999/platforms/loyalty/api/loyalty/programs/WIZAMART` |
|
|
|
|
---
|
|
|
|
## Production URLs (loyalty.lu)
|
|
|
|
In production, the platform uses **domain-based routing** instead of the `/platforms/loyalty/` path prefix.
|
|
Store context is detected via **custom domains** (registered in `store_domains` table)
|
|
or **subdomains** of `loyalty.lu` (from `Store.subdomain`).
|
|
|
|
### URL Routing Summary
|
|
|
|
| Routing mode | Pattern | Example |
|
|
|-------------|---------|---------|
|
|
| Platform domain | `loyalty.lu/...` | Admin pages, public API |
|
|
| Custom domain | `{custom_domain}/...` | All store pages (store has custom domain) |
|
|
| Store subdomain | `{store_code}.loyalty.lu/...` | All store pages (no custom domain) |
|
|
|
|
### Case 1: Store with custom domain (e.g., `wizamart.shop`)
|
|
|
|
The store has a verified entry in the `store_domains` table. **All** store URLs
|
|
(storefront, store backend, store APIs) are served from the custom domain.
|
|
|
|
**Storefront (customer-facing):**
|
|
|
|
| Page | Production URL |
|
|
|------|----------------|
|
|
| Loyalty Dashboard | `https://wizamart.shop/account/loyalty` |
|
|
| Transaction History | `https://wizamart.shop/account/loyalty/history` |
|
|
| Self-Enrollment | `https://wizamart.shop/loyalty/join` |
|
|
| Enrollment Success | `https://wizamart.shop/loyalty/join/success` |
|
|
|
|
**Storefront API:**
|
|
|
|
| Method | Production URL |
|
|
|--------|----------------|
|
|
| GET card | `https://wizamart.shop/api/storefront/loyalty/card` |
|
|
| GET transactions | `https://wizamart.shop/api/storefront/loyalty/transactions` |
|
|
| POST enroll | `https://wizamart.shop/api/storefront/loyalty/enroll` |
|
|
| GET program | `https://wizamart.shop/api/storefront/loyalty/program` |
|
|
|
|
**Store backend (staff/owner):**
|
|
|
|
| Page | Production URL |
|
|
|------|----------------|
|
|
| Store Login | `https://wizamart.shop/store/WIZAMART/login` |
|
|
| Terminal | `https://wizamart.shop/store/WIZAMART/loyalty/terminal` |
|
|
| Cards | `https://wizamart.shop/store/WIZAMART/loyalty/cards` |
|
|
| Card Detail | `https://wizamart.shop/store/WIZAMART/loyalty/cards/{card_id}` |
|
|
| Settings | `https://wizamart.shop/store/WIZAMART/loyalty/settings` |
|
|
| Stats | `https://wizamart.shop/store/WIZAMART/loyalty/stats` |
|
|
| Enroll Customer | `https://wizamart.shop/store/WIZAMART/loyalty/enroll` |
|
|
|
|
**Store API:**
|
|
|
|
| Method | Production URL |
|
|
|--------|----------------|
|
|
| GET program | `https://wizamart.shop/api/store/loyalty/program` |
|
|
| POST program | `https://wizamart.shop/api/store/loyalty/program` |
|
|
| POST stamp | `https://wizamart.shop/api/store/loyalty/stamp` |
|
|
| POST points | `https://wizamart.shop/api/store/loyalty/points` |
|
|
| POST enroll | `https://wizamart.shop/api/store/loyalty/cards/enroll` |
|
|
| POST lookup | `https://wizamart.shop/api/store/loyalty/cards/lookup` |
|
|
|
|
### Case 2: Store without custom domain (uses platform subdomain)
|
|
|
|
The store has no entry in `store_domains`. **All** store URLs are served via a
|
|
subdomain of the platform domain: `{store_code}.loyalty.lu`.
|
|
|
|
**Storefront (customer-facing):**
|
|
|
|
| Page | Production URL |
|
|
|------|----------------|
|
|
| Loyalty Dashboard | `https://bookstore.loyalty.lu/account/loyalty` |
|
|
| Transaction History | `https://bookstore.loyalty.lu/account/loyalty/history` |
|
|
| Self-Enrollment | `https://bookstore.loyalty.lu/loyalty/join` |
|
|
| Enrollment Success | `https://bookstore.loyalty.lu/loyalty/join/success` |
|
|
|
|
**Storefront API:**
|
|
|
|
| Method | Production URL |
|
|
|--------|----------------|
|
|
| GET card | `https://bookstore.loyalty.lu/api/storefront/loyalty/card` |
|
|
| GET transactions | `https://bookstore.loyalty.lu/api/storefront/loyalty/transactions` |
|
|
| POST enroll | `https://bookstore.loyalty.lu/api/storefront/loyalty/enroll` |
|
|
| GET program | `https://bookstore.loyalty.lu/api/storefront/loyalty/program` |
|
|
|
|
**Store backend (staff/owner):**
|
|
|
|
| Page | Production URL |
|
|
|------|----------------|
|
|
| Store Login | `https://bookstore.loyalty.lu/store/BOOKSTORE/login` |
|
|
| Terminal | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/terminal` |
|
|
| Cards | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/cards` |
|
|
| Settings | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/settings` |
|
|
| Stats | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/stats` |
|
|
|
|
**Store API:**
|
|
|
|
| Method | Production URL |
|
|
|--------|----------------|
|
|
| GET program | `https://bookstore.loyalty.lu/api/store/loyalty/program` |
|
|
| POST stamp | `https://bookstore.loyalty.lu/api/store/loyalty/stamp` |
|
|
| POST points | `https://bookstore.loyalty.lu/api/store/loyalty/points` |
|
|
| POST enroll | `https://bookstore.loyalty.lu/api/store/loyalty/cards/enroll` |
|
|
| POST lookup | `https://bookstore.loyalty.lu/api/store/loyalty/cards/lookup` |
|
|
|
|
### Platform Admin & Public API (always on platform domain)
|
|
|
|
| Page / Endpoint | Production URL |
|
|
|-----------------|----------------|
|
|
| Admin Programs | `https://loyalty.lu/admin/loyalty/programs` |
|
|
| Admin Analytics | `https://loyalty.lu/admin/loyalty/analytics` |
|
|
| Admin Merchant Detail | `https://loyalty.lu/admin/loyalty/merchants/{id}` |
|
|
| Admin Merchant Settings | `https://loyalty.lu/admin/loyalty/merchants/{id}/settings` |
|
|
| Admin API - Programs | `GET https://loyalty.lu/api/admin/loyalty/programs` |
|
|
| Admin API - Stats | `GET https://loyalty.lu/api/admin/loyalty/stats` |
|
|
| Public API - Program | `GET https://loyalty.lu/api/loyalty/programs/WIZAMART` |
|
|
| Apple Wallet Pass | `GET https://loyalty.lu/api/loyalty/passes/apple/{serial}.pkpass` |
|
|
|
|
### Domain configuration per store (current DB state)
|
|
|
|
| Store | Custom Domain | Production URL |
|
|
|-------|---------------|----------------|
|
|
| WIZAMART | `wizamart.shop` | `https://wizamart.shop/...` |
|
|
| FASHIONHUB | `fashionhub.store` | `https://fashionhub.store/...` |
|
|
| WIZAGADGETS | _(none)_ | `https://wizagadgets.loyalty.lu/...` |
|
|
| WIZAHOME | _(none)_ | `https://wizahome.loyalty.lu/...` |
|
|
| FASHIONOUTLET | _(none)_ | `https://fashionoutlet.loyalty.lu/...` |
|
|
| BOOKSTORE | _(none)_ | `https://bookstore.loyalty.lu/...` |
|
|
| BOOKDIGITAL | _(none)_ | `https://bookdigital.loyalty.lu/...` |
|
|
|
|
!!! info "`{store_domain}` in journey URLs"
|
|
In the journeys below, `{store_domain}` refers to the store's resolved domain:
|
|
|
|
- **Custom domain**: `wizamart.shop` (from `store_domains` table)
|
|
- **Subdomain fallback**: `wizamart.loyalty.lu` (from `Store.subdomain` + platform domain)
|
|
|
|
---
|
|
|
|
## User Journeys
|
|
|
|
### Journey 1: Merchant Owner - First-Time Setup
|
|
|
|
**Persona:** Merchant Owner (e.g., john.owner@wizacorp.com)
|
|
**Goal:** Set up a loyalty program for their merchant
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Login as store owner] --> B[Navigate to store loyalty settings]
|
|
B --> C{Program exists?}
|
|
C -->|No| D[Create loyalty program]
|
|
D --> E[Choose type: stamps / points / hybrid]
|
|
E --> F[Configure program settings]
|
|
F --> G[Set branding - colors, logo]
|
|
G --> H[Configure anti-fraud settings]
|
|
H --> I[Create staff PINs]
|
|
I --> J[Program is live]
|
|
C -->|Yes| K[View/edit existing program]
|
|
```
|
|
|
|
**Steps:**
|
|
|
|
1. Login as `john.owner@wizacorp.com` at:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/login`
|
|
- Prod (custom domain): `https://wizamart.shop/store/WIZAMART/login`
|
|
- Prod (subdomain): `https://wizamart.loyalty.lu/store/WIZAMART/login`
|
|
2. Navigate to loyalty settings:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/settings`
|
|
- Prod (custom domain): `https://wizamart.shop/store/WIZAMART/loyalty/settings`
|
|
- Prod (subdomain): `https://wizamart.loyalty.lu/store/WIZAMART/loyalty/settings`
|
|
3. Create a new loyalty program:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/program`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/program`
|
|
4. Choose loyalty type (stamps, points, or hybrid)
|
|
5. Configure program parameters (stamp target, points-per-euro, rewards)
|
|
6. Set branding (card color, logo, hero image)
|
|
7. Configure anti-fraud (cooldown, daily limits, PIN requirements)
|
|
8. Create staff PINs:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/pins`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/pins`
|
|
9. Verify program is live - check from another store (same merchant):
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/settings`
|
|
- Prod (subdomain): `https://wizagadgets.loyalty.lu/store/WIZAGADGETS/loyalty/settings`
|
|
|
|
**Expected blockers in current state:**
|
|
|
|
- No subscriptions exist - feature gating may prevent program creation
|
|
- No loyalty programs exist - this is the first journey to test
|
|
|
|
---
|
|
|
|
### Journey 2: Store Staff - Daily Operations (Stamps)
|
|
|
|
**Persona:** Store Staff (e.g., alice.manager@wizacorp.com)
|
|
**Goal:** Process customer loyalty stamp transactions
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Open terminal] --> B[Customer presents card/QR]
|
|
B --> C[Scan/lookup card]
|
|
C --> D[Enter staff PIN]
|
|
D --> E[Add stamp]
|
|
E --> F{Target reached?}
|
|
F -->|Yes| G[Prompt: Redeem reward?]
|
|
G -->|Yes| H[Redeem stamps for reward]
|
|
G -->|No| I[Save for later]
|
|
F -->|No| J[Done - show updated count]
|
|
H --> J
|
|
I --> J
|
|
```
|
|
|
|
**Steps:**
|
|
|
|
1. Login as `alice.manager@wizacorp.com` and open the terminal:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/terminal`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/terminal`
|
|
2. Scan customer QR code or enter card number:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
|
3. Enter staff PIN for verification
|
|
4. Add stamp:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp`
|
|
5. If target reached, redeem reward:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/redeem`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/redeem`
|
|
6. View updated card:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/cards/{card_id}`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/cards/{card_id}`
|
|
7. Browse all cards:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/cards`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/cards`
|
|
|
|
**Anti-fraud scenarios to test:**
|
|
|
|
- Cooldown rejection (stamp within 15 min)
|
|
- Daily limit hit (max 5 stamps/day)
|
|
- PIN lockout (5 failed attempts)
|
|
|
|
---
|
|
|
|
### Journey 3: Store Staff - Daily Operations (Points)
|
|
|
|
**Persona:** Store Staff (e.g., alice.manager@wizacorp.com)
|
|
**Goal:** Process customer loyalty points from purchase
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Open terminal] --> B[Customer presents card]
|
|
B --> C[Scan/lookup card]
|
|
C --> D[Enter purchase amount]
|
|
D --> E[Enter staff PIN]
|
|
E --> F[Points calculated & added]
|
|
F --> G{Enough for reward?}
|
|
G -->|Yes| H[Offer redemption]
|
|
G -->|No| I[Done - show balance]
|
|
H --> I
|
|
```
|
|
|
|
**Steps:**
|
|
|
|
1. Open the terminal:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/terminal`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/terminal`
|
|
2. Lookup card:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
|
3. Enter purchase amount (e.g., 25.00 EUR)
|
|
4. Earn points (auto-calculated at 10 pts/EUR = 250 points):
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/points`
|
|
5. If enough balance, redeem points for reward:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points/redeem`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/points/redeem`
|
|
6. Check store-level stats:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/stats`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/stats`
|
|
|
|
---
|
|
|
|
### Journey 4: Customer Self-Enrollment
|
|
|
|
**Persona:** Anonymous Customer
|
|
**Goal:** Join a merchant's loyalty program
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[See QR code at store counter] --> B[Scan QR / visit enrollment page]
|
|
B --> C[Fill in details - email, name]
|
|
C --> D[Submit enrollment]
|
|
D --> E[Receive card number]
|
|
E --> F[Optional: Add to Apple/Google Wallet]
|
|
F --> G[Start collecting stamps/points]
|
|
```
|
|
|
|
**Steps:**
|
|
|
|
1. Visit the public enrollment page:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/loyalty/join`
|
|
- Prod (custom domain): `https://wizamart.shop/loyalty/join`
|
|
- Prod (subdomain): `https://bookstore.loyalty.lu/loyalty/join`
|
|
2. Fill in enrollment form (email, name)
|
|
3. Submit enrollment:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/storefront/loyalty/enroll`
|
|
- Prod (custom domain): `POST https://wizamart.shop/api/storefront/loyalty/enroll`
|
|
- Prod (subdomain): `POST https://bookstore.loyalty.lu/api/storefront/loyalty/enroll`
|
|
4. Redirected to success page:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
|
- Prod (custom domain): `https://wizamart.shop/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
|
- Prod (subdomain): `https://bookstore.loyalty.lu/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
|
5. Optionally download Apple Wallet pass:
|
|
- Dev: `GET http://localhost:9999/platforms/loyalty/api/loyalty/passes/apple/{serial_number}.pkpass`
|
|
- Prod: `GET https://loyalty.lu/api/loyalty/passes/apple/{serial_number}.pkpass`
|
|
|
|
---
|
|
|
|
### Journey 5: Customer - View Loyalty Status
|
|
|
|
**Persona:** Authenticated Customer (e.g., `customer1@wizamart.example.com`)
|
|
**Goal:** Check loyalty balance and history
|
|
|
|
**Steps:**
|
|
|
|
1. Login as customer at the storefront
|
|
2. View loyalty dashboard (card balance, available rewards):
|
|
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty`
|
|
- Prod (custom domain): `https://wizamart.shop/account/loyalty`
|
|
- Prod (subdomain): `https://bookstore.loyalty.lu/account/loyalty`
|
|
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/storefront/loyalty/card`
|
|
- API Prod: `GET https://wizamart.shop/api/storefront/loyalty/card`
|
|
3. View full transaction history:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty/history`
|
|
- Prod (custom domain): `https://wizamart.shop/account/loyalty/history`
|
|
- Prod (subdomain): `https://bookstore.loyalty.lu/account/loyalty/history`
|
|
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/storefront/loyalty/transactions`
|
|
- API Prod: `GET https://wizamart.shop/api/storefront/loyalty/transactions`
|
|
|
|
---
|
|
|
|
### Journey 6: Platform Admin - Oversight
|
|
|
|
**Persona:** Platform Admin (`admin@wizamart.com` or `samir.boulahtit@gmail.com`)
|
|
**Goal:** Monitor all loyalty programs across merchants
|
|
|
|
**Steps:**
|
|
|
|
1. Login as admin
|
|
2. View all programs:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/programs`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/programs`
|
|
3. View platform-wide analytics:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/analytics`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/analytics`
|
|
4. Drill into WizaCorp's program:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/merchants/1`
|
|
5. Manage WizaCorp's merchant-level settings:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/merchants/1/settings`
|
|
- API Dev: `PATCH http://localhost:9999/platforms/loyalty/api/admin/loyalty/merchants/1/settings`
|
|
- API Prod: `PATCH https://loyalty.lu/api/admin/loyalty/merchants/1/settings`
|
|
6. Adjust settings: PIN policy, self-enrollment toggle, void permissions
|
|
7. Check other merchants:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/merchants/2`
|
|
|
|
---
|
|
|
|
### Journey 7: Void / Return Flow
|
|
|
|
**Persona:** Store Staff (e.g., alice.manager@wizacorp.com)
|
|
**Goal:** Reverse a loyalty transaction (customer return)
|
|
|
|
**Steps:**
|
|
|
|
1. Open terminal and lookup card:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/terminal`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/terminal`
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
|
2. View the card's transaction history to find the transaction to void:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/cards/{card_id}`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/cards/{card_id}`
|
|
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/{card_id}/transactions`
|
|
- API Prod: `GET https://{store_domain}/api/store/loyalty/cards/{card_id}/transactions`
|
|
3. Void a stamp transaction:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/void`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/void`
|
|
4. Or void a points transaction:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points/void`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/points/void`
|
|
5. Verify: original and void transactions are linked in the audit log
|
|
|
|
---
|
|
|
|
### Journey 8: Cross-Store Redemption
|
|
|
|
**Persona:** Customer + Store Staff at two different stores
|
|
**Goal:** Customer earns at Store A, redeems at Store B (same merchant)
|
|
|
|
**Precondition:** Cross-location redemption must be enabled in merchant settings:
|
|
|
|
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
|
- Prod: `https://loyalty.lu/admin/loyalty/merchants/1/settings`
|
|
|
|
**Steps:**
|
|
|
|
1. Staff at WIZAMART adds stamps to customer's card:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAMART/loyalty/terminal`
|
|
- Prod: `https://{store_domain}/store/WIZAMART/loyalty/terminal`
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp`
|
|
2. Customer visits WIZAGADGETS
|
|
3. Staff at WIZAGADGETS looks up the same card:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/terminal`
|
|
- Prod: `https://{store_domain}/store/WIZAGADGETS/loyalty/terminal`
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
|
4. Card is found (same merchant) with accumulated stamps
|
|
5. Staff at WIZAGADGETS redeems the reward:
|
|
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/redeem`
|
|
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/redeem`
|
|
6. Verify transaction history shows both stores:
|
|
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/cards/{card_id}`
|
|
- Prod: `https://{store_domain}/store/WIZAGADGETS/loyalty/cards/{card_id}`
|
|
|
|
---
|
|
|
|
## Recommended Test Order
|
|
|
|
1. **Journey 1** - Create a program first (nothing else works without this)
|
|
2. **Journey 4** - Enroll a test customer
|
|
3. **Journey 2 or 3** - Process stamps/points
|
|
4. **Journey 5** - Verify customer can see their data
|
|
5. **Journey 7** - Test void/return
|
|
6. **Journey 8** - Test cross-store (enroll via WIZAMART, redeem via WIZAGADGETS)
|
|
7. **Journey 6** - Admin overview (verify data appears correctly)
|