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

```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

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

```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, store_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, store_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 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
