refactor: remove all backward compatibility code across 70 files
Some checks failed
Some checks failed
Clean up 28 backward compatibility instances identified in the codebase. The app is not live, so all shims are replaced with the target architecture: - Remove legacy Inventory.location column (use bin_location exclusively) - Remove dashboard _extract_metric_value helper (use flat metrics dict) - Remove legacy stat field duplicates (total_stores, total_imports, etc.) - Remove 13 re-export shims and class aliases across modules - Remove module-enabling JSON fallback (use PlatformModule junction table) - Remove menu_to_legacy_format() conversion (return dataclasses directly) - Remove title/description from MarketplaceProductBase schema - Clean billing convenience method docstrings - Clean test fixtures and backward-compat comments - Add PlatformModule seeding to init_production.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,9 @@ The application serves multiple frontends from a single codebase:
|
||||
|
||||
| Frontend | Description | Example URLs |
|
||||
|----------|-------------|--------------|
|
||||
| **ADMIN** | Platform administration | `/admin/*`, `/api/v1/admin/*`, `admin.oms.lu/*` |
|
||||
| **ADMIN** | Platform administration | `/admin/*`, `/api/v1/admin/*`, `admin.omsflow.lu/*` |
|
||||
| **STORE** | Store dashboard | `/store/*`, `/api/v1/store/*` |
|
||||
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/stores/*`, `orion.oms.lu/*` |
|
||||
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/stores/*`, `orion.omsflow.lu/*` |
|
||||
| **PLATFORM** | Marketing pages | `/`, `/pricing`, `/about` |
|
||||
|
||||
The `FrontendDetector` class provides centralized, consistent detection of which frontend a request targets.
|
||||
@@ -64,13 +64,13 @@ class FrontendType(str, Enum):
|
||||
The `FrontendDetector` uses the following priority order:
|
||||
|
||||
```
|
||||
1. Admin subdomain (admin.oms.lu) → ADMIN
|
||||
1. Admin subdomain (admin.omsflow.lu) → ADMIN
|
||||
2. Path-based detection:
|
||||
- /admin/* or /api/v1/admin/* → ADMIN
|
||||
- /store/* or /api/v1/store/* → STORE
|
||||
- /storefront/*, /shop/*, /stores/* → STOREFRONT
|
||||
- /api/v1/platform/* → PLATFORM
|
||||
3. Store subdomain (orion.oms.lu) → STOREFRONT
|
||||
3. Store subdomain (orion.omsflow.lu) → STOREFRONT
|
||||
4. Store context set by middleware → STOREFRONT
|
||||
5. Default → PLATFORM
|
||||
```
|
||||
@@ -133,7 +133,7 @@ from app.modules.enums import FrontendType
|
||||
|
||||
# Full detection
|
||||
frontend_type = FrontendDetector.detect(
|
||||
host="orion.oms.lu",
|
||||
host="orion.omsflow.lu",
|
||||
path="/products",
|
||||
has_store_context=True
|
||||
)
|
||||
@@ -167,10 +167,10 @@ if FrontendDetector.is_storefront(host, path, has_store_context=True):
|
||||
|
||||
| Request | Host | Path | Frontend |
|
||||
|---------|------|------|----------|
|
||||
| Admin subdomain | admin.oms.lu | /dashboard | ADMIN |
|
||||
| Store subdomain | orion.oms.lu | /products | STOREFRONT |
|
||||
| Admin subdomain | admin.omsflow.lu | /dashboard | ADMIN |
|
||||
| Store subdomain | orion.omsflow.lu | /products | STOREFRONT |
|
||||
| Custom domain | mybakery.lu | /products | STOREFRONT |
|
||||
| Platform root | oms.lu | /pricing | PLATFORM |
|
||||
| Platform root | omsflow.lu | /pricing | PLATFORM |
|
||||
|
||||
## Request State
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ This middleware layer is **system-wide** and enables the multi-tenant architectu
|
||||
|
||||
**What it does**:
|
||||
- Detects platform from:
|
||||
- Custom domain (e.g., `oms.lu`, `loyalty.lu`)
|
||||
- Custom domain (e.g., `omsflow.lu`, `rewardflow.lu`)
|
||||
- Path prefix in development (e.g., `/platforms/oms/`, `/platforms/loyalty/`)
|
||||
- Default to `main` platform for localhost without prefix
|
||||
- Rewrites path for platform-prefixed requests (strips `/platforms/{code}/`)
|
||||
@@ -33,7 +33,7 @@ Request arrives
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Production domain? (oms.lu, etc.) │
|
||||
│ Production domain? (omsflow.lu, etc.) │
|
||||
└─────────────────────────────────────┘
|
||||
│ YES → Use that platform
|
||||
│
|
||||
@@ -135,13 +135,13 @@ Injects: request.state.store = <Store object>
|
||||
|
||||
**Detection Priority** (handled by `FrontendDetector`):
|
||||
```python
|
||||
1. Admin subdomain (admin.oms.lu) → ADMIN
|
||||
1. Admin subdomain (admin.omsflow.lu) → ADMIN
|
||||
2. Path-based detection:
|
||||
- /admin/* or /api/v1/admin/* → ADMIN
|
||||
- /store/* or /api/v1/store/* → STORE
|
||||
- /storefront/*, /shop/*, /stores/* → STOREFRONT
|
||||
- /api/v1/platform/* → PLATFORM
|
||||
3. Store subdomain (orion.oms.lu) → STOREFRONT
|
||||
3. Store subdomain (orion.omsflow.lu) → STOREFRONT
|
||||
4. Store context set by middleware → STOREFRONT
|
||||
5. Default → PLATFORM
|
||||
```
|
||||
|
||||
@@ -123,7 +123,7 @@ The system uses different URL patterns for development vs production:
|
||||
|
||||
**Production (custom domains):**
|
||||
- Main marketing site: `orion.lu/` → `main` platform
|
||||
- Platform sites: `oms.lu/`, `loyalty.lu/` → specific platform
|
||||
- Platform sites: `omsflow.lu/`, `rewardflow.lu/` → specific platform
|
||||
|
||||
### Request Processing
|
||||
|
||||
@@ -259,7 +259,7 @@ Request: GET /about
|
||||
1. Insert platform record:
|
||||
```sql
|
||||
INSERT INTO platforms (code, name, description, domain, path_prefix)
|
||||
VALUES ('loyalty', 'Loyalty Program', 'Customer loyalty and rewards', 'loyalty.lu', 'loyalty');
|
||||
VALUES ('loyalty', 'Loyalty Program', 'Customer loyalty and rewards', 'rewardflow.lu', 'loyalty');
|
||||
```
|
||||
|
||||
2. Create platform-specific content pages:
|
||||
@@ -270,7 +270,7 @@ Request: GET /about
|
||||
|
||||
3. Configure routing:
|
||||
- **Development:** Access at `localhost:9999/platforms/loyalty/`
|
||||
- **Production:** Access at `loyalty.lu/`
|
||||
- **Production:** Access at `rewardflow.lu/`
|
||||
- Platform detected automatically by `PlatformContextMiddleware`
|
||||
- No additional route configuration needed
|
||||
|
||||
@@ -285,5 +285,5 @@ Request: GET /about
|
||||
| Platform | Code | Dev URL | Prod URL |
|
||||
|----------|------|---------|----------|
|
||||
| Main Marketing | `main` | `localhost:9999/` | `orion.lu/` |
|
||||
| OMS | `oms` | `localhost:9999/platforms/oms/` | `oms.lu/` |
|
||||
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `loyalty.lu/` |
|
||||
| OMS | `oms` | `localhost:9999/platforms/oms/` | `omsflow.lu/` |
|
||||
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `rewardflow.lu/` |
|
||||
|
||||
@@ -63,14 +63,14 @@ Orion supports multiple platforms (OMS, Loyalty, Site Builder), each with its ow
|
||||
|-----|----------------|
|
||||
| `orion.lu/` | Main marketing site homepage |
|
||||
| `orion.lu/about` | Main marketing site about page |
|
||||
| `oms.lu/` | OMS platform homepage |
|
||||
| `oms.lu/pricing` | OMS platform pricing page |
|
||||
| `oms.lu/admin/` | Admin panel for OMS platform |
|
||||
| `oms.lu/store/{code}/` | Store dashboard on OMS |
|
||||
| `omsflow.lu/` | OMS platform homepage |
|
||||
| `omsflow.lu/pricing` | OMS platform pricing page |
|
||||
| `omsflow.lu/admin/` | Admin panel for OMS platform |
|
||||
| `omsflow.lu/store/{code}/` | Store dashboard on OMS |
|
||||
| `https://mybakery.lu/storefront/` | Store storefront (store's custom domain) |
|
||||
| `loyalty.lu/` | Loyalty platform homepage |
|
||||
| `rewardflow.lu/` | Loyalty platform homepage |
|
||||
|
||||
**Note:** In production, stores configure their own custom domains for storefronts. The platform domain (e.g., `oms.lu`) is used for admin and store dashboards, while storefronts use store-owned domains.
|
||||
**Note:** In production, stores configure their own custom domains for storefronts. The platform domain (e.g., `omsflow.lu`) is used for admin and store dashboards, while storefronts use store-owned domains.
|
||||
|
||||
### Quick Reference by Platform
|
||||
|
||||
@@ -83,9 +83,9 @@ Dev:
|
||||
Storefront: http://localhost:8000/platforms/oms/stores/{store_code}/storefront/
|
||||
|
||||
Prod:
|
||||
Platform: https://oms.lu/
|
||||
Admin: https://oms.lu/admin/
|
||||
Store: https://oms.lu/store/{store_code}/
|
||||
Platform: https://omsflow.lu/
|
||||
Admin: https://omsflow.lu/admin/
|
||||
Store: https://omsflow.lu/store/{store_code}/
|
||||
Storefront: https://mybakery.lu/storefront/ (store's custom domain)
|
||||
```
|
||||
|
||||
@@ -98,9 +98,9 @@ Dev:
|
||||
Storefront: http://localhost:8000/platforms/loyalty/stores/{store_code}/storefront/
|
||||
|
||||
Prod:
|
||||
Platform: https://loyalty.lu/
|
||||
Admin: https://loyalty.lu/admin/
|
||||
Store: https://loyalty.lu/store/{store_code}/
|
||||
Platform: https://rewardflow.lu/
|
||||
Admin: https://rewardflow.lu/admin/
|
||||
Store: https://rewardflow.lu/store/{store_code}/
|
||||
Storefront: https://myrewards.lu/storefront/ (store's custom domain)
|
||||
```
|
||||
|
||||
@@ -112,7 +112,7 @@ Request arrives
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Check: Is this production domain? │
|
||||
│ (oms.lu, loyalty.lu, etc.) │
|
||||
│ (omsflow.lu, rewardflow.lu, etc.) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── YES → Route to that platform
|
||||
@@ -139,8 +139,8 @@ Request arrives
|
||||
| Platform | Code | Dev URL | Prod Domain |
|
||||
|----------|------|---------|-------------|
|
||||
| Main Marketing | `main` | `localhost:8000/` | `orion.lu` |
|
||||
| OMS | `oms` | `localhost:8000/platforms/oms/` | `oms.lu` |
|
||||
| Loyalty | `loyalty` | `localhost:8000/platforms/loyalty/` | `loyalty.lu` |
|
||||
| OMS | `oms` | `localhost:8000/platforms/oms/` | `omsflow.lu` |
|
||||
| Loyalty | `loyalty` | `localhost:8000/platforms/loyalty/` | `rewardflow.lu` |
|
||||
| Site Builder | `site-builder` | `localhost:8000/platforms/site-builder/` | `sitebuilder.lu` |
|
||||
|
||||
**See:** [Multi-Platform CMS Architecture](../multi-platform-cms.md) for content management details.
|
||||
|
||||
@@ -204,7 +204,7 @@ Migration: `alembic/versions/z5f6g7h8i9j0_add_loyalty_platform.py`
|
||||
Inserts Loyalty platform with:
|
||||
- code: `loyalty`
|
||||
- name: `Loyalty+`
|
||||
- domain: `loyalty.lu`
|
||||
- domain: `rewardflow.lu`
|
||||
- path_prefix: `loyalty`
|
||||
- theme_config: purple color scheme
|
||||
|
||||
@@ -256,8 +256,8 @@ Inserts Loyalty platform with:
|
||||
| Platform | Code | Dev URL | Prod URL |
|
||||
|----------|------|---------|----------|
|
||||
| Main Marketing | `main` | `localhost:9999/` | `orion.lu/` |
|
||||
| OMS | `oms` | `localhost:9999/platforms/oms/` | `oms.lu/` |
|
||||
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `loyalty.lu/` |
|
||||
| OMS | `oms` | `localhost:9999/platforms/oms/` | `omsflow.lu/` |
|
||||
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `rewardflow.lu/` |
|
||||
|
||||
### 7.4 Middleware Detection Logic
|
||||
|
||||
@@ -266,7 +266,7 @@ Request arrives
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Production domain? (oms.lu, etc.) │
|
||||
│ Production domain? (omsflow.lu, etc.) │
|
||||
└─────────────────────────────────────┘
|
||||
│ YES → Route to that platform
|
||||
│
|
||||
@@ -324,7 +324,7 @@ Included in `docs/architecture/multi-platform-cms.md`:
|
||||
### Three-Tier Content Resolution
|
||||
|
||||
```
|
||||
Customer visits: oms.lu/stores/orion/about
|
||||
Customer visits: omsflow.lu/stores/orion/about
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
|
||||
@@ -54,7 +54,7 @@ Complete step-by-step guide for deploying Orion on a Hetzner Cloud VPS.
|
||||
|
||||
**Deferred (not urgent, do when all platforms ready):**
|
||||
|
||||
- [ ] DNS A + AAAA records for platform domains (`oms.lu`, `rewardflow.lu`)
|
||||
- [ ] DNS A + AAAA records for platform domains (`omsflow.lu`, `rewardflow.lu`)
|
||||
- [ ] Uncomment platform domains in Caddyfile after DNS propagation
|
||||
|
||||
!!! success "Progress — 2026-02-14"
|
||||
@@ -63,7 +63,7 @@ Complete step-by-step guide for deploying Orion on a Hetzner Cloud VPS.
|
||||
- **Wizamart → Orion rename** — 1,086 occurrences replaced across 184 files (database identifiers, email addresses, domains, config, templates, docs, seed data)
|
||||
- Template renamed: `homepage-wizamart.html` → `homepage-orion.html`
|
||||
- **Production DB rebuilt from scratch** with Orion naming (`orion_db`, `orion_user`)
|
||||
- Platform domains configured in seed data: wizard.lu (main), oms.lu, rewardflow.lu (loyalty)
|
||||
- Platform domains configured in seed data: wizard.lu (main), omsflow.lu, rewardflow.lu (loyalty)
|
||||
- Docker volume explicitly named `orion_postgres_data`
|
||||
- `.dockerignore` added — prevents `.env` from being baked into Docker images
|
||||
- `env_file: .env` added to `docker-compose.yml` — containers load host env vars properly
|
||||
@@ -438,7 +438,7 @@ Before setting up Caddy, point your domain's DNS to the server.
|
||||
| A | `git` | `91.99.65.229` | 300 |
|
||||
| A | `flower` | `91.99.65.229` | 300 |
|
||||
|
||||
### oms.lu (OMS Platform) — TODO
|
||||
### omsflow.lu (OMS Platform) — TODO
|
||||
|
||||
| Type | Name | Value | TTL |
|
||||
|---|---|---|---|
|
||||
@@ -470,7 +470,7 @@ It should match the value in the Hetzner Cloud Console (Networking tab). Then cr
|
||||
| AAAA | `git` | `2a01:4f8:1c1a:b39c::1` | 300 |
|
||||
| AAAA | `flower` | `2a01:4f8:1c1a:b39c::1` | 300 |
|
||||
|
||||
Repeat for `oms.lu` and `rewardflow.lu`.
|
||||
Repeat for `omsflow.lu` and `rewardflow.lu`.
|
||||
|
||||
!!! tip "DNS propagation"
|
||||
Set TTL to 300 (5 minutes) initially. DNS changes can take up to 24 hours to propagate globally, but usually complete within 30 minutes. Verify with: `dig api.wizard.lu +short`
|
||||
@@ -502,14 +502,14 @@ www.wizard.lu {
|
||||
redir https://wizard.lu{uri} permanent
|
||||
}
|
||||
|
||||
# ─── Platform 2: OMS (oms.lu) ───────────────────────────────
|
||||
# Uncomment after DNS is configured for oms.lu
|
||||
# oms.lu {
|
||||
# ─── Platform 2: OMS (omsflow.lu) ───────────────────────────────
|
||||
# Uncomment after DNS is configured for omsflow.lu
|
||||
# omsflow.lu {
|
||||
# reverse_proxy localhost:8001
|
||||
# }
|
||||
#
|
||||
# www.oms.lu {
|
||||
# redir https://oms.lu{uri} permanent
|
||||
# www.omsflow.lu {
|
||||
# redir https://omsflow.lu{uri} permanent
|
||||
# }
|
||||
|
||||
# ─── Platform 3: Loyalty+ (rewardflow.lu) ──────────────────
|
||||
@@ -537,14 +537,14 @@ flower.wizard.lu {
|
||||
```
|
||||
|
||||
!!! info "How multi-platform routing works"
|
||||
All platform domains (`wizard.lu`, `oms.lu`, `rewardflow.lu`) point to the **same FastAPI backend** on port 8001. The `PlatformContextMiddleware` reads the `Host` header to detect which platform the request is for. Caddy preserves the Host header by default, so no extra configuration is needed.
|
||||
All platform domains (`wizard.lu`, `omsflow.lu`, `rewardflow.lu`) point to the **same FastAPI backend** on port 8001. The `PlatformContextMiddleware` reads the `Host` header to detect which platform the request is for. Caddy preserves the Host header by default, so no extra configuration is needed.
|
||||
|
||||
The `domain` column in the `platforms` database table must match:
|
||||
|
||||
| Platform | code | domain |
|
||||
|---|---|---|
|
||||
| Main | `main` | `wizard.lu` |
|
||||
| OMS | `oms` | `oms.lu` |
|
||||
| OMS | `oms` | `omsflow.lu` |
|
||||
| Loyalty+ | `loyalty` | `rewardflow.lu` |
|
||||
|
||||
Start Caddy:
|
||||
@@ -588,17 +588,17 @@ cd ~/gitea && docker compose up -d gitea
|
||||
|
||||
Stores on each platform use two routing modes:
|
||||
|
||||
- **Standard (subdomain)**: `acme.oms.lu` — included in the base subscription
|
||||
- **Standard (subdomain)**: `acme.omsflow.lu` — included in the base subscription
|
||||
- **Premium (custom domain)**: `acme.lu` — available with premium subscription tiers
|
||||
|
||||
Both modes are handled by the `StoreContextMiddleware` which reads the `Host` header, so Caddy just needs to forward requests and preserve the header.
|
||||
|
||||
#### Wildcard Subdomains (for store subdomains)
|
||||
|
||||
When stores start using subdomains like `acme.oms.lu`, add wildcard blocks:
|
||||
When stores start using subdomains like `acme.omsflow.lu`, add wildcard blocks:
|
||||
|
||||
```caddy
|
||||
*.oms.lu {
|
||||
*.omsflow.lu {
|
||||
reverse_proxy localhost:8001
|
||||
}
|
||||
|
||||
@@ -1099,7 +1099,7 @@ docker stats --no-stream
|
||||
|---|---|---|---|
|
||||
| Orion API | 8000 | 8001 | `api.wizard.lu` |
|
||||
| Main Platform | 8000 | 8001 | `wizard.lu` |
|
||||
| OMS Platform | 8000 | 8001 | `oms.lu` (TODO) |
|
||||
| OMS Platform | 8000 | 8001 | `omsflow.lu` (TODO) |
|
||||
| Loyalty+ Platform | 8000 | 8001 | `rewardflow.lu` (TODO) |
|
||||
| PostgreSQL | 5432 | 5432 | (internal only) |
|
||||
| Redis | 6379 | 6380 | (internal only) |
|
||||
@@ -1250,7 +1250,7 @@ After Caddy is configured:
|
||||
| Gitea | `https://git.wizard.lu` |
|
||||
| Flower | `https://flower.wizard.lu` |
|
||||
| Grafana | `https://grafana.wizard.lu` |
|
||||
| OMS Platform | `https://oms.lu` (after DNS) |
|
||||
| OMS Platform | `https://omsflow.lu` (after DNS) |
|
||||
| Loyalty+ Platform | `https://rewardflow.lu` (after DNS) |
|
||||
|
||||
Direct IP access (temporary, until firewall rules are removed):
|
||||
|
||||
@@ -173,20 +173,20 @@ Login as a customer (e.g., `customer1@orion.example.com`).
|
||||
|
||||
---
|
||||
|
||||
## Production URLs (loyalty.lu)
|
||||
## Production URLs (rewardflow.lu)
|
||||
|
||||
In production, the platform uses **domain-based routing** instead of the `/platforms/loyalty/` path prefix.
|
||||
Store context is detected via **custom domains** (registered in `store_domains` table)
|
||||
or **subdomains** of `loyalty.lu` (from `Store.subdomain`).
|
||||
or **subdomains** of `rewardflow.lu` (from `Store.subdomain`).
|
||||
|
||||
### URL Routing Summary
|
||||
|
||||
| Routing mode | Priority | Pattern | Example |
|
||||
|-------------|----------|---------|---------|
|
||||
| Platform domain | — | `loyalty.lu/...` | Admin pages, public API |
|
||||
| Platform domain | — | `rewardflow.lu/...` | Admin pages, public API |
|
||||
| Store custom domain | 1 (highest) | `{custom_domain}/...` | Store with its own domain (overrides merchant domain) |
|
||||
| Merchant domain | 2 | `{merchant_domain}/...` | All stores inherit merchant's domain |
|
||||
| Store subdomain | 3 (fallback) | `{store_code}.loyalty.lu/...` | Default when no custom/merchant domain |
|
||||
| Store subdomain | 3 (fallback) | `{store_code}.rewardflow.lu/...` | Default when no custom/merchant domain |
|
||||
|
||||
!!! info "Domain Resolution Priority"
|
||||
When a request arrives, the middleware resolves the store in this order:
|
||||
@@ -295,58 +295,58 @@ store when the URL includes `/store/{store_code}/...`.
|
||||
### Case 3: Store without custom domain (uses platform subdomain)
|
||||
|
||||
The store has no entry in `store_domains` and the merchant has no registered domain.
|
||||
**All** store URLs are served via a subdomain of the platform domain: `{store_code}.loyalty.lu`.
|
||||
**All** store URLs are served via a subdomain of the platform domain: `{store_code}.rewardflow.lu`.
|
||||
|
||||
**Storefront (customer-facing):**
|
||||
|
||||
| Page | Production URL |
|
||||
|------|----------------|
|
||||
| Loyalty Dashboard | `https://bookstore.loyalty.lu/account/loyalty` |
|
||||
| Transaction History | `https://bookstore.loyalty.lu/account/loyalty/history` |
|
||||
| Self-Enrollment | `https://bookstore.loyalty.lu/loyalty/join` |
|
||||
| Enrollment Success | `https://bookstore.loyalty.lu/loyalty/join/success` |
|
||||
| Loyalty Dashboard | `https://bookstore.rewardflow.lu/account/loyalty` |
|
||||
| Transaction History | `https://bookstore.rewardflow.lu/account/loyalty/history` |
|
||||
| Self-Enrollment | `https://bookstore.rewardflow.lu/loyalty/join` |
|
||||
| Enrollment Success | `https://bookstore.rewardflow.lu/loyalty/join/success` |
|
||||
|
||||
**Storefront API:**
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET card | `https://bookstore.loyalty.lu/api/storefront/loyalty/card` |
|
||||
| GET transactions | `https://bookstore.loyalty.lu/api/storefront/loyalty/transactions` |
|
||||
| POST enroll | `https://bookstore.loyalty.lu/api/storefront/loyalty/enroll` |
|
||||
| GET program | `https://bookstore.loyalty.lu/api/storefront/loyalty/program` |
|
||||
| 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` |
|
||||
|
||||
**Store backend (staff/owner):**
|
||||
|
||||
| Page | Production URL |
|
||||
|------|----------------|
|
||||
| Store Login | `https://bookstore.loyalty.lu/store/BOOKSTORE/login` |
|
||||
| Terminal | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/terminal` |
|
||||
| Cards | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/cards` |
|
||||
| Settings | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/settings` |
|
||||
| Stats | `https://bookstore.loyalty.lu/store/BOOKSTORE/loyalty/stats` |
|
||||
| Store Login | `https://bookstore.rewardflow.lu/store/BOOKSTORE/login` |
|
||||
| Terminal | `https://bookstore.rewardflow.lu/store/BOOKSTORE/loyalty/terminal` |
|
||||
| Cards | `https://bookstore.rewardflow.lu/store/BOOKSTORE/loyalty/cards` |
|
||||
| Settings | `https://bookstore.rewardflow.lu/store/BOOKSTORE/loyalty/settings` |
|
||||
| Stats | `https://bookstore.rewardflow.lu/store/BOOKSTORE/loyalty/stats` |
|
||||
|
||||
**Store API:**
|
||||
|
||||
| Method | Production URL |
|
||||
|--------|----------------|
|
||||
| GET program | `https://bookstore.loyalty.lu/api/store/loyalty/program` |
|
||||
| POST stamp | `https://bookstore.loyalty.lu/api/store/loyalty/stamp` |
|
||||
| POST points | `https://bookstore.loyalty.lu/api/store/loyalty/points` |
|
||||
| POST enroll | `https://bookstore.loyalty.lu/api/store/loyalty/cards/enroll` |
|
||||
| POST lookup | `https://bookstore.loyalty.lu/api/store/loyalty/cards/lookup` |
|
||||
| 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` |
|
||||
|
||||
### Platform Admin & Public API (always on platform domain)
|
||||
|
||||
| Page / Endpoint | Production URL |
|
||||
|-----------------|----------------|
|
||||
| Admin Programs | `https://loyalty.lu/admin/loyalty/programs` |
|
||||
| Admin Analytics | `https://loyalty.lu/admin/loyalty/analytics` |
|
||||
| Admin Merchant Detail | `https://loyalty.lu/admin/loyalty/merchants/{id}` |
|
||||
| Admin Merchant Settings | `https://loyalty.lu/admin/loyalty/merchants/{id}/settings` |
|
||||
| Admin API - Programs | `GET https://loyalty.lu/api/admin/loyalty/programs` |
|
||||
| Admin API - Stats | `GET https://loyalty.lu/api/admin/loyalty/stats` |
|
||||
| Public API - Program | `GET https://loyalty.lu/api/loyalty/programs/ORION` |
|
||||
| Apple Wallet Pass | `GET https://loyalty.lu/api/loyalty/passes/apple/{serial}.pkpass` |
|
||||
| Admin Programs | `https://rewardflow.lu/admin/loyalty/programs` |
|
||||
| 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` |
|
||||
|
||||
### Domain configuration per store (current DB state)
|
||||
|
||||
@@ -364,11 +364,11 @@ The store has no entry in `store_domains` and the merchant has no registered dom
|
||||
|-------|----------|---------------------|------------------|
|
||||
| ORION | WizaCorp | `orion.shop` | `orion.shop` (store override) |
|
||||
| FASHIONHUB | Fashion Group | `fashionhub.store` | `fashionhub.store` (store override) |
|
||||
| WIZAGADGETS | WizaCorp | _(none)_ | `wizagadgets.loyalty.lu` (subdomain fallback) |
|
||||
| WIZAHOME | WizaCorp | _(none)_ | `wizahome.loyalty.lu` (subdomain fallback) |
|
||||
| FASHIONOUTLET | Fashion Group | _(none)_ | `fashionoutlet.loyalty.lu` (subdomain fallback) |
|
||||
| BOOKSTORE | BookWorld | _(none)_ | `bookstore.loyalty.lu` (subdomain fallback) |
|
||||
| BOOKDIGITAL | BookWorld | _(none)_ | `bookdigital.loyalty.lu` (subdomain fallback) |
|
||||
| WIZAGADGETS | WizaCorp | _(none)_ | `wizagadgets.rewardflow.lu` (subdomain fallback) |
|
||||
| WIZAHOME | WizaCorp | _(none)_ | `wizahome.rewardflow.lu` (subdomain fallback) |
|
||||
| FASHIONOUTLET | Fashion Group | _(none)_ | `fashionoutlet.rewardflow.lu` (subdomain fallback) |
|
||||
| BOOKSTORE | BookWorld | _(none)_ | `bookstore.rewardflow.lu` (subdomain fallback) |
|
||||
| BOOKDIGITAL | BookWorld | _(none)_ | `bookdigital.rewardflow.lu` (subdomain fallback) |
|
||||
|
||||
!!! example "After merchant domain registration"
|
||||
If WizaCorp registers `myloyaltyprogram.lu` as their merchant domain, the table becomes:
|
||||
@@ -384,7 +384,7 @@ The store has no entry in `store_domains` and the merchant has no registered dom
|
||||
|
||||
1. **Store custom domain**: `orion.shop` (from `store_domains` table) — highest priority
|
||||
2. **Merchant domain**: `myloyaltyprogram.lu` (from `merchant_domains` table) — inherited default
|
||||
3. **Subdomain fallback**: `orion.loyalty.lu` (from `Store.subdomain` + platform domain)
|
||||
3. **Subdomain fallback**: `orion.rewardflow.lu` (from `Store.subdomain` + platform domain)
|
||||
|
||||
---
|
||||
|
||||
@@ -420,7 +420,7 @@ flowchart TD
|
||||
1. Login as `john.owner@wizacorp.com` and navigate to billing:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/billing`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/billing`
|
||||
- Prod (subdomain): `https://orion.loyalty.lu/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 Prod: `GET https://{store_domain}/api/v1/store/billing/tiers`
|
||||
@@ -441,19 +441,19 @@ flowchart TD
|
||||
|
||||
1. Platform admin registers a merchant domain:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/admin/merchants/{merchant_id}/domains`
|
||||
- API Prod: `POST https://loyalty.lu/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 Prod: `GET https://loyalty.lu/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 Prod: `POST https://loyalty.lu/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 Prod: `PUT https://loyalty.lu/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
|
||||
|
||||
@@ -463,7 +463,7 @@ If a store needs its own domain (e.g., ORION is a major brand and wants `mysuper
|
||||
|
||||
1. Platform admin registers a store domain:
|
||||
- API Dev: `POST http://localhost:9999/platforms/loyalty/api/v1/admin/stores/{store_id}/domains`
|
||||
- API Prod: `POST https://loyalty.lu/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
|
||||
3. Once active, this store's effective domain becomes `mysuperloyaltyprogram.lu` (overrides merchant domain)
|
||||
@@ -509,11 +509,11 @@ flowchart TD
|
||||
1. Login as `john.owner@wizacorp.com` at:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/ORION/login`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/login`
|
||||
- Prod (subdomain): `https://orion.loyalty.lu/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`
|
||||
- Prod (custom domain): `https://orion.shop/store/ORION/loyalty/settings`
|
||||
- Prod (subdomain): `https://orion.loyalty.lu/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`
|
||||
@@ -526,7 +526,7 @@ flowchart TD
|
||||
- Prod: `POST https://{store_domain}/api/store/loyalty/pins`
|
||||
9. Verify program is live - check from another store (same merchant):
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/store/WIZAGADGETS/loyalty/settings`
|
||||
- Prod (subdomain): `https://wizagadgets.loyalty.lu/store/WIZAGADGETS/loyalty/settings`
|
||||
- Prod (subdomain): `https://wizagadgets.rewardflow.lu/store/WIZAGADGETS/loyalty/settings`
|
||||
|
||||
**Expected blockers in current state:**
|
||||
|
||||
@@ -649,19 +649,19 @@ flowchart TD
|
||||
1. Visit the public enrollment page:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/loyalty/join`
|
||||
- Prod (custom domain): `https://orion.shop/loyalty/join`
|
||||
- Prod (subdomain): `https://bookstore.loyalty.lu/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.loyalty.lu/api/storefront/loyalty/enroll`
|
||||
- Prod (subdomain): `POST https://bookstore.rewardflow.lu/api/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`
|
||||
- Prod (subdomain): `https://bookstore.loyalty.lu/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://loyalty.lu/api/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
- Prod: `GET https://rewardflow.lu/api/loyalty/passes/apple/{serial_number}.pkpass`
|
||||
|
||||
---
|
||||
|
||||
@@ -676,13 +676,13 @@ flowchart TD
|
||||
2. View loyalty dashboard (card balance, available rewards):
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty`
|
||||
- Prod (custom domain): `https://orion.shop/account/loyalty`
|
||||
- Prod (subdomain): `https://bookstore.loyalty.lu/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`
|
||||
3. View full transaction history:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/account/loyalty/history`
|
||||
- Prod (custom domain): `https://orion.shop/account/loyalty/history`
|
||||
- Prod (subdomain): `https://bookstore.loyalty.lu/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`
|
||||
|
||||
@@ -698,22 +698,22 @@ flowchart TD
|
||||
1. Login as admin
|
||||
2. View all programs:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/programs`
|
||||
- Prod: `https://loyalty.lu/admin/loyalty/programs`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/programs`
|
||||
3. View platform-wide analytics:
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/analytics`
|
||||
- Prod: `https://loyalty.lu/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`
|
||||
- Prod: `https://loyalty.lu/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`
|
||||
- Prod: `https://loyalty.lu/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://loyalty.lu/api/admin/loyalty/merchants/1/settings`
|
||||
- API Prod: `PATCH https://rewardflow.lu/api/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`
|
||||
- Prod: `https://loyalty.lu/admin/loyalty/merchants/2`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/2`
|
||||
|
||||
---
|
||||
|
||||
@@ -752,7 +752,7 @@ flowchart TD
|
||||
**Precondition:** Cross-location redemption must be enabled in merchant settings:
|
||||
|
||||
- Dev: `http://localhost:9999/platforms/loyalty/admin/loyalty/merchants/1/settings`
|
||||
- Prod: `https://loyalty.lu/admin/loyalty/merchants/1/settings`
|
||||
- Prod: `https://rewardflow.lu/admin/loyalty/merchants/1/settings`
|
||||
|
||||
**Steps:**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user