feat: wire Google Wallet into loyalty enrollment, stamps, and points flows

Connect the fully-implemented Google Wallet service to the loyalty module:
- Create wallet class/object on customer enrollment
- Sync wallet passes on stamp and points operations
- Expose wallet URLs in storefront API responses
- Add conditional "Add to Google Wallet" buttons on dashboard and enroll-success pages
- Use platform-wide env var config (not per-merchant DB column)
- Add Google service account patterns to .gitignore
- Add LOYALTY_GOOGLE_* fields to app Settings
- Update deployment docs and add local testing guide

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 10:38:46 +01:00
parent 6c78827c7f
commit 32e4aa6564
13 changed files with 358 additions and 56 deletions

View File

@@ -416,6 +416,12 @@ class CardService:
db.commit()
db.refresh(card)
# Create wallet objects (Google Wallet, Apple Wallet)
# Lazy import to avoid circular imports; exception-safe (logs but doesn't raise)
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.create_wallet_objects(db, card)
logger.info(
f"Enrolled customer {customer_id} in merchant {merchant_id} loyalty program "
f"(card: {card.card_number}, bonus: {program.welcome_bonus_points} pts)"

View File

@@ -169,6 +169,11 @@ class PointsService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated points balance
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Added {points_earned} points to card {card.id} at store {store_id} "
f"(purchase: €{purchase_euros:.2f}, balance: {card.points_balance})"
@@ -295,6 +300,11 @@ class PointsService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated points balance
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Redeemed {points_required} points from card {card.id} at store {store_id} "
f"(reward: {reward_name}, balance: {card.points_balance})"
@@ -437,6 +447,11 @@ class PointsService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated points balance
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Voided {actual_voided} points from card {card.id} at store {store_id} "
f"(balance: {card.points_balance})"
@@ -523,6 +538,11 @@ class PointsService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated points balance
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Adjusted points for card {card.id} by {points_delta:+d} "
f"(reason: {reason}, balance: {card.points_balance})"

View File

@@ -154,6 +154,11 @@ class StampService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated stamp count
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
stamps_today += 1
logger.info(
@@ -273,6 +278,11 @@ class StampService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated stamp count
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Redeemed stamps from card {card.id} at store {store_id} "
f"(reward: {program.stamps_reward_description}, "
@@ -400,6 +410,11 @@ class StampService:
db.commit()
db.refresh(card)
# Sync wallet passes with updated stamp count
from app.modules.loyalty.services.wallet_service import wallet_service
wallet_service.sync_card_to_wallets(db, card)
logger.info(
f"Voided {actual_voided} stamps from card {card.id} at store {store_id} "
f"(balance: {card.stamp_count})"

View File

@@ -47,8 +47,8 @@ class WalletService:
program = card.program
# Google Wallet
if program.google_issuer_id or program.google_class_id:
# Google Wallet — platform-wide config via env vars
if google_wallet_service.is_configured or program.google_class_id:
try:
urls["google_wallet_url"] = google_wallet_service.get_save_url(db, card)
except Exception as e: # noqa: EXC003
@@ -131,8 +131,8 @@ class WalletService:
program = card.program
# Create Google Wallet object
if program.google_issuer_id:
# Create Google Wallet object — platform-wide config via env vars
if google_wallet_service.is_configured:
try:
google_wallet_service.create_object(db, card)
results["google_wallet"] = True