Files
orion/app/modules/loyalty/models/apple_device.py
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

80 lines
2.1 KiB
Python

# app/modules/loyalty/models/apple_device.py
"""
Apple device registration database model.
Tracks devices that have added an Apple Wallet pass for push
notification updates when the pass changes.
"""
from sqlalchemy import (
Column,
ForeignKey,
Index,
Integer,
String,
)
from sqlalchemy.orm import relationship
from app.core.database import Base
from models.database.base import TimestampMixin
class AppleDeviceRegistration(Base, TimestampMixin):
"""
Apple Wallet device registration.
When a user adds a pass to Apple Wallet, the device registers
with us to receive push notifications when the pass updates.
This implements the Apple Wallet Web Service for passbook updates:
https://developer.apple.com/documentation/walletpasses/
"""
__tablename__ = "apple_device_registrations"
id = Column(Integer, primary_key=True, index=True)
# Card relationship
card_id = Column(
Integer,
ForeignKey("loyalty_cards.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
# Device identification
device_library_identifier = Column(
String(100),
nullable=False,
index=True,
comment="Unique identifier for the device/library",
)
# Push notification token
push_token = Column(
String(100),
nullable=False,
comment="APNs push token for this device",
)
# =========================================================================
# Relationships
# =========================================================================
card = relationship("LoyaltyCard", back_populates="apple_devices")
# Indexes - unique constraint on device + card combination
__table_args__ = (
Index(
"idx_apple_device_card",
"device_library_identifier",
"card_id",
unique=True,
),
)
def __repr__(self) -> str:
return (
f"<AppleDeviceRegistration(id={self.id}, "
f"device='{self.device_library_identifier[:8]}...', card_id={self.card_id})>"
)