Files
orion/docs/modules/loyalty.md
Samir Boulahtit b5a803cde8 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>
2026-01-28 23:04:00 +01:00

8.8 KiB

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_):

# 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).

# 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

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

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

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

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