feat(loyalty): fix Google Wallet integration and improve enrollment flow
- Fix Google Wallet class creation: add required issuerName field (merchant name), programLogo with default logo fallback, hexBackgroundColor default - Add default loyalty logo assets (200px + 512px) for programs without custom logos - Smart retry: skip retries on 400/401/403/404 client errors (not transient) - Fix enrollment success page: use sessionStorage for wallet URLs instead of authenticated API call (self-enrolled customers have no session) - Hide wallet section on success page when no wallet URLs available - Wire up T&C modal on enrollment page with program.terms_text - Add startup validation for Google/Apple Wallet configs in lifespan - Add admin wallet status dashboard endpoint and UI (moved to service layer) - Fix Apple Wallet push notifications with real APNs HTTP/2 implementation - Fix docs: correct enrollment URLs (port, path segments, /v1 prefix) - Fix test assertion: !loyalty-enroll! → !enrollment! Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,9 +62,9 @@
|
||||
|
||||
---
|
||||
|
||||
## Dev URLs (localhost:9999)
|
||||
## Dev URLs (localhost:8000)
|
||||
|
||||
The dev server uses path-based platform routing: `http://localhost:9999/platforms/loyalty/...`
|
||||
The dev server uses path-based platform routing: `http://localhost:8000/platforms/loyalty/...`
|
||||
|
||||
### 1. Platform Admin Pages
|
||||
|
||||
@@ -72,14 +72,14 @@ Login as: `admin@orion.lu` or `samir.boulahtit@gmail.com`
|
||||
|
||||
| Page | Dev URL |
|
||||
|------|---------|
|
||||
| Programs Dashboard | `http://localhost:9999/platforms/loyalty/admin/loyalty/programs` |
|
||||
| Analytics | `http://localhost:9999/platforms/loyalty/admin/loyalty/analytics` |
|
||||
| WizaCorp Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1` |
|
||||
| WizaCorp Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings` |
|
||||
| Fashion Group Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2` |
|
||||
| Fashion Group Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2/settings` |
|
||||
| BookWorld Detail | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/3` |
|
||||
| BookWorld Settings | `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/3/settings` |
|
||||
| Programs Dashboard | `http://localhost:8000/platforms/loyalty/admin/loyalty/programs` |
|
||||
| Analytics | `http://localhost:8000/platforms/loyalty/admin/loyalty/analytics` |
|
||||
| WizaCorp Detail | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/1` |
|
||||
| WizaCorp Settings | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/1/settings` |
|
||||
| Fashion Group Detail | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/2` |
|
||||
| Fashion Group Settings | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/2/settings` |
|
||||
| BookWorld Detail | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/3` |
|
||||
| BookWorld Settings | `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/3/settings` |
|
||||
|
||||
### 2. Merchant Owner / Store Pages
|
||||
|
||||
@@ -89,87 +89,87 @@ Login as the store owner, then navigate to any of their stores.
|
||||
|
||||
| Page | Dev URL |
|
||||
|------|---------|
|
||||
| Terminal | `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/terminal` |
|
||||
| Cards | `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/cards` |
|
||||
| Settings | `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/settings` |
|
||||
| Stats | `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/enroll` |
|
||||
| Terminal | `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/terminal` |
|
||||
| Cards | `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/cards` |
|
||||
| Settings | `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/settings` |
|
||||
| Stats | `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/enroll` |
|
||||
|
||||
**Fashion Group (jane.owner@fashiongroup.com):**
|
||||
|
||||
| Page | Dev URL |
|
||||
|------|---------|
|
||||
| Terminal | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/terminal` |
|
||||
| Cards | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/cards` |
|
||||
| Settings | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/settings` |
|
||||
| Stats | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/FASHIONHUB/loyalty/enroll` |
|
||||
| Terminal | `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/loyalty/terminal` |
|
||||
| Cards | `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/loyalty/cards` |
|
||||
| Settings | `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/loyalty/settings` |
|
||||
| Stats | `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/loyalty/enroll` |
|
||||
|
||||
**BookWorld (bob.owner@bookworld.com):**
|
||||
|
||||
| Page | Dev URL |
|
||||
|------|---------|
|
||||
| Terminal | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/terminal` |
|
||||
| Cards | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/cards` |
|
||||
| Settings | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/settings` |
|
||||
| Stats | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:9999/platforms/loyalty/store/BOOKSTORE/loyalty/enroll` |
|
||||
| Terminal | `http://localhost:8000/platforms/loyalty/store/BOOKSTORE/loyalty/terminal` |
|
||||
| Cards | `http://localhost:8000/platforms/loyalty/store/BOOKSTORE/loyalty/cards` |
|
||||
| Settings | `http://localhost:8000/platforms/loyalty/store/BOOKSTORE/loyalty/settings` |
|
||||
| Stats | `http://localhost:8000/platforms/loyalty/store/BOOKSTORE/loyalty/stats` |
|
||||
| Enroll Customer | `http://localhost:8000/platforms/loyalty/store/BOOKSTORE/loyalty/enroll` |
|
||||
|
||||
### 3. Customer Storefront Pages
|
||||
|
||||
Login as a customer (e.g., `customer1@orion.example.com`).
|
||||
|
||||
!!! note "Store domain required"
|
||||
Storefront pages require a store domain context. Only ORION (`orion.shop`)
|
||||
and FASHIONHUB (`fashionhub.store`) have domains configured. In dev, storefront
|
||||
routes may need to be accessed through the store's domain or platform path.
|
||||
!!! note "Store code required in dev"
|
||||
Storefront pages in dev require the store code in the URL path:
|
||||
`/platforms/loyalty/storefront/{STORE_CODE}/...`. In production, the store
|
||||
is resolved from the domain (custom domain, merchant domain, or subdomain).
|
||||
|
||||
| Page | Dev URL |
|
||||
| Page | Dev URL (FASHIONHUB example) |
|
||||
|------|---------|
|
||||
| Loyalty Dashboard | `http://localhost:9999/platforms/loyalty/account/loyalty` |
|
||||
| Transaction History | `http://localhost:9999/platforms/loyalty/account/loyalty/history` |
|
||||
| Loyalty Dashboard | `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/loyalty` |
|
||||
| Transaction History | `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/loyalty/history` |
|
||||
|
||||
### 4. Public Pages (No Auth)
|
||||
|
||||
| Page | Dev URL |
|
||||
| Page | Dev URL (FASHIONHUB example) |
|
||||
|------|---------|
|
||||
| Self-Enrollment | `http://localhost:9999/platforms/loyalty/loyalty/join` |
|
||||
| Enrollment Success | `http://localhost:9999/platforms/loyalty/loyalty/join/success` |
|
||||
| Self-Enrollment | `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/loyalty/join` |
|
||||
| Enrollment Success | `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/loyalty/join/success` |
|
||||
|
||||
### 5. API Endpoints
|
||||
|
||||
**Admin API** (prefix: `/platforms/loyalty/api/admin/loyalty/`):
|
||||
**Admin API** (prefix: `/platforms/loyalty/api/v1/admin/loyalty/`):
|
||||
|
||||
| Method | Dev URL |
|
||||
|--------|---------|
|
||||
| GET | `http://localhost:9999/platforms/loyalty/api/admin/loyalty/programs` |
|
||||
| GET | `http://localhost:9999/platforms/loyalty/api/admin/loyalty/stats` |
|
||||
| GET | `http://localhost:8000/platforms/loyalty/api/v1/admin/loyalty/programs` |
|
||||
| GET | `http://localhost:8000/platforms/loyalty/api/v1/admin/loyalty/stats` |
|
||||
|
||||
**Store API** (prefix: `/platforms/loyalty/api/store/loyalty/`):
|
||||
**Store API** (prefix: `/platforms/loyalty/api/v1/store/loyalty/`):
|
||||
|
||||
| Method | Endpoint | Dev URL |
|
||||
|--------|----------|---------|
|
||||
| GET | program | `http://localhost:9999/platforms/loyalty/api/store/loyalty/program` |
|
||||
| POST | program | `http://localhost:9999/platforms/loyalty/api/store/loyalty/program` |
|
||||
| POST | stamp | `http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp` |
|
||||
| POST | points | `http://localhost:9999/platforms/loyalty/api/store/loyalty/points` |
|
||||
| POST | enroll | `http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/enroll` |
|
||||
| POST | lookup | `http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup` |
|
||||
| GET | program | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/program` |
|
||||
| POST | program | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/program` |
|
||||
| POST | stamp | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp` |
|
||||
| POST | points | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/points` |
|
||||
| POST | enroll | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/enroll` |
|
||||
| POST | lookup | `http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/lookup` |
|
||||
|
||||
**Storefront API** (prefix: `/platforms/loyalty/api/storefront/`):
|
||||
**Storefront API** (prefix: `/platforms/loyalty/api/v1/storefront/`):
|
||||
|
||||
| Method | Endpoint | Dev URL |
|
||||
|--------|----------|---------|
|
||||
| GET | program | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/program` |
|
||||
| POST | enroll | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/enroll` |
|
||||
| GET | card | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/card` |
|
||||
| GET | transactions | `http://localhost:9999/platforms/loyalty/api/storefront/loyalty/transactions` |
|
||||
| GET | program | `http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/program` |
|
||||
| POST | enroll | `http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/enroll` |
|
||||
| GET | card | `http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/card` |
|
||||
| GET | transactions | `http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/transactions` |
|
||||
|
||||
**Public API** (prefix: `/platforms/loyalty/api/loyalty/`):
|
||||
**Public API** (prefix: `/platforms/loyalty/api/v1/loyalty/`):
|
||||
|
||||
| Method | Endpoint | Dev URL |
|
||||
|--------|----------|---------|
|
||||
| GET | program | `http://localhost:9999/platforms/loyalty/api/loyalty/programs/ORION` |
|
||||
| GET | program | `http://localhost:8000/platforms/loyalty/api/v1/loyalty/programs/ORION` |
|
||||
|
||||
---
|
||||
|
||||
@@ -213,10 +213,10 @@ The store has a verified entry in the `store_domains` table. **All** store URLs
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET card | `https://orion.shop/api/storefront/loyalty/card` |
|
||||
| GET transactions | `https://orion.shop/api/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://orion.shop/api/storefront/loyalty/enroll` |
|
||||
| GET program | `https://orion.shop/api/storefront/loyalty/program` |
|
||||
| GET card | `https://orion.shop/api/v1/storefront/loyalty/card` |
|
||||
| GET transactions | `https://orion.shop/api/v1/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://orion.shop/api/v1/storefront/loyalty/enroll` |
|
||||
| GET program | `https://orion.shop/api/v1/storefront/loyalty/program` |
|
||||
|
||||
**Store backend (staff/owner):**
|
||||
|
||||
@@ -234,12 +234,12 @@ The store has a verified entry in the `store_domains` table. **All** store URLs
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET program | `https://orion.shop/api/store/loyalty/program` |
|
||||
| POST program | `https://orion.shop/api/store/loyalty/program` |
|
||||
| POST stamp | `https://orion.shop/api/store/loyalty/stamp` |
|
||||
| POST points | `https://orion.shop/api/store/loyalty/points` |
|
||||
| POST enroll | `https://orion.shop/api/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://orion.shop/api/store/loyalty/cards/lookup` |
|
||||
| GET program | `https://orion.shop/api/v1/store/loyalty/program` |
|
||||
| POST program | `https://orion.shop/api/v1/store/loyalty/program` |
|
||||
| POST stamp | `https://orion.shop/api/v1/store/loyalty/stamp` |
|
||||
| POST points | `https://orion.shop/api/v1/store/loyalty/points` |
|
||||
| POST enroll | `https://orion.shop/api/v1/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://orion.shop/api/v1/store/loyalty/cards/lookup` |
|
||||
|
||||
### Case 2: Store with merchant domain (e.g., `myloyaltyprogram.lu`)
|
||||
|
||||
@@ -261,10 +261,10 @@ store when the URL includes `/store/{store_code}/...`.
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET card | `https://myloyaltyprogram.lu/api/storefront/loyalty/card` |
|
||||
| GET transactions | `https://myloyaltyprogram.lu/api/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://myloyaltyprogram.lu/api/storefront/loyalty/enroll` |
|
||||
| GET program | `https://myloyaltyprogram.lu/api/storefront/loyalty/program` |
|
||||
| GET card | `https://myloyaltyprogram.lu/api/v1/storefront/loyalty/card` |
|
||||
| GET transactions | `https://myloyaltyprogram.lu/api/v1/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://myloyaltyprogram.lu/api/v1/storefront/loyalty/enroll` |
|
||||
| GET program | `https://myloyaltyprogram.lu/api/v1/storefront/loyalty/program` |
|
||||
|
||||
**Store backend (staff/owner):**
|
||||
|
||||
@@ -280,11 +280,11 @@ store when the URL includes `/store/{store_code}/...`.
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET program | `https://myloyaltyprogram.lu/api/store/loyalty/program` |
|
||||
| POST stamp | `https://myloyaltyprogram.lu/api/store/loyalty/stamp` |
|
||||
| POST points | `https://myloyaltyprogram.lu/api/store/loyalty/points` |
|
||||
| POST enroll | `https://myloyaltyprogram.lu/api/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://myloyaltyprogram.lu/api/store/loyalty/cards/lookup` |
|
||||
| GET program | `https://myloyaltyprogram.lu/api/v1/store/loyalty/program` |
|
||||
| POST stamp | `https://myloyaltyprogram.lu/api/v1/store/loyalty/stamp` |
|
||||
| POST points | `https://myloyaltyprogram.lu/api/v1/store/loyalty/points` |
|
||||
| POST enroll | `https://myloyaltyprogram.lu/api/v1/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://myloyaltyprogram.lu/api/v1/store/loyalty/cards/lookup` |
|
||||
|
||||
!!! note "Merchant domain resolves to first active store"
|
||||
When a customer visits `myloyaltyprogram.lu` without a `/store/{code}/...` path,
|
||||
@@ -310,10 +310,10 @@ The store has no entry in `store_domains` and the merchant has no registered dom
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET card | `https://bookstore.rewardflow.lu/api/storefront/loyalty/card` |
|
||||
| GET transactions | `https://bookstore.rewardflow.lu/api/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://bookstore.rewardflow.lu/api/storefront/loyalty/enroll` |
|
||||
| GET program | `https://bookstore.rewardflow.lu/api/storefront/loyalty/program` |
|
||||
| GET card | `https://bookstore.rewardflow.lu/api/v1/storefront/loyalty/card` |
|
||||
| GET transactions | `https://bookstore.rewardflow.lu/api/v1/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://bookstore.rewardflow.lu/api/v1/storefront/loyalty/enroll` |
|
||||
| GET program | `https://bookstore.rewardflow.lu/api/v1/storefront/loyalty/program` |
|
||||
|
||||
**Store backend (staff/owner):**
|
||||
|
||||
@@ -329,11 +329,11 @@ The store has no entry in `store_domains` and the merchant has no registered dom
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET program | `https://bookstore.rewardflow.lu/api/store/loyalty/program` |
|
||||
| POST stamp | `https://bookstore.rewardflow.lu/api/store/loyalty/stamp` |
|
||||
| POST points | `https://bookstore.rewardflow.lu/api/store/loyalty/points` |
|
||||
| POST enroll | `https://bookstore.rewardflow.lu/api/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://bookstore.rewardflow.lu/api/store/loyalty/cards/lookup` |
|
||||
| GET program | `https://bookstore.rewardflow.lu/api/v1/store/loyalty/program` |
|
||||
| POST stamp | `https://bookstore.rewardflow.lu/api/v1/store/loyalty/stamp` |
|
||||
| POST points | `https://bookstore.rewardflow.lu/api/v1/store/loyalty/points` |
|
||||
| POST enroll | `https://bookstore.rewardflow.lu/api/v1/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://bookstore.rewardflow.lu/api/v1/store/loyalty/cards/lookup` |
|
||||
|
||||
### Platform Admin & Public API (always on platform domain)
|
||||
|
||||
@@ -343,10 +343,10 @@ The store has no entry in `store_domains` and the merchant has no registered dom
|
||||
| Admin Analytics | `https://rewardflow.lu/admin/loyalty/analytics` |
|
||||
| Admin Merchant Detail | `https://rewardflow.lu/admin/loyalty/merchants/{id}` |
|
||||
| Admin Merchant Settings | `https://rewardflow.lu/admin/loyalty/merchants/{id}/settings` |
|
||||
| Admin API - Programs | `GET https://rewardflow.lu/api/admin/loyalty/programs` |
|
||||
| Admin API - Stats | `GET https://rewardflow.lu/api/admin/loyalty/stats` |
|
||||
| Public API - Program | `GET https://rewardflow.lu/api/loyalty/programs/ORION` |
|
||||
| Apple Wallet Pass | `GET https://rewardflow.lu/api/loyalty/passes/apple/{serial}.pkpass` |
|
||||
| Admin API - Programs | `GET https://rewardflow.lu/api/v1/admin/loyalty/programs` |
|
||||
| Admin API - Stats | `GET https://rewardflow.lu/api/v1/admin/loyalty/stats` |
|
||||
| Public API - Program | `GET https://rewardflow.lu/api/v1/loyalty/programs/ORION` |
|
||||
| Apple Wallet Pass | `GET https://rewardflow.lu/api/v1/loyalty/passes/apple/{serial}.pkpass` |
|
||||
|
||||
### Domain configuration per store (current DB state)
|
||||
|
||||
@@ -418,19 +418,19 @@ flowchart TD
|
||||
**Step 1: Subscribe to the platform**
|
||||
|
||||
1. Login as `john.owner@wizacorp.com` and navigate to billing:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/billing`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/billing`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/billing`
|
||||
- Prod (subdomain): `https://orion.rewardflow.lu/store/ORION/billing`
|
||||
2. View available subscription tiers:
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/v1/store/billing/tiers`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/store/billing/tiers`
|
||||
- API Prod: `GET https://{store_domain}/api/v1/store/billing/tiers`
|
||||
3. Select a tier and initiate Stripe checkout:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/store/billing/checkout`
|
||||
- API Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/billing/checkout`
|
||||
- API Prod: `POST https://{store_domain}/api/v1/store/billing/checkout`
|
||||
4. Complete payment on Stripe checkout page
|
||||
5. Webhook `checkout.session.completed` activates the subscription
|
||||
6. Verify subscription is active:
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/v1/store/billing/subscription`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/store/billing/subscription`
|
||||
- API Prod: `GET https://{store_domain}/api/v1/store/billing/subscription`
|
||||
|
||||
**Step 2: Register merchant domain (admin action)**
|
||||
@@ -440,19 +440,19 @@ flowchart TD
|
||||
registers the domain on behalf of the merchant via the admin API.
|
||||
|
||||
1. Platform admin registers a merchant domain:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/admin/merchants/{merchant_id}/domains`
|
||||
- API Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/admin/merchants/{merchant_id}/domains`
|
||||
- API Prod: `POST https://rewardflow.lu/api/v1/admin/merchants/{merchant_id}/domains`
|
||||
- Body: `{"domain": "myloyaltyprogram.lu", "is_primary": true}`
|
||||
2. The API returns a `verification_token` for DNS verification
|
||||
3. Get DNS verification instructions:
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}/verification-instructions`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}/verification-instructions`
|
||||
- API Prod: `GET https://rewardflow.lu/api/v1/admin/merchants/domains/merchant/{domain_id}/verification-instructions`
|
||||
4. Merchant adds a DNS TXT record: `_orion-verify.myloyaltyprogram.lu TXT {verification_token}`
|
||||
5. Verify the domain:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}/verify`
|
||||
- API Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}/verify`
|
||||
- API Prod: `POST https://rewardflow.lu/api/v1/admin/merchants/domains/merchant/{domain_id}/verify`
|
||||
6. Activate the domain:
|
||||
- API Dev: `PUT http://localhost:9999/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}`
|
||||
- API Dev: `PUT http://localhost:8000/platforms/loyalty/api/v1/admin/merchants/domains/merchant/{domain_id}`
|
||||
- API Prod: `PUT https://rewardflow.lu/api/v1/admin/merchants/domains/merchant/{domain_id}`
|
||||
- Body: `{"is_active": true}`
|
||||
7. All merchant stores now inherit `myloyaltyprogram.lu` as their effective domain
|
||||
@@ -462,7 +462,7 @@ flowchart TD
|
||||
If a store needs its own domain (e.g., ORION is a major brand and wants `mysuperloyaltyprogram.lu`):
|
||||
|
||||
1. Platform admin registers a store domain:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/admin/stores/{store_id}/domains`
|
||||
- API Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/admin/stores/{store_id}/domains`
|
||||
- API Prod: `POST https://rewardflow.lu/api/v1/admin/stores/{store_id}/domains`
|
||||
- Body: `{"domain": "mysuperloyaltyprogram.lu", "is_primary": true}`
|
||||
2. Follow the same DNS verification and activation flow as merchant domains
|
||||
@@ -507,25 +507,25 @@ flowchart TD
|
||||
**Steps:**
|
||||
|
||||
1. Login as `john.owner@wizacorp.com` at:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/login`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/login`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/login`
|
||||
- Prod (subdomain): `https://orion.rewardflow.lu/store/ORION/login`
|
||||
2. Navigate to loyalty settings:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/settings`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/settings`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/loyalty/settings`
|
||||
- Prod (subdomain): `https://orion.rewardflow.lu/store/ORION/loyalty/settings`
|
||||
3. Create a new loyalty program:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/program`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/program`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/program`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/program`
|
||||
4. Choose loyalty type (stamps, points, or hybrid)
|
||||
5. Configure program parameters (stamp target, points-per-euro, rewards)
|
||||
6. Set branding (card color, logo, hero image)
|
||||
7. Configure anti-fraud (cooldown, daily limits, PIN requirements)
|
||||
8. Create staff PINs:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/pins`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/pins`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/pins`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/pins`
|
||||
9. Verify program is live - check from another store (same merchant):
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/settings`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/WIZAGADGETS/loyalty/settings`
|
||||
- Prod (subdomain): `https://wizagadgets.rewardflow.lu/store/WIZAGADGETS/loyalty/settings`
|
||||
|
||||
**Expected blockers in current state:**
|
||||
@@ -563,23 +563,23 @@ flowchart TD
|
||||
**Steps:**
|
||||
|
||||
1. Login as `alice.manager@wizacorp.com` and open the terminal:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/terminal`
|
||||
2. Scan customer QR code or enter card number:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/cards/lookup`
|
||||
3. Enter staff PIN for verification
|
||||
4. Add stamp:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/stamp`
|
||||
5. If target reached, redeem reward:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/redeem`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/stamp/redeem`
|
||||
6. View updated card:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/cards/{card_id}`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/cards/{card_id}`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/cards/{card_id}`
|
||||
7. Browse all cards:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/cards`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/cards`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/cards`
|
||||
|
||||
**Anti-fraud scenarios to test:**
|
||||
@@ -611,20 +611,20 @@ flowchart TD
|
||||
**Steps:**
|
||||
|
||||
1. Open the terminal:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/terminal`
|
||||
2. Lookup card:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/cards/lookup`
|
||||
3. Enter purchase amount (e.g., 25.00 EUR)
|
||||
4. Earn points (auto-calculated at 10 pts/EUR = 250 points):
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/points`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/points`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/points`
|
||||
5. If enough balance, redeem points for reward:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/points/redeem`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/points/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/points/redeem`
|
||||
6. Check store-level stats:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/stats`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/stats`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/stats`
|
||||
|
||||
---
|
||||
@@ -647,21 +647,21 @@ flowchart TD
|
||||
**Steps:**
|
||||
|
||||
1. Visit the public enrollment page:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/loyalty/join`
|
||||
- Prod (custom domain): `https://orion.shop/loyalty/join`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/loyalty/join`
|
||||
- Prod (custom domain): `https://fashionhub.store/loyalty/join`
|
||||
- Prod (subdomain): `https://bookstore.rewardflow.lu/loyalty/join`
|
||||
2. Fill in enrollment form (email, name)
|
||||
3. Submit enrollment:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/storefront/loyalty/enroll`
|
||||
- Prod (custom domain): `POST https://orion.shop/api/storefront/loyalty/enroll`
|
||||
- Prod (subdomain): `POST https://bookstore.rewardflow.lu/api/storefront/loyalty/enroll`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/enroll`
|
||||
- Prod (custom domain): `POST https://fashionhub.store/api/v1/storefront/loyalty/enroll`
|
||||
- Prod (subdomain): `POST https://bookstore.rewardflow.lu/api/v1/storefront/loyalty/enroll`
|
||||
4. Redirected to success page:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
||||
- Prod (custom domain): `https://orion.shop/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
||||
- Prod (custom domain): `https://fashionhub.store/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
||||
- Prod (subdomain): `https://bookstore.rewardflow.lu/loyalty/join/success?card=XXXX-XXXX-XXXX`
|
||||
5. Optionally download Apple Wallet pass:
|
||||
- Dev: `GET http://localhost:9999/platforms/loyalty/api/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
- Prod: `GET https://rewardflow.lu/api/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
- Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
- Prod: `GET https://rewardflow.lu/api/v1/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
|
||||
---
|
||||
|
||||
@@ -674,17 +674,17 @@ flowchart TD
|
||||
|
||||
1. Login as customer at the storefront
|
||||
2. View loyalty dashboard (card balance, available rewards):
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty`
|
||||
- Prod (custom domain): `https://orion.shop/account/loyalty`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/loyalty`
|
||||
- Prod (custom domain): `https://fashionhub.store/account/loyalty`
|
||||
- Prod (subdomain): `https://bookstore.rewardflow.lu/account/loyalty`
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/storefront/loyalty/card`
|
||||
- API Prod: `GET https://orion.shop/api/storefront/loyalty/card`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/card`
|
||||
- API Prod: `GET https://fashionhub.store/api/v1/storefront/loyalty/card`
|
||||
3. View full transaction history:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty/history`
|
||||
- Prod (custom domain): `https://orion.shop/account/loyalty/history`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/loyalty/history`
|
||||
- Prod (custom domain): `https://fashionhub.store/account/loyalty/history`
|
||||
- Prod (subdomain): `https://bookstore.rewardflow.lu/account/loyalty/history`
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/storefront/loyalty/transactions`
|
||||
- API Prod: `GET https://orion.shop/api/storefront/loyalty/transactions`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/storefront/loyalty/transactions`
|
||||
- API Prod: `GET https://fashionhub.store/api/v1/storefront/loyalty/transactions`
|
||||
|
||||
---
|
||||
|
||||
@@ -697,22 +697,22 @@ flowchart TD
|
||||
|
||||
1. Login as admin
|
||||
2. View all programs:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/programs`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/programs`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/programs`
|
||||
3. View platform-wide analytics:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/analytics`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/analytics`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/analytics`
|
||||
4. Drill into WizaCorp's program:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/1`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/1`
|
||||
5. Manage WizaCorp's merchant-level settings:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/1/settings`
|
||||
- API Dev: `PATCH http://localhost:9999/platforms/loyalty/api/admin/loyalty/merchants/1/settings`
|
||||
- API Prod: `PATCH https://rewardflow.lu/api/admin/loyalty/merchants/1/settings`
|
||||
- API Dev: `PATCH http://localhost:8000/platforms/loyalty/api/v1/admin/loyalty/merchants/1/settings`
|
||||
- API Prod: `PATCH https://rewardflow.lu/api/v1/admin/loyalty/merchants/1/settings`
|
||||
6. Adjust settings: PIN policy, self-enrollment toggle, void permissions
|
||||
7. Check other merchants:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/2`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/2`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/2`
|
||||
|
||||
---
|
||||
@@ -725,21 +725,21 @@ flowchart TD
|
||||
**Steps:**
|
||||
|
||||
1. Open terminal and lookup card:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/terminal`
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/cards/lookup`
|
||||
2. View the card's transaction history to find the transaction to void:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/cards/{card_id}`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/cards/{card_id}`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/cards/{card_id}`
|
||||
- API Dev: `GET http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/{card_id}/transactions`
|
||||
- API Prod: `GET https://{store_domain}/api/store/loyalty/cards/{card_id}/transactions`
|
||||
- API Dev: `GET http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/{card_id}/transactions`
|
||||
- API Prod: `GET https://{store_domain}/api/v1/store/loyalty/cards/{card_id}/transactions`
|
||||
3. Void a stamp transaction:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/void`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/void`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp/void`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/stamp/void`
|
||||
4. Or void a points transaction:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/points/void`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/points/void`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/points/void`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/points/void`
|
||||
5. Verify: original and void transactions are linked in the audit log
|
||||
|
||||
---
|
||||
@@ -751,28 +751,28 @@ flowchart TD
|
||||
|
||||
**Precondition:** Cross-location redemption must be enabled in merchant settings:
|
||||
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/1/settings`
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Staff at ORION adds stamps to customer's card:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/ORION/loyalty/terminal`
|
||||
- Prod: `https://{store_domain}/store/ORION/loyalty/terminal`
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/stamp`
|
||||
2. Customer visits WIZAGADGETS
|
||||
3. Staff at WIZAGADGETS looks up the same card:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/terminal`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/WIZAGADGETS/loyalty/terminal`
|
||||
- Prod: `https://{store_domain}/store/WIZAGADGETS/loyalty/terminal`
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/cards/lookup`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/cards/lookup`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/cards/lookup`
|
||||
4. Card is found (same merchant) with accumulated stamps
|
||||
5. Staff at WIZAGADGETS redeems the reward:
|
||||
- Dev: `POST http://localhost:9999/platforms/loyalty/api/store/loyalty/stamp/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/stamp/redeem`
|
||||
- Dev: `POST http://localhost:8000/platforms/loyalty/api/v1/store/loyalty/stamp/redeem`
|
||||
- Prod: `POST https://{store_domain}/api/v1/store/loyalty/stamp/redeem`
|
||||
6. Verify transaction history shows both stores:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/cards/{card_id}`
|
||||
- Dev: `http://localhost:8000/platforms/loyalty/store/WIZAGADGETS/loyalty/cards/{card_id}`
|
||||
- Prod: `https://{store_domain}/store/WIZAGADGETS/loyalty/cards/{card_id}`
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user