docs(loyalty): go-live readiness snapshot — 2026-05-10
Some checks failed
Some checks failed
Captures where the loyalty pre-launch checklist actually stands after
tonight's prod readiness pass:
- Step 1 (email templates seeded) ✅
- Step 2 (Google Wallet config) ✅ validated via wallet-debug
- Step 3 (migrations) ✅ all module heads incl. loyalty_011 on prod
- Step 7 (Wallet real-device test) ✅
- Steps 4, 5 (FR/DE/LB analytics keys, store-owner template
permission) deferred — cosmetic / non-blocking
- Step 6 (8 user-journey E2E tests) is the remaining human gate
- Step 9 (Google Wallet production access) post-launch
Also records the SMTP path-change diagnosis (own mail server on port
465 blocked outbound from Hetzner; switched to 587 STARTTLS via
/admin/settings DB overrides) and the cosmetic fix shipped in
f2d1bdcd so the test email reports the *effective* config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -325,10 +325,13 @@ Tracked separately, not blocking launch.
|
||||
|
||||
Everything below must be completed before going live. Items are ordered by dependency.
|
||||
|
||||
### Step 1: Seed email templates on prod DB
|
||||
- [ ] SSH into prod server
|
||||
- [ ] Run: `python scripts/seed/seed_email_templates_loyalty.py`
|
||||
- [ ] Verify: 20 rows created (5 templates × 4 locales)
|
||||
> **Latest status snapshot:** see [Go-Live Readiness (2026-05-10)](../../proposals/loyalty-go-live-readiness.md)
|
||||
> — only Steps 6 and 8 still pending; Step 9 is post-launch.
|
||||
|
||||
### Step 1: Seed email templates on prod DB ✅
|
||||
- [x] SSH into prod server
|
||||
- [x] Run: `python scripts/seed/seed_email_templates_loyalty.py`
|
||||
- [x] Verify: 20 rows created (5 templates × 4 locales)
|
||||
- [ ] Review EN email copy — adjust subject lines/body if needed via admin UI at `/admin/email-templates`
|
||||
|
||||
### Step 2: Google Wallet — already deployed, verify config
|
||||
@@ -339,12 +342,13 @@ The Google Wallet integration is already deployed on the Hetzner server (see Ste
|
||||
- Service account linked to Issuer with Admin role ✅
|
||||
|
||||
Verify after deploy:
|
||||
- [ ] Restart app — confirm no startup error (config validator checks file exists)
|
||||
- [ ] `GET /api/v1/admin/loyalty/wallet-status` returns `google_configured: true`
|
||||
- [x] Restart app — confirm no startup error (config validator checks file exists)
|
||||
- [x] `GET /api/v1/admin/loyalty/wallet-status` returns `google_configured: true`
|
||||
- [x] Wallet diagnostics screen at `/admin/loyalty/wallet-debug` → Config Validation → all green (validated 2026-05-10)
|
||||
|
||||
### Step 3: Apply database migrations
|
||||
- [ ] Run: `alembic upgrade heads`
|
||||
- [ ] Verify migrations applied: `loyalty_003` through `loyalty_006`, `customers_003`
|
||||
### Step 3: Apply database migrations ✅
|
||||
- [x] Run: `alembic upgrade heads`
|
||||
- [x] Verify migrations applied: all four module heads current incl. `loyalty_011`
|
||||
|
||||
### Step 4: FR/DE/LB translations for new analytics i18n keys
|
||||
- [ ] Add translations for 7 keys in `app/modules/loyalty/locales/{fr,de,lb}.json`:
|
||||
@@ -375,12 +379,12 @@ Follow the **Pre-Launch E2E Test Checklist** at the bottom of `user-journeys.md`
|
||||
- [ ] **Test 7:** Admin oversight (programs, merchants, analytics)
|
||||
- [ ] **Test 8:** Cross-location disabled behavior (separate cards per store)
|
||||
|
||||
### Step 7: Google Wallet real-device test (demo mode)
|
||||
### Step 7: Google Wallet real-device test (demo mode) ✅
|
||||
Google Wallet currently works in **demo/test mode** — only your Google account and explicitly added test accounts can see passes. This is sufficient for launch testing.
|
||||
- [ ] Enroll a test customer on prod
|
||||
- [ ] Tap "Add to Google Wallet" on success page
|
||||
- [ ] Open Google Wallet on Android device — verify pass renders with merchant branding
|
||||
- [ ] Trigger a stamp/points transaction — verify pass auto-updates within 60s
|
||||
- [x] Enroll a test customer on prod
|
||||
- [x] Tap "Add to Google Wallet" on success page
|
||||
- [x] Open Google Wallet on Android device — verify pass renders with merchant branding
|
||||
- [x] Trigger a stamp/points transaction — verify pass auto-updates within 60s
|
||||
|
||||
### Step 8: Go live
|
||||
- [ ] Remove any test data from prod DB (test customers, test cards)
|
||||
|
||||
134
docs/proposals/loyalty-go-live-readiness.md
Normal file
134
docs/proposals/loyalty-go-live-readiness.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Loyalty Go-Live Readiness — 2026-05-10
|
||||
|
||||
Snapshot of where the loyalty platform stands the night of 2026-05-10. The
|
||||
canonical sequenced plan is still
|
||||
[`app/modules/loyalty/docs/production-launch-plan.md`](../modules/loyalty/production-launch-plan.md);
|
||||
this doc records the *current state* (✅ / ⏳ / 🟡) and what surfaced
|
||||
during the prod readiness pass.
|
||||
|
||||
## TL;DR
|
||||
|
||||
The technical pre-launch checklist is green. The remaining gate is a
|
||||
human one — walking the 8 user-journey E2E tests on prod with a real
|
||||
test customer and confirming nothing surprises us. After that, flip the
|
||||
loyalty platform live for FASHIONHUB's stores and start the Google
|
||||
Wallet production-access review in parallel (1–3 day Google review,
|
||||
non-blocking).
|
||||
|
||||
## Status board
|
||||
|
||||
| # | Pre-launch step | State | Notes |
|
||||
|---|---|---|---|
|
||||
| 1 | Seed loyalty email templates on prod | ✅ | 20 rows (5 templates × 4 locales) all `is_active=true` |
|
||||
| 2 | Google Wallet config on Hetzner | ✅ | Wallet config validator green: credentials valid, issuer `3388000000023089598`, origin `https://rewardflow.lu`, default logo reachable |
|
||||
| 3 | Database migrations | ✅ | All four module heads current incl. `loyalty_011` (acting-device audit) on prod |
|
||||
| 4 | FR/DE/LB translations for analytics i18n keys | 🟡 | 8 keys still EN-only. Cosmetic, doesn't block soft launch |
|
||||
| 5 | `messaging.manage_templates` permission for store owners | 🟡 | Only matters if merchants self-edit templates. Admin can edit centrally. Defer |
|
||||
| 6 | 8 user-journey E2E tests | ⏳ | **The remaining gate** — user does this with a real test customer |
|
||||
| 7 | Google Wallet real-device pass test | ✅ | Already confirmed earlier — cards register, points/redeem visible on personal Google Wallet |
|
||||
| 8 | Go live | ⏳ | Gated by #6. Cleanup test data + enable platform feature flags for FASHIONHUB |
|
||||
| 9 | Google Wallet production access | ⏳ | Post-launch, 1–3 day Google review. App-side change is zero; same issuer + service account, passes become public-visible once approved |
|
||||
|
||||
## What got sorted tonight
|
||||
|
||||
### SMTP wired to a self-hosted mail server
|
||||
|
||||
Started here:
|
||||
|
||||
- prod `.env` had `EMAIL_PROVIDER=sendgrid` + a SendGrid API key
|
||||
- SendGrid free trial (60 days) had expired
|
||||
- `SMTP_*` env vars were placeholders pointing at `smtp.example.com`
|
||||
|
||||
Discovered that `/admin/settings` lets you store SMTP config in the DB
|
||||
(table `admin_settings`, category `email`) and those values **win over
|
||||
.env**. User had already configured:
|
||||
|
||||
- `email_provider=smtp`
|
||||
- `smtp_host=mail1.myservices.hosting`
|
||||
- `smtp_port=465` ← problematic
|
||||
- `smtp_user=support@wizard.lu` / encrypted password
|
||||
- `smtp_use_ssl=true, smtp_use_tls=false`
|
||||
|
||||
Diagnosis from the prod container:
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| DNS resolves `mail1.myservices.hosting` | ✅ `185.26.107.245` |
|
||||
| TCP `mail1.myservices.hosting:465` | ❌ timed out |
|
||||
| TCP `mail1.myservices.hosting:587` | ✅ open |
|
||||
|
||||
Either Hetzner blocks 465 outbound for this VPS or the provider
|
||||
firewalls Hetzner's IP range on 465. Either way, port 587 (submission
|
||||
+ STARTTLS) is the modern path and works.
|
||||
|
||||
**Fix:** changed `/admin/settings` to port 587, SSL off, TLS on. Test
|
||||
email landed in inbox immediately, sender header `Support Wizard
|
||||
<support@wizard.lu>` — proving the DB override was being used.
|
||||
|
||||
### Cosmetic bug found and fixed
|
||||
|
||||
The test email's body claimed the configuration that **would have been
|
||||
used if .env were authoritative** — i.e. it said `Provider: sendgrid`
|
||||
and `From: noreply@wizard.lu` even though the actual send went via
|
||||
SMTP from `support@wizard.lu`. Two places in the code:
|
||||
|
||||
1. `app/modules/core/routes/api/admin_settings.py::send_test_email` —
|
||||
body template hardcoded `app_settings.email_provider` and
|
||||
`app_settings.email_from_address`
|
||||
2. `app/modules/messaging/services/email_service.py` — the "template
|
||||
not found" `EmailLog` branch recorded `settings.email_provider` /
|
||||
`settings.email_from_address` instead of the effective config
|
||||
|
||||
Both now read from `get_effective_email_config(db)` /
|
||||
`self._platform_config`, so the test email page and audit logs reflect
|
||||
what was actually used.
|
||||
|
||||
Commit: `f2d1bdcd` on master, deployed via Gitea Actions.
|
||||
|
||||
## What the user does next
|
||||
|
||||
In priority order:
|
||||
|
||||
1. **Tonight or tomorrow — review email copy.** Open
|
||||
`/admin/email-templates` and skim the 5 loyalty templates (EN
|
||||
locale). `loyalty_enrollment`, `loyalty_welcome_bonus` and
|
||||
`loyalty_reward_ready` are the customer-visible ones — adjust
|
||||
subject lines + body copy if anything reads off-brand.
|
||||
2. **Walk the 8 user-journey E2E tests** — checklist at the bottom of
|
||||
`app/modules/loyalty/docs/user-journeys.md`. Use a personal email
|
||||
as the test customer.
|
||||
3. **Flip live for FASHIONHUB** — clean any test data, double-check
|
||||
Celery (`docker compose ps | grep celery`), enable loyalty feature
|
||||
on FASHIONHUB's stores via the admin UI.
|
||||
4. **In parallel, file Google Wallet production access** —
|
||||
[pay.google.com/business/console](https://pay.google.com/business/console)
|
||||
→ Wallet API → Manage → Request production access. Use sample pass
|
||||
screenshots from FASHIONHUB. Google reviews the Issuer, not
|
||||
individual merchants — once approved all merchants on the platform
|
||||
are covered.
|
||||
|
||||
## Open follow-ups (non-blocking)
|
||||
|
||||
These can wait but are worth tracking:
|
||||
|
||||
- **FR/DE/LB translations** for the 8 analytics i18n keys
|
||||
(`store.analytics.revenue_title`, `store.analytics.cohort_title`,
|
||||
etc.). EN shows through; cosmetic only.
|
||||
- **`messaging.manage_templates` permission discovery for
|
||||
merchant_owner role** — needed if/when merchants self-edit templates.
|
||||
Admin can edit centrally for v1.
|
||||
- **Failed-PIN-attempt reporting from Android tablet → server lockout
|
||||
counter** — tablet bcrypts locally and silently fails; a stolen
|
||||
tablet's brute-forcer doesn't trip server-side lockout. Add a tiny
|
||||
`POST /pins/{id}/record-failed-attempt` endpoint plus a call from the
|
||||
PinViewModel's failure branch.
|
||||
- **Splash screen + per-action success animation** for the Android
|
||||
tablet — Phase F polish that was intentionally deferred.
|
||||
|
||||
## Reference
|
||||
|
||||
- Canonical plan: [`app/modules/loyalty/docs/production-launch-plan.md`](../modules/loyalty/production-launch-plan.md)
|
||||
- Hetzner runbook: [`deployment/hetzner-server-setup.md`](../deployment/hetzner-server-setup.md)
|
||||
- Wallet diagnostics page: `/admin/loyalty/wallet-debug` (super admin only)
|
||||
- Recent commits relevant to this session:
|
||||
- `f2d1bdcd` fix(messaging): test email + EmailLog show effective config
|
||||
Reference in New Issue
Block a user