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>
80 lines
2.1 KiB
Python
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})>"
|
|
)
|