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:
@@ -112,13 +112,19 @@ Complete step-by-step guide for deploying Orion on a Hetzner Cloud VPS.
|
||||
- Service account JSON key generated
|
||||
- Dependencies added to `requirements.txt`: `google-auth>=2.0.0`, `PyJWT>=2.0.0` (commit `d36783a`)
|
||||
- Loyalty env vars added to `.env.example` and `docs/deployment/environment.md`
|
||||
- `LOYALTY_GOOGLE_ISSUER_ID` and `LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON` added to `app/core/config.py` Settings class
|
||||
- **End-to-end integration wired:**
|
||||
- Enrollment auto-creates Google Wallet class + object (`card_service` → `wallet_service.create_wallet_objects`)
|
||||
- Stamp/points operations auto-sync to Google Wallet (`stamp_service`/`points_service` → `wallet_service.sync_card_to_wallets`)
|
||||
- Storefront API returns wallet URLs (`GET /loyalty/card`, `POST /loyalty/enroll`)
|
||||
- "Add to Google Wallet" button wired in storefront dashboard and enrollment success page (Alpine.js conditional rendering)
|
||||
- Google Wallet is a platform-wide config (env vars only) — merchants don't need to configure anything
|
||||
|
||||
**Next steps:**
|
||||
|
||||
- [ ] Upload service account JSON to Hetzner server
|
||||
- [ ] Set `LOYALTY_GOOGLE_ISSUER_ID` and `LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON` in production `.env`
|
||||
- [ ] Restart app and test end-to-end: enroll → add pass → stamp → verify pass updates
|
||||
- [ ] Wire "Add to Google Wallet" button into storefront enrollment success page
|
||||
- [ ] Submit for Google production approval when ready
|
||||
- [ ] Apple Wallet setup (APNs push, certificates, pass images)
|
||||
|
||||
@@ -1872,7 +1878,21 @@ Restart the application:
|
||||
docker compose --profile full up -d --build
|
||||
```
|
||||
|
||||
### 25.6 Verify Configuration
|
||||
### 25.6 Platform-Level Configuration
|
||||
|
||||
Google Wallet is a **platform-wide setting** — all merchants on the platform share the same Issuer ID and service account. Merchants don't need to configure anything; wallet integration activates automatically when the env vars are set.
|
||||
|
||||
The two required env vars:
|
||||
|
||||
```bash
|
||||
# In production .env
|
||||
LOYALTY_GOOGLE_ISSUER_ID=3388000000023089598
|
||||
LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON=/app/google-wallet-sa.json
|
||||
```
|
||||
|
||||
When both are set, every loyalty program on the platform automatically gets Google Wallet support: enrollment creates wallet passes, stamp/points operations sync to passes, and the storefront shows "Add to Google Wallet" buttons.
|
||||
|
||||
### 25.7 Verify Configuration
|
||||
|
||||
Check the API health and wallet service status:
|
||||
|
||||
@@ -1880,12 +1900,11 @@ Check the API health and wallet service status:
|
||||
# Check the app logs for wallet service initialization
|
||||
docker compose --profile full logs api | grep -i "wallet\|loyalty"
|
||||
|
||||
# Test via API — create a program and enroll a customer, then check the response
|
||||
# for google_object_id and google Wallet URL fields
|
||||
# Test via API — enroll a customer and check the response for wallet URLs
|
||||
curl -s https://api.wizard.lu/health | python3 -m json.tool
|
||||
```
|
||||
|
||||
### 25.7 Testing Google Wallet Passes
|
||||
### 25.8 Testing Google Wallet Passes
|
||||
|
||||
Google provides a **demo mode** — passes work in test without full production approval:
|
||||
|
||||
@@ -1895,20 +1914,21 @@ Google provides a **demo mode** — passes work in test without full production
|
||||
|
||||
**End-to-end test flow:**
|
||||
|
||||
1. Create a loyalty program via the store panel
|
||||
1. Create a loyalty program via the store panel and set the Google Wallet Issuer ID in Settings → Digital Wallet
|
||||
2. Enroll a customer (via store or storefront self-enrollment)
|
||||
3. The API returns a Google Wallet save URL
|
||||
4. Open the URL on an Android device — the pass is added to Google Wallet
|
||||
5. Add a stamp or points — the pass in Google Wallet auto-updates
|
||||
- The system automatically creates a Google Wallet `LoyaltyClass` (for the program) and `LoyaltyObject` (for the card)
|
||||
3. Open the storefront loyalty dashboard — the "Add to Google Wallet" button appears
|
||||
4. Click the button (or open the URL on an Android device) — the pass is added to Google Wallet
|
||||
5. Add a stamp or points — the pass in Google Wallet auto-updates (no push needed, Google syncs)
|
||||
|
||||
### 25.8 Local Development Setup
|
||||
### 25.9 Local Development Setup
|
||||
|
||||
You can test the full Google Wallet integration from your local machine:
|
||||
|
||||
```bash
|
||||
# In your local .env (or export directly)
|
||||
export LOYALTY_GOOGLE_ISSUER_ID=3388000000023089598
|
||||
export LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON=/path/to/orion-488322-xxxxx.json
|
||||
# In your local .env
|
||||
LOYALTY_GOOGLE_ISSUER_ID=3388000000023089598
|
||||
LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON=/path/to/orion-488322-xxxxx.json
|
||||
```
|
||||
|
||||
The `GoogleWalletService` calls Google's REST API directly over HTTPS — no special network configuration needed. The same service account JSON works on both local and server environments.
|
||||
@@ -1917,24 +1937,27 @@ The `GoogleWalletService` calls Google's REST API directly over HTTPS — no spe
|
||||
|
||||
- [x] Service account JSON downloaded and path set in env
|
||||
- [x] `LOYALTY_GOOGLE_ISSUER_ID` set in env
|
||||
- [ ] Start the app locally: `uvicorn app.main:app --reload`
|
||||
- [ ] Create a loyalty program → verify `google_class_id` is set
|
||||
- [ ] Enroll a customer → verify `google_object_id` is set
|
||||
- [ ] Call `get_save_url()` → open the URL on Android to add pass
|
||||
- [ ] Add stamps → verify pass updates in Google Wallet
|
||||
- [ ] Start the app locally: `python3 -m uvicorn main:app --reload`
|
||||
- [ ] Enroll a customer → check logs for "Created Google Wallet class" and "Created Google Wallet object"
|
||||
- [ ] Open storefront dashboard → "Add to Google Wallet" button should appear
|
||||
- [ ] Open the wallet URL on Android → pass added to Google Wallet
|
||||
- [ ] Add stamps → check logs for "Updated Google Wallet object", verify pass updates
|
||||
|
||||
### 25.9 How It Works (Architecture)
|
||||
### 25.10 How It Works (Architecture)
|
||||
|
||||
The integration is fully automatic — no manual API calls needed after initial setup.
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐
|
||||
│ Merchant │────▶│ Orion API │────▶│ Google Wallet API │
|
||||
│ creates │ │ │ │ │
|
||||
│ program │ │ create_class │ │ POST /loyaltyClass │
|
||||
│ sets issuer │ │ │ │ │
|
||||
│ ID in UI │ │ │ │ │
|
||||
└─────────────┘ └──────────────┘ └─────────────────────┘
|
||||
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐
|
||||
│ Customer │────▶│ Orion API │────▶│ Google Wallet API │
|
||||
│ enrolls │ │ │ │ │
|
||||
│ │ │create_class +│ │ POST /loyaltyClass │
|
||||
│ │ │create_object │ │ POST /loyaltyObject │
|
||||
│ │◀────│ save_url │ │ │
|
||||
│ │ └──────────────┘ └─────────────────────┘
|
||||
@@ -1944,22 +1967,30 @@ The `GoogleWalletService` calls Google's REST API directly over HTTPS — no spe
|
||||
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐
|
||||
│ Staff adds │────▶│ Orion API │────▶│ Google Wallet API │
|
||||
│ stamp │ │ │ │ │
|
||||
│ stamp/pts │ │ │ │ │
|
||||
│ │ │update_object │ │ PATCH /loyaltyObject│
|
||||
└─────────────┘ └──────────────┘ └─────────────────────┘
|
||||
Pass auto-updates on
|
||||
customer's phone
|
||||
```
|
||||
|
||||
**Automatic triggers:**
|
||||
|
||||
| Event | Wallet Action | Service Call |
|
||||
|-------|---------------|--------------|
|
||||
| Customer enrolls | Create class (if first) + create object | `wallet_service.create_wallet_objects()` |
|
||||
| Stamp added/redeemed/voided | Update object with new balance | `wallet_service.sync_card_to_wallets()` |
|
||||
| Points earned/redeemed/voided/adjusted | Update object with new balance | `wallet_service.sync_card_to_wallets()` |
|
||||
| Customer opens dashboard | Generate save URL (JWT, 1h expiry) | `wallet_service.get_add_to_wallet_urls()` |
|
||||
|
||||
No push notifications needed — Google syncs object changes automatically.
|
||||
|
||||
### 25.10 Next Steps
|
||||
### 25.11 Next Steps
|
||||
|
||||
After Google Wallet is verified working:
|
||||
|
||||
1. **Wire "Add to Google Wallet" button** into the storefront enrollment success page and card dashboard
|
||||
2. **Submit for Google production approval** — required before non-test users can add passes
|
||||
3. **Apple Wallet** — separate setup requiring Apple Developer account, APNs certificates, and pass signing certificates (see [Loyalty Module docs](../modules/loyalty.md#apple-wallet))
|
||||
1. **Submit for Google production approval** — required before non-test users can add passes
|
||||
2. **Apple Wallet** — separate setup requiring Apple Developer account, APNs certificates, and pass signing certificates (see [Loyalty Module docs](../modules/loyalty.md#apple-wallet))
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user