Files
orion/docs/modules/loyalty.md
Samir Boulahtit e9253fbd84 refactor: rename Wizamart to Orion across entire codebase
Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:46:56 +01:00

11 KiB

Loyalty Module

The Loyalty Module provides stamp-based and points-based loyalty programs for Orion stores with Google Wallet and Apple Wallet integration.

Overview

Aspect Description
Module Code loyalty
Dependencies customers
Status Phase 2 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

┌─────────────────┐       ┌─────────────────┐
│    Merchant      │───────│ LoyaltyProgram  │
└─────────────────┘  1:1  └─────────────────┘
        │                         │
        ▼                ┌────────┼──────────┐
┌──────────────────┐     │        │          │
│  MerchantLoyalty  │     ▼        ▼          ▼
│  Settings        │┌──────────┐┌──────────┐┌────────┐
└──────────────────┘│ StaffPin ││LoyaltyCard││(config)│
                    └──────────┘└──────────┘└────────┘
                          │          │
                          │          ▼
                          │   ┌──────────────┐
                          └──▶│ Transaction  │
                               └──────────────┘
                                     │
                                     ▼
                          ┌──────────────────────┐
                          │AppleDeviceRegistration│
                          └──────────────────────┘

Database Tables

Table Purpose
loyalty_programs Merchant's program configuration (type, targets, branding)
loyalty_cards Customer cards with stamp/point balances (merchant-scoped)
loyalty_transactions Immutable audit log of all operations
staff_pins Hashed PINs for fraud prevention
apple_device_registrations Apple Wallet push notification tokens
merchant_loyalty_settings Admin-controlled per-merchant settings

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

Store Endpoints (/api/v1/store/loyalty/)

Method Endpoint Description
GET /program Get store'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

Storefront Endpoints (/api/v1/storefront/loyalty/)

Method Endpoint Description
GET /card Get customer's loyalty card and balance
GET /transactions Get customer's transaction history
POST /enroll Self-enrollment in loyalty program

Public Endpoints (/api/v1/loyalty/)

Method Endpoint Description
GET /programs/{store_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, store_id=1, data=data)

Enroll a Customer

from app.modules.loyalty.services import card_service

card = card_service.enroll_customer(db, customer_id=123, store_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 points for inactive cards (based on points_expiration_days)

UI Pages

Admin Pages

Page Path Description
Programs Dashboard /admin/loyalty/programs List all loyalty programs with stats
Merchant Detail /admin/loyalty/merchants/{id} Detailed view of a merchant's program
Merchant Settings /admin/loyalty/merchants/{id}/settings Admin-controlled merchant settings
Analytics /admin/loyalty/analytics Platform-wide analytics

Store Pages

Page Path Description
Terminal /store/loyalty/terminal Scan card, add stamps/points, redeem
Cards List /store/loyalty/cards Browse customer cards
Card Detail /store/loyalty/cards/{id} Individual card detail
Enroll /store/loyalty/enroll Enroll new customer
Settings /store/loyalty/settings Program settings
Stats /store/loyalty/stats Store-level statistics

Storefront Pages

Page Path Description
Dashboard /loyalty/dashboard Customer's card and balance
History /loyalty/history Transaction history
Enroll /loyalty/enroll Self-enrollment page

Localization

Available in 5 languages:

  • English (en.json)
  • French (fr.json)
  • German (de.json)
  • Luxembourgish (lu.json, lb.json)

Future Enhancements (Phase 3+)

  • Rewards catalog with configurable tiers
  • Customer tiers (Bronze/Silver/Gold)
  • Promotions engine (bonus points, discounts, free items)
  • Referral program
  • Gamification (spin wheel, scratch cards)
  • POS integration
  • Batch import of existing loyalty cards
  • Real-time WebSocket updates
  • Receipt printing