feat(loyalty): implement complete loyalty module MVP
Add stamp-based and points-based loyalty programs for vendors with: Database Models (5 tables): - loyalty_programs: Vendor program configuration - loyalty_cards: Customer cards with stamp/point balances - loyalty_transactions: Immutable audit log - staff_pins: Fraud prevention PINs (bcrypt hashed) - apple_device_registrations: Apple Wallet push tokens Services: - program_service: Program CRUD and statistics - card_service: Customer enrollment and card lookup - stamp_service: Stamp operations with anti-fraud checks - points_service: Points earning and redemption - pin_service: Staff PIN management with lockout - wallet_service: Unified wallet abstraction - google_wallet_service: Google Wallet API integration - apple_wallet_service: Apple Wallet .pkpass generation API Routes: - Admin: /api/v1/admin/loyalty/* (programs list, stats) - Vendor: /api/v1/vendor/loyalty/* (stamp, points, cards, PINs) - Public: /api/v1/loyalty/* (enrollment, Apple Web Service) Anti-Fraud Features: - Staff PIN verification (configurable per program) - Cooldown period between stamps (default 15 min) - Daily stamp limits (default 5/day) - PIN lockout after failed attempts Wallet Integration: - Google Wallet: LoyaltyClass and LoyaltyObject management - Apple Wallet: .pkpass generation with PKCS#7 signing - Apple Web Service endpoints for device registration/updates Also includes: - Alembic migration for all tables with indexes - Localization files (en, fr, de, lu) - Module documentation - Phase 2 interface and user journey plan Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
275
docs/modules/loyalty.md
Normal file
275
docs/modules/loyalty.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Loyalty Module
|
||||
|
||||
The Loyalty Module provides stamp-based and points-based loyalty programs for Wizamart vendors with Google Wallet and Apple Wallet integration.
|
||||
|
||||
## Overview
|
||||
|
||||
| Aspect | Description |
|
||||
|--------|-------------|
|
||||
| Module Code | `loyalty` |
|
||||
| Dependencies | `customers` |
|
||||
| Status | Phase 1 MVP Complete |
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Stamp-based loyalty**: Collect N stamps, get a reward (e.g., "Buy 10 coffees, get 1 free")
|
||||
- **Points-based loyalty**: Earn points per euro spent, redeem for rewards
|
||||
- **Hybrid programs**: Support both stamps and points simultaneously
|
||||
- **Anti-fraud system**: Staff PINs, cooldown periods, daily limits, lockout protection
|
||||
- **Wallet integration**: Google Wallet and Apple Wallet pass generation
|
||||
- **Full audit trail**: Transaction logging with IP, user agent, and staff attribution
|
||||
|
||||
## Entity Model
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ Vendor │───────│ LoyaltyProgram │
|
||||
└─────────────────┘ 1:1 └─────────────────┘
|
||||
│
|
||||
┌───────────┼───────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ StaffPin │ │LoyaltyCard│ │ (config)│
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────┐
|
||||
└───▶│ Transaction │
|
||||
└──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│AppleDeviceRegistration│
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
### Database Tables
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `loyalty_programs` | Vendor's program configuration (type, targets, branding) |
|
||||
| `loyalty_cards` | Customer cards with stamp/point balances |
|
||||
| `loyalty_transactions` | Immutable audit log of all operations |
|
||||
| `staff_pins` | Hashed PINs for fraud prevention |
|
||||
| `apple_device_registrations` | Apple Wallet push notification tokens |
|
||||
|
||||
## Configuration
|
||||
|
||||
Environment variables (prefix: `LOYALTY_`):
|
||||
|
||||
```bash
|
||||
# Anti-fraud defaults
|
||||
LOYALTY_DEFAULT_COOLDOWN_MINUTES=15
|
||||
LOYALTY_MAX_DAILY_STAMPS=5
|
||||
LOYALTY_PIN_MAX_FAILED_ATTEMPTS=5
|
||||
LOYALTY_PIN_LOCKOUT_MINUTES=30
|
||||
|
||||
# Points
|
||||
LOYALTY_DEFAULT_POINTS_PER_EURO=10
|
||||
|
||||
# Google Wallet
|
||||
LOYALTY_GOOGLE_ISSUER_ID=3388000000012345678
|
||||
LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON=/path/to/service-account.json
|
||||
|
||||
# Apple Wallet
|
||||
LOYALTY_APPLE_PASS_TYPE_ID=pass.com.example.loyalty
|
||||
LOYALTY_APPLE_TEAM_ID=ABCD1234
|
||||
LOYALTY_APPLE_WWDR_CERT_PATH=/path/to/wwdr.pem
|
||||
LOYALTY_APPLE_SIGNER_CERT_PATH=/path/to/signer.pem
|
||||
LOYALTY_APPLE_SIGNER_KEY_PATH=/path/to/signer.key
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Vendor Endpoints (`/api/v1/vendor/loyalty/`)
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/program` | Get vendor's loyalty program |
|
||||
| `POST` | `/program` | Create loyalty program |
|
||||
| `PATCH` | `/program` | Update loyalty program |
|
||||
| `GET` | `/stats` | Get program statistics |
|
||||
| `GET` | `/cards` | List customer cards |
|
||||
| `POST` | `/cards/enroll` | Enroll customer in program |
|
||||
| `POST` | `/cards/lookup` | Look up card by QR/number |
|
||||
| `POST` | `/stamp` | Add stamp to card |
|
||||
| `POST` | `/stamp/redeem` | Redeem stamps for reward |
|
||||
| `POST` | `/points` | Earn points from purchase |
|
||||
| `POST` | `/points/redeem` | Redeem points for reward |
|
||||
| `GET` | `/pins` | List staff PINs |
|
||||
| `POST` | `/pins` | Create staff PIN |
|
||||
| `PATCH` | `/pins/{id}` | Update staff PIN |
|
||||
| `DELETE` | `/pins/{id}` | Delete staff PIN |
|
||||
| `POST` | `/pins/{id}/unlock` | Unlock locked PIN |
|
||||
|
||||
### Admin Endpoints (`/api/v1/admin/loyalty/`)
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/programs` | List all loyalty programs |
|
||||
| `GET` | `/programs/{id}` | Get specific program |
|
||||
| `GET` | `/programs/{id}/stats` | Get program statistics |
|
||||
| `GET` | `/stats` | Platform-wide statistics |
|
||||
|
||||
### Public Endpoints (`/api/v1/loyalty/`)
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/programs/{vendor_code}` | Get program info for enrollment |
|
||||
| `GET` | `/passes/apple/{serial}.pkpass` | Download Apple Wallet pass |
|
||||
| `POST` | `/apple/v1/devices/...` | Apple Web Service: register device |
|
||||
| `DELETE` | `/apple/v1/devices/...` | Apple Web Service: unregister |
|
||||
| `GET` | `/apple/v1/devices/...` | Apple Web Service: get updates |
|
||||
| `GET` | `/apple/v1/passes/...` | Apple Web Service: get pass |
|
||||
|
||||
## Anti-Fraud System
|
||||
|
||||
### Staff PIN Verification
|
||||
|
||||
All stamp and points operations require staff PIN verification (configurable per program).
|
||||
|
||||
```python
|
||||
# PIN is hashed with bcrypt
|
||||
pin.set_pin("1234") # Stores bcrypt hash
|
||||
pin.verify_pin("1234") # Returns True/False
|
||||
```
|
||||
|
||||
### Lockout Protection
|
||||
|
||||
- **Max failed attempts**: 5 (configurable)
|
||||
- **Lockout duration**: 30 minutes (configurable)
|
||||
- After lockout expires, PIN can be used again
|
||||
- Admin can manually unlock via API
|
||||
|
||||
### Cooldown Period
|
||||
|
||||
Prevents rapid stamp collection (fraud prevention):
|
||||
|
||||
```
|
||||
Customer scans card → Gets stamp → Must wait 15 minutes → Can get next stamp
|
||||
```
|
||||
|
||||
### Daily Limits
|
||||
|
||||
Maximum stamps per card per day (default: 5).
|
||||
|
||||
## Wallet Integration
|
||||
|
||||
### Google Wallet
|
||||
|
||||
Architecture: **Server-side storage with API updates**
|
||||
|
||||
1. Program created → Create `LoyaltyClass` via Google API
|
||||
2. Customer enrolls → Create `LoyaltyObject` via Google API
|
||||
3. Stamp/points change → `PATCH` the object
|
||||
4. Generate JWT for "Add to Wallet" button
|
||||
|
||||
No device registration needed - Google syncs automatically.
|
||||
|
||||
### Apple Wallet
|
||||
|
||||
Architecture: **Push notification model**
|
||||
|
||||
1. Customer adds pass → Device registers with our server
|
||||
2. Stamp/points change → Send push notification to APNs
|
||||
3. Device receives push → Fetches updated pass from our server
|
||||
|
||||
Requires `apple_device_registrations` table for push tokens.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Create a Loyalty Program
|
||||
|
||||
```python
|
||||
from app.modules.loyalty.services import program_service
|
||||
from app.modules.loyalty.schemas import ProgramCreate
|
||||
|
||||
data = ProgramCreate(
|
||||
loyalty_type="stamps",
|
||||
stamps_target=10,
|
||||
stamps_reward_description="Free coffee",
|
||||
cooldown_minutes=15,
|
||||
max_daily_stamps=5,
|
||||
require_staff_pin=True,
|
||||
card_color="#4F46E5",
|
||||
)
|
||||
|
||||
program = program_service.create_program(db, vendor_id=1, data=data)
|
||||
```
|
||||
|
||||
### Enroll a Customer
|
||||
|
||||
```python
|
||||
from app.modules.loyalty.services import card_service
|
||||
|
||||
card = card_service.enroll_customer(db, customer_id=123, vendor_id=1)
|
||||
# Returns LoyaltyCard with unique card_number and qr_code_data
|
||||
```
|
||||
|
||||
### Add a Stamp
|
||||
|
||||
```python
|
||||
from app.modules.loyalty.services import stamp_service
|
||||
|
||||
result = stamp_service.add_stamp(
|
||||
db,
|
||||
qr_code="abc123xyz",
|
||||
staff_pin="1234",
|
||||
ip_address="192.168.1.1",
|
||||
)
|
||||
# Returns dict with stamp_count, reward_earned, next_stamp_available_at, etc.
|
||||
```
|
||||
|
||||
### Earn Points from Purchase
|
||||
|
||||
```python
|
||||
from app.modules.loyalty.services import points_service
|
||||
|
||||
result = points_service.earn_points(
|
||||
db,
|
||||
card_number="123456789012",
|
||||
purchase_amount_cents=2500, # €25.00
|
||||
order_reference="ORD-12345",
|
||||
staff_pin="1234",
|
||||
)
|
||||
# Returns dict with points_earned (250 at 10pts/€), points_balance, etc.
|
||||
```
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Purpose |
|
||||
|---------|---------|
|
||||
| `program_service` | Program CRUD and statistics |
|
||||
| `card_service` | Card enrollment, lookup, management |
|
||||
| `stamp_service` | Stamp operations with anti-fraud |
|
||||
| `points_service` | Points operations and redemption |
|
||||
| `pin_service` | Staff PIN CRUD and verification |
|
||||
| `wallet_service` | Unified wallet abstraction |
|
||||
| `google_wallet_service` | Google Wallet API integration |
|
||||
| `apple_wallet_service` | Apple Wallet pass generation |
|
||||
|
||||
## Scheduled Tasks
|
||||
|
||||
| Task | Schedule | Description |
|
||||
|------|----------|-------------|
|
||||
| `loyalty.sync_wallet_passes` | Hourly | Sync cards that missed real-time updates |
|
||||
| `loyalty.expire_points` | Daily 02:00 | Expire old points (future enhancement) |
|
||||
|
||||
## Localization
|
||||
|
||||
Available in 4 languages:
|
||||
- English (`en.json`)
|
||||
- French (`fr.json`)
|
||||
- German (`de.json`)
|
||||
- Luxembourgish (`lu.json`)
|
||||
|
||||
## Future Enhancements (Phase 2)
|
||||
|
||||
- Rewards catalog with configurable tiers
|
||||
- Customer tiers (Bronze/Silver/Gold)
|
||||
- Referral program
|
||||
- Gamification (spin wheel, scratch cards)
|
||||
- POS integration
|
||||
- Points expiration rules
|
||||
- Batch import of existing loyalty cards
|
||||
670
docs/proposals/loyalty-phase2-interfaces-plan.md
Normal file
670
docs/proposals/loyalty-phase2-interfaces-plan.md
Normal file
@@ -0,0 +1,670 @@
|
||||
# Loyalty Module Phase 2: Admin & Vendor Interfaces
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document outlines the plan for building admin and vendor interfaces for the Loyalty Module, along with detailed user journeys for stamp-based and points-based loyalty programs. The design follows market best practices from leading loyalty platforms (Square Loyalty, Toast, Fivestars, Belly, Punchh).
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Interface Design
|
||||
|
||||
### 1.1 Vendor Dashboard (Retail Store)
|
||||
|
||||
#### Main Loyalty Dashboard (`/vendor/loyalty`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 Loyalty Program [Setup] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐│
|
||||
│ │ 1,247 │ │ 892 │ │ 156 │ │ €2.3k ││
|
||||
│ │ Members │ │ Active │ │ Redeemed │ │ Saved ││
|
||||
│ │ Total │ │ 30 days │ │ This Month │ │ Value ││
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘│
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ 📊 Activity Chart (Last 30 Days) ││
|
||||
│ │ [Stamps Issued] [Rewards Redeemed] [New Members] ││
|
||||
│ │ ═══════════════════════════════════════════════ ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
|
||||
│ │ 🔥 Quick Actions │ │ 📋 Recent Activity │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ [➕ Add Stamp] │ │ • John D. earned stamp #8 │ │
|
||||
│ │ [🎁 Redeem Reward] │ │ • Marie L. redeemed reward │ │
|
||||
│ │ [👤 Enroll Customer] │ │ • Alex K. joined program │ │
|
||||
│ │ [🔍 Look Up Card] │ │ • Sarah M. earned 50 pts │ │
|
||||
│ │ │ │ │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Stamp/Points Terminal (`/vendor/loyalty/terminal`)
|
||||
|
||||
**Primary interface for daily operations - optimized for tablet/touchscreen:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 Loyalty Terminal │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 📷 SCAN QR CODE │ │
|
||||
│ │ │ │
|
||||
│ │ [Camera Viewfinder Area] │ │
|
||||
│ │ │ │
|
||||
│ │ or enter card number │ │
|
||||
│ │ ┌─────────────────────────┐ │ │
|
||||
│ │ │ Card Number... │ │ │
|
||||
│ │ └─────────────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Use Camera] [Enter Manually] [Recent Cards ▼] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**After scanning - Customer Card View:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ← Back Customer Card │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────┐ │
|
||||
│ │ 👤 Marie Laurent │ │
|
||||
│ │ marie.laurent@email.com │ │
|
||||
│ │ Member since: Jan 2024 │ │
|
||||
│ └─────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ☕ ☕ ☕ ☕ ☕ ☕ ☕ ☕ ○ ○ │ │
|
||||
│ │ │ │
|
||||
│ │ 8 / 10 stamps │ │
|
||||
│ │ 2 more until FREE COFFEE │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ [ ➕ ADD STAMP ] [ 🎁 REDEEM ] │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ⚠️ Next stamp available in 12 minutes │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**PIN Entry Modal (appears when adding stamp):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Enter Staff PIN │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ ● ● ● ● │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ 1 │ │ 2 │ │ 3 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ 4 │ │ 5 │ │ 6 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ 7 │ │ 8 │ │ 9 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ ⌫ │ │ 0 │ │ ✓ │ │
|
||||
│ └─────┘ └─────┘ └─────┘ │
|
||||
│ │
|
||||
│ [Cancel] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Program Setup (`/vendor/loyalty/settings`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ⚙️ Loyalty Program Settings │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Program Type │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ ☑️ Stamps │ │ ☐ Points │ │ ☐ Hybrid │ │
|
||||
│ │ Buy 10 Get 1 │ │ Earn per € │ │ Both systems │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Stamp Configuration │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Stamps needed for reward: [ 10 ▼ ] │ │
|
||||
│ │ Reward description: [ Free coffee of choice ] │ │
|
||||
│ │ Reward value (optional): [ €4.50 ] │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ 🛡️ Fraud Prevention │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ ☑️ Require staff PIN for operations │ │
|
||||
│ │ Cooldown between stamps: [ 15 ] minutes │ │
|
||||
│ │ Max stamps per day: [ 5 ] │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ 🎨 Card Branding │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Card name: [ Café Loyalty Card ] │ │
|
||||
│ │ Primary color: [████] #4F46E5 │ │
|
||||
│ │ Logo: [Upload] cafe-logo.png ✓ │ │
|
||||
│ │ │ │
|
||||
│ │ Preview: ┌────────────────────┐ │ │
|
||||
│ │ │ ☕ Café Loyalty │ │ │
|
||||
│ │ │ ████████░░ │ │ │
|
||||
│ │ │ 8/10 stamps │ │ │
|
||||
│ │ └────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Cancel] [Save Changes] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Staff PIN Management (`/vendor/loyalty/pins`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🔐 Staff PINs [+ Add PIN] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ 👤 Marie (Manager) [Edit] [🗑️] │ │
|
||||
│ │ Last used: Today, 14:32 │ │
|
||||
│ │ Status: ✅ Active │ │
|
||||
│ ├─────────────────────────────────────────────────────────┤ │
|
||||
│ │ 👤 Thomas (Staff) [Edit] [🗑️] │ │
|
||||
│ │ Last used: Today, 11:15 │ │
|
||||
│ │ Status: ✅ Active │ │
|
||||
│ ├─────────────────────────────────────────────────────────┤ │
|
||||
│ │ 👤 Julie (Staff) [Edit] [🗑️] │ │
|
||||
│ │ Last used: Yesterday │ │
|
||||
│ │ Status: 🔒 Locked (3 failed attempts) [Unlock] │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ℹ️ Staff PINs prevent unauthorized stamp/point operations. │
|
||||
│ PINs are locked after 5 failed attempts for 30 minutes. │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Customer Cards List (`/vendor/loyalty/cards`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 👥 Loyalty Members 🔍 [Search...] [Export]│
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Filter: [All ▼] [Active ▼] [Has Reward Ready ▼] │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ Customer │ Card # │ Stamps │ Last Visit │ ⋮ ││
|
||||
│ ├───────────────────┼──────────────┼────────┼────────────┼────┤│
|
||||
│ │ Marie Laurent │ 4821-7493 │ 8/10 ⭐│ Today │ ⋮ ││
|
||||
│ │ Jean Dupont │ 4821-2847 │ 10/10 🎁│ Yesterday │ ⋮ ││
|
||||
│ │ Sophie Martin │ 4821-9382 │ 3/10 │ 3 days ago │ ⋮ ││
|
||||
│ │ Pierre Bernard │ 4821-1029 │ 6/10 │ 1 week ago │ ⋮ ││
|
||||
│ │ ... │ ... │ ... │ ... │ ⋮ ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
│ Showing 1-20 of 1,247 members [← Prev] [1] [2] [Next →]│
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 Admin Dashboard (Platform)
|
||||
|
||||
#### Platform Loyalty Overview (`/admin/loyalty`)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 Loyalty Programs Platform │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐│
|
||||
│ │ 47 │ │ 38 │ │ 12,847 │ │ €47k ││
|
||||
│ │ Programs │ │ Active │ │ Members │ │ Saved ││
|
||||
│ │ Total │ │ Programs │ │ Total │ │ Value ││
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘│
|
||||
│ │
|
||||
│ Programs by Type: │
|
||||
│ ═══════════════════════════════════════ │
|
||||
│ Stamps: ████████████████████ 32 (68%) │
|
||||
│ Points: ███████ 11 (23%) │
|
||||
│ Hybrid: ████ 4 (9%) │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ Vendor │ Type │ Members │ Activity │ Status ││
|
||||
│ ├───────────────────┼─────────┼─────────┼──────────┼──────────┤│
|
||||
│ │ Café du Coin │ Stamps │ 1,247 │ High │ ✅ Active││
|
||||
│ │ Boulangerie Paul │ Points │ 892 │ Medium │ ✅ Active││
|
||||
│ │ Pizza Roma │ Stamps │ 456 │ Low │ ⚠️ Setup ││
|
||||
│ │ ... │ ... │ ... │ ... │ ... ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 2: User Journeys
|
||||
|
||||
### 2.1 Stamp-Based Loyalty Journey
|
||||
|
||||
#### Customer Journey: Enrollment
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ STAMP LOYALTY - ENROLLMENT │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ DISCOVER│────▶│ JOIN │────▶│ SAVE │────▶│ USE │
|
||||
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 1. Customer sees │ 2. Scans QR at │ 3. Card added │ 4. Ready to │
|
||||
│ sign at counter│ register or │ to Google/ │ collect │
|
||||
│ "Join our │ gives email │ Apple Wallet│ stamps! │
|
||||
│ loyalty!" │ to cashier │ │ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Detailed Steps:**
|
||||
|
||||
1. **Discovery** (In-Store)
|
||||
- Customer sees loyalty program signage/tent card
|
||||
- QR code displayed at counter
|
||||
- Staff mentions program during checkout
|
||||
|
||||
2. **Sign Up** (30 seconds)
|
||||
- Customer scans QR code with phone
|
||||
- Lands on mobile enrollment page
|
||||
- Enters: Email (required), Name (optional)
|
||||
- Accepts terms with checkbox
|
||||
- Submits
|
||||
|
||||
3. **Card Creation** (Instant)
|
||||
- System creates loyalty card
|
||||
- Generates unique card number & QR code
|
||||
- Shows "Add to Wallet" buttons
|
||||
- Sends welcome email with card link
|
||||
|
||||
4. **Wallet Save** (Optional but encouraged)
|
||||
- Customer taps "Add to Google Wallet" or "Add to Apple Wallet"
|
||||
- Pass appears in their wallet app
|
||||
- Always accessible, works offline
|
||||
|
||||
#### Customer Journey: Earning Stamps
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ STAMP LOYALTY - EARNING │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Customer Staff System Wallet
|
||||
│ │ │ │
|
||||
│ 1. Makes │ │ │
|
||||
│ purchase │ │ │
|
||||
│───────────────▶│ │ │
|
||||
│ │ │ │
|
||||
│ 2. Shows │ │ │
|
||||
│ loyalty card │ │ │
|
||||
│───────────────▶│ │ │
|
||||
│ │ 3. Scans QR │ │
|
||||
│ │─────────────────▶│ │
|
||||
│ │ │ │
|
||||
│ │ 4. Enters PIN │ │
|
||||
│ │─────────────────▶│ │
|
||||
│ │ │ │
|
||||
│ │ 5. Confirms │ │
|
||||
│ │◀─────────────────│ │
|
||||
│ │ "Stamp added!" │ │
|
||||
│ │ │ │
|
||||
│ 6. Verbal │ │ 7. Push │
|
||||
│ confirmation │ │ notification │
|
||||
│◀───────────────│ │────────────────▶│
|
||||
│ │ │ │
|
||||
│ │ 8. Pass updates│
|
||||
│◀───────────────────────────────────│────────────────▶│
|
||||
│ "8/10 stamps" │ │
|
||||
```
|
||||
|
||||
**Anti-Fraud Checks (Automatic):**
|
||||
|
||||
1. ✅ Card is active
|
||||
2. ✅ Program is active
|
||||
3. ✅ Staff PIN is valid
|
||||
4. ✅ Cooldown period elapsed (15 min since last stamp)
|
||||
5. ✅ Daily limit not reached (max 5/day)
|
||||
|
||||
**Success Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"stamp_count": 8,
|
||||
"stamps_target": 10,
|
||||
"stamps_until_reward": 2,
|
||||
"message": "2 more stamps until your free coffee!",
|
||||
"next_stamp_available": "2024-01-28T15:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Customer Journey: Redeeming Reward
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ STAMP LOYALTY - REDEMPTION │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Customer Staff System
|
||||
│ │ │
|
||||
│ 1. "I'd like │ │
|
||||
│ to redeem my │ │
|
||||
│ free coffee" │ │
|
||||
│───────────────▶│ │
|
||||
│ │ │
|
||||
│ 2. Shows card │ │
|
||||
│ (10/10 stamps)│ │
|
||||
│───────────────▶│ │
|
||||
│ │ 3. Scans + sees │
|
||||
│ │ "REWARD READY" │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 4. Clicks │
|
||||
│ │ [REDEEM REWARD] │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 5. Enters PIN │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 6. Confirms │
|
||||
│ │◀─────────────────│
|
||||
│ │ "Reward redeemed"│
|
||||
│ │ Stamps reset: 0 │
|
||||
│ │ │
|
||||
│ 7. Gives free │ │
|
||||
│ coffee │ │
|
||||
│◀───────────────│ │
|
||||
│ │ │
|
||||
│ 🎉 HAPPY │ │
|
||||
│ CUSTOMER! │ │
|
||||
```
|
||||
|
||||
### 2.2 Points-Based Loyalty Journey
|
||||
|
||||
#### Customer Journey: Earning Points
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ POINTS LOYALTY - EARNING │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Customer Staff System
|
||||
│ │ │
|
||||
│ 1. Purchases │ │
|
||||
│ €25.00 order │ │
|
||||
│───────────────▶│ │
|
||||
│ │ │
|
||||
│ 2. Shows │ │
|
||||
│ loyalty card │ │
|
||||
│───────────────▶│ │
|
||||
│ │ 3. Scans card │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 4. Enters amount │
|
||||
│ │ €25.00 │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 5. Enters PIN │
|
||||
│ │─────────────────▶│
|
||||
│ │ │ ┌──────────┐
|
||||
│ │ │ │Calculate:│
|
||||
│ │ │ │€25 × 10 │
|
||||
│ │ │ │= 250 pts │
|
||||
│ │ │ └──────────┘
|
||||
│ │ 6. Confirms │
|
||||
│ │◀─────────────────│
|
||||
│ │ "+250 points!" │
|
||||
│ │ │
|
||||
│ 7. Receipt │ │
|
||||
│ shows points │ │
|
||||
│◀───────────────│ │
|
||||
```
|
||||
|
||||
**Points Calculation:**
|
||||
```
|
||||
Purchase: €25.00
|
||||
Rate: 10 points per euro
|
||||
Points Earned: 250 points
|
||||
New Balance: 750 points
|
||||
```
|
||||
|
||||
#### Customer Journey: Redeeming Points
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ POINTS LOYALTY - REDEMPTION │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Customer Staff System
|
||||
│ │ │
|
||||
│ 1. Views │ │
|
||||
│ rewards in │ │
|
||||
│ wallet app │ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ ┌──────────┐ │ │
|
||||
│ │ REWARDS │ │ │
|
||||
│ │──────────│ │ │
|
||||
│ │ 500 pts │ │ │
|
||||
│ │ Free │ │ │
|
||||
│ │ Drink │ │ │
|
||||
│ │──────────│ │ │
|
||||
│ │ 1000 pts │ │ │
|
||||
│ │ Free │ │ │
|
||||
│ │ Meal │ │ │
|
||||
│ └──────────┘ │ │
|
||||
│ │ │
|
||||
│ 2. "I want to │ │
|
||||
│ redeem for │ │
|
||||
│ free drink" │ │
|
||||
│───────────────▶│ │
|
||||
│ │ 3. Scans card │
|
||||
│ │ Selects reward │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 4. Enters PIN │
|
||||
│ │─────────────────▶│
|
||||
│ │ │
|
||||
│ │ 5. Confirms │
|
||||
│ │◀─────────────────│
|
||||
│ │ "-500 points" │
|
||||
│ │ Balance: 250 pts │
|
||||
│ │ │
|
||||
│ 6. Gets free │ │
|
||||
│ drink │ │
|
||||
│◀───────────────│ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Market Best Practices
|
||||
|
||||
### 3.1 Competitive Analysis
|
||||
|
||||
| Feature | Square Loyalty | Toast | Fivestars | **Wizamart** |
|
||||
|---------|---------------|-------|-----------|--------------|
|
||||
| Stamp cards | ✅ | ✅ | ✅ | ✅ |
|
||||
| Points system | ✅ | ✅ | ✅ | ✅ |
|
||||
| Google Wallet | ✅ | ❌ | ✅ | ✅ |
|
||||
| Apple Wallet | ✅ | ✅ | ✅ | ✅ |
|
||||
| Staff PIN | ❌ | ✅ | ✅ | ✅ |
|
||||
| Cooldown fraud protection | ❌ | ❌ | ✅ | ✅ |
|
||||
| Daily limits | ❌ | ❌ | ✅ | ✅ |
|
||||
| Tablet terminal | ✅ | ✅ | ✅ | ✅ (planned) |
|
||||
| Customer app | ✅ | ✅ | ✅ | Via Wallet |
|
||||
| Analytics dashboard | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
### 3.2 Best Practices to Implement
|
||||
|
||||
#### UX Best Practices
|
||||
|
||||
1. **Instant gratification** - Show stamp/points immediately after transaction
|
||||
2. **Progress visualization** - Clear progress bars/stamp grids
|
||||
3. **Reward proximity** - "Only 2 more until your free coffee!"
|
||||
4. **Wallet-first** - Push customers to save to wallet
|
||||
5. **Offline support** - Card works even without internet (via wallet)
|
||||
|
||||
#### Fraud Prevention Best Practices
|
||||
|
||||
1. **Multi-layer security** - PIN + cooldown + daily limits
|
||||
2. **Staff accountability** - Every transaction tied to a staff PIN
|
||||
3. **Audit trail** - Complete history with IP/device info
|
||||
4. **Lockout protection** - Automatic PIN lockout after failures
|
||||
5. **Admin oversight** - Unlock and PIN management in dashboard
|
||||
|
||||
#### Engagement Best Practices
|
||||
|
||||
1. **Welcome bonus** - Give 1 stamp on enrollment (configurable)
|
||||
2. **Birthday rewards** - Extra stamps/points on customer birthday
|
||||
3. **Milestone notifications** - "Congrats! 50 stamps earned lifetime!"
|
||||
4. **Re-engagement** - Remind inactive customers via email
|
||||
5. **Double points days** - Promotional multipliers (future)
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Implementation Roadmap
|
||||
|
||||
### Phase 2A: Vendor Interface (Priority)
|
||||
|
||||
| Task | Effort | Priority |
|
||||
|------|--------|----------|
|
||||
| Loyalty terminal (scan/stamp/redeem) | 3 days | P0 |
|
||||
| Program setup wizard | 2 days | P0 |
|
||||
| Staff PIN management | 1 day | P0 |
|
||||
| Customer cards list | 1 day | P1 |
|
||||
| Dashboard with stats | 2 days | P1 |
|
||||
| Export functionality | 1 day | P2 |
|
||||
|
||||
### Phase 2B: Admin Interface
|
||||
|
||||
| Task | Effort | Priority |
|
||||
|------|--------|----------|
|
||||
| Programs list view | 1 day | P1 |
|
||||
| Platform-wide stats | 1 day | P1 |
|
||||
| Program detail view | 0.5 day | P2 |
|
||||
|
||||
### Phase 2C: Customer Experience
|
||||
|
||||
| Task | Effort | Priority |
|
||||
|------|--------|----------|
|
||||
| Enrollment page (mobile) | 1 day | P0 |
|
||||
| Card detail page | 0.5 day | P1 |
|
||||
| Wallet pass polish | 1 day | P1 |
|
||||
| Email templates | 1 day | P2 |
|
||||
|
||||
### Phase 2D: Polish & Advanced
|
||||
|
||||
| Task | Effort | Priority |
|
||||
|------|--------|----------|
|
||||
| QR code scanner (JS) | 2 days | P0 |
|
||||
| Real-time updates (WebSocket) | 1 day | P2 |
|
||||
| Receipt printing | 1 day | P3 |
|
||||
| POS integration hooks | 2 days | P3 |
|
||||
|
||||
---
|
||||
|
||||
## Part 5: Technical Specifications
|
||||
|
||||
### Vendor Terminal Requirements
|
||||
|
||||
- **Responsive**: Works on tablet (primary), desktop, mobile
|
||||
- **Touch-friendly**: Large buttons, numpad for PIN
|
||||
- **Camera access**: For QR code scanning (WebRTC)
|
||||
- **Offline-capable**: Queue operations if network down (future)
|
||||
- **Real-time**: WebSocket for instant updates
|
||||
|
||||
### Frontend Stack
|
||||
|
||||
- **Framework**: React/Vue components (match existing stack)
|
||||
- **QR Scanner**: `html5-qrcode` or `@aspect-sdk/barcode-reader`
|
||||
- **Charts**: Existing charting library (Chart.js or similar)
|
||||
- **Animations**: CSS transitions for stamp animations
|
||||
|
||||
### API Considerations
|
||||
|
||||
- All vendor endpoints require `vendor_id` from auth token
|
||||
- Staff PIN passed in request body, not headers
|
||||
- Rate limiting on lookup/scan endpoints
|
||||
- Pagination on card list (default 50)
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Mockup Reference Images
|
||||
|
||||
### Stamp Card Visual (Wallet Pass)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
│ ☕ Café du Coin │
|
||||
│ │
|
||||
│ ████ ████ ████ ████ ████ │
|
||||
│ ████ ████ ████ ░░░░ ░░░░ │
|
||||
│ │
|
||||
│ 8/10 STAMPS │
|
||||
│ 2 more until FREE COFFEE │
|
||||
│ │
|
||||
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
|
||||
│ ▓▓▓▓▓▓▓▓ QR CODE ▓▓▓▓▓▓▓▓ │
|
||||
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
|
||||
│ │
|
||||
│ Card #4821-7493-2841 │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Points Card Visual (Wallet Pass)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
│ 🍕 Pizza Roma Rewards │
|
||||
│ │
|
||||
│ ★ 750 ★ │
|
||||
│ POINTS │
|
||||
│ │
|
||||
│ ────────────────────── │
|
||||
│ Next reward: 500 pts │
|
||||
│ Free drink │
|
||||
│ ────────────────────── │
|
||||
│ │
|
||||
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
|
||||
│ ▓▓▓▓▓▓▓▓ QR CODE ▓▓▓▓▓▓▓▓ │
|
||||
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
|
||||
│ │
|
||||
│ Card #4821-2847-9283 │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Document Version: 1.0*
|
||||
*Created: 2025-01-28*
|
||||
*Author: Wizamart Engineering*
|
||||
Reference in New Issue
Block a user