docs(onboarding): merchant intake checklist (EN + FR)
All checks were successful
CI / ruff (push) Successful in 17s
CI / pytest (push) Successful in 2h39m52s
CI / validate (push) Successful in 31s
CI / dependency-scanning (push) Successful in 35s
CI / docs (push) Successful in 52s
CI / deploy (push) Successful in 1m43s

Practical field-by-field intake doc for the first merchants — maps
each question to its DB column so the call → admin UI is 1:1.
Includes a section on marketing-material reuse aimed at franchisees,
with written-authorization clauses for the future marketing module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 23:08:35 +02:00
parent a3fb7029bd
commit 29b2170448
3 changed files with 665 additions and 0 deletions

View File

@@ -0,0 +1,340 @@
# Fiche d'intégration commerçant — Premier point de vente
Liste pratique de toutes les données à collecter auprès d'un nouveau
commerçant avant de mettre en service son premier point de vente sur la
plateforme de fidélité. Chaque champ est rattaché à la colonne de base
de données correspondante, de sorte que ce qui est collecté lors de la
réunion d'intégration se reporte 1:1 dans l'interface d'administration.
À utiliser comme formulaire à partager, ou comme trame de discussion
pour le premier appel d'intégration.
## Comment utiliser ce document
1. Envoyer au commerçant les sections **« Ce que vous devez nous
fournir »** (17) avant la réunion de lancement, pour qu'il
prépare les logos, numéros de TVA et informations du personnel.
2. Utiliser les sections **décisions métier** (3, 5) pour structurer
l'appel — ce sont les choix que seul le commerçant peut faire.
3. Après l'appel, remplir l'interface d'administration dans l'ordre
ci-dessous ; le modèle impose cette chaîne de dépendances
(commerçant → point de vente → programme de fidélité → personnel).
## TL;DR — ce sans quoi vous ne pouvez absolument pas lancer
- Raison sociale + numéro de TVA + adresse de l'entreprise
- E-mail du propriétaire (identifiant et destinataire de l'invitation)
- Nom du point de vente + code de point de vente (slug URL)
- Type de programme de fidélité (tampons / points / hybride) et
structure de récompense
- URL de la politique de confidentialité (RGPD)
- Logo de la marque (PNG carré, fond transparent)
- Couleur principale de la marque (hex)
- Au moins un code PIN du personnel
Tout le reste peut être complété de manière itérative après le
lancement.
---
## 1. Identité de l'entreprise (entité **commerçant**)
Un par commerçant — alimente la table `merchants`.
| Champ | Requis ? | Colonne BDD | Notes |
|---|---|---|---|
| Raison sociale | ✅ | `name` | Apparaît sur les factures et pieds d'e-mails |
| Nom commercial (si différent) | optionnel | (utiliser `name`) | Le nom que voient les clients |
| Description de l'activité | recommandé | `description` | Slogan d'une ligne |
| Numéro de TVA | ✅ UE | `tax_number` | Format LU : `LU12345678` |
| Numéro d'immatriculation | ✅ | `tax_number` (ou champ séparé si disponible) | LU : `RCS Lxxxxxx` |
| Adresse de l'entreprise | ✅ | `business_address` | Rue + ville + code postal + pays |
| E-mail de contact principal | ✅ | `contact_email` | Pour factures et alertes plateforme |
| Téléphone principal | recommandé | `contact_phone` | Pour rappels du support |
| URL du site web | optionnel | `website` | Lié depuis le pied de page du storefront |
## 2. Compte propriétaire / administrateur
La personne qui se connecte en tant que propriétaire du commerçant.
| Champ | Requis ? | Notes |
|---|---|---|
| Nom complet | ✅ | Affiché dans les journaux d'audit |
| E-mail (identifiant) | ✅ | L'invitation y est envoyée — doit être livrable |
| Téléphone | recommandé | |
| Langue d'interface préférée (en / fr / de / lb) | ✅ | Pilote la langue du tableau de bord d'admin |
## 3. Chaque point de vente
À répéter par emplacement. Alimente la table `stores`. La plateforme
supporte le multi-emplacement dès le premier jour — même les
commerçants mono-magasin vivent dans cette structure.
| Champ | Requis ? | Colonne BDD | Notes |
|---|---|---|---|
| Nom du point de vente | ✅ | `name` | Ex. : « Fashion Hub City Concorde » |
| Code interne du point de vente | ✅ | `store_code` | Slug en MAJUSCULES, sans espaces — utilisé dans les URLs (`/store/FASHIONHUB`). À choisir une fois, difficile à modifier |
| Sous-domaine | optionnel | `subdomain` | Ex. : `fashionhub.rewardflow.lu` pour le storefront |
| Adresse physique | ✅ | `business_address` | Par emplacement, remplace l'adresse du commerçant si différente |
| E-mail de contact local | optionnel | `contact_email` | Remplace l'e-mail commerçant si le point de vente a le sien |
| Téléphone local | optionnel | `contact_phone` | |
| Taux de TVA par défaut % | optionnel | `letzshop_default_tax_rate` | Par défaut 17 % (TVA standard LU) |
| Langue d'interface par défaut | ✅ | `default_language` / `dashboard_language` | Langue d'ouverture du terminal du personnel |
| Langues client | ✅ | `storefront_languages` | Sous-ensemble de `{en, fr, de, lb}` pour LU |
| Horaires d'ouverture | optionnel | pas encore dans le schéma | Contexte utile pour le support |
## 4. Personnel (par point de vente, pour chaque caissier)
Alimente `users` + `store_users` (flux d'invitation).
| Champ | Requis ? | Notes |
|---|---|---|
| Nom complet | ✅ | Affiché sur l'écran PIN de la tablette |
| E-mail | ✅ si accès web nécessaire | Les opérateurs uniquement-PIN-tablette n'ont pas besoin de compte |
| Code PIN à 4 chiffres | ✅ | Ils le choisissent ; vous le configurez dans `/admin/loyalty/pins` ; saisi à chaque transaction sur la tablette |
| Identifiant / numéro d'employé | optionnel | Affiché à côté du nom dans la piste d'audit |
| Rôle | ✅ | Un parmi : `merchant_owner`, `store_admin`, `store_staff` |
## 5. Programme de fidélité — les **décisions métier**
Un programme par commerçant. Alimente la table `loyalty_programs`.
C'est ici que se déroule l'essentiel de la discussion de lancement —
aidez-les à choisir.
### 5.1 Type de programme
| Type | Quand le recommander |
|---|---|
| **Tampons** | Modèle « achetez-en 10, le suivant est offert ». Cafés, coiffeurs, restaurants, partout où il existe une unité de vente claire |
| **Points** | « Gagnez X points par €, échangez-les contre des récompenses ». Commerce avec paniers variés |
| **Hybride** | Les deux à la fois. Rare en v1 — à différer |
### 5.2 Si tampons
| Champ | Valeur typique | Notes |
|---|---|---|
| `stamps_target` | 10 | Tampons pour remplir une carte |
| `stamps_reward_description` | « Café offert » | Nom de la récompense côté client |
| `stamps_reward_value_cents` | 500 (= 5 €) | Valeur interne pour l'analytique |
### 5.3 Si points
| Champ | Valeur typique | Notes |
|---|---|---|
| `points_per_euro` | 1, 5 ou 10 | Taux d'acquisition |
| `welcome_bonus_points` | 50100 | Attribués à l'inscription, donnent une bonne première impression |
| `minimum_purchase_cents` | 500 (= 5 €) | Montant minimum de vente pour gagner — élimine les achats de chewing-gums |
| `minimum_redemption_points` | 100 | Points minimum pour échanger quoi que ce soit |
| `points_expiration_days` | 365 | Au-delà, les points non utilisés expirent |
| `points_rewards` (liste JSON) | voir ci-dessous | Le menu des récompenses |
Chaque entrée de `points_rewards` doit contenir : `name`,
`points_required`, `description`, `value`. Exemple :
```json
[
{"name": "Bon de 5 €", "points_required": 100, "description": "À utiliser en caisse", "value": 500},
{"name": "Bon de 15 €", "points_required": 250, "description": "À utiliser en caisse", "value": 1500},
{"name": "Produit XYZ offert", "points_required": 400, "description": "Retrait en magasin", "value": null}
]
```
### 5.4 Garde-fous anti-fraude
| Champ | Défaut | Quand modifier |
|---|---|---|
| `cooldown_minutes` | 0 | Mettre 3060 pour les cafés à fort volume, pour limiter les abus du personnel |
| `max_daily_stamps` | 10 | Réduire si volume faible ; plafonne les tampons quotidiens d'une carte |
| `require_staff_pin` | true | Recommandé `true` dès qu'il y a >1 employé ; la tablette gère hors ligne |
### 5.5 Comportement multi-emplacement
Ces paramètres sont sur les réglages commerçant
(`merchant_loyalty_settings`), pas sur le programme :
| Paramètre | Défaut | Notes |
|---|---|---|
| `allow_cross_location_redemption` | ✅ activé | Gagner en A et échanger en B ? Oui pour les chaînes, non pour des indépendants partageant l'infrastructure |
| `allow_void_transactions` | ✅ activé | Certains commerçants désactivent pour contrôler la fraude |
| `require_order_reference` | désactivé | Utile si intégration avec leur caisse — relie chaque transaction de fidélité à un numéro de ticket |
## 6. Éléments de marque
À collecter sous forme de **fichiers**, pas de liens — nous les
hébergeons.
| Élément | Format | Taille | Champ BDD |
|---|---|---|---|
| Logo principal | PNG, fond transparent | 600×600 min, carré | `logo_url` |
| Bannière hero | JPG / PNG | 1200×400 | `hero_image_url` |
| Couleur principale | Hex | ex. `#7C3AED` | `card_color` |
| Couleur secondaire | Hex | ex. `#10B981` | `card_secondary_color` |
| Nom affiché de la carte | Chaîne courte | ex. « Fashion Hub Rewards » | `card_name` |
Le logo sert aussi de **logo Google Wallet**, donc 600×600 minimum
avec fond transparent est le plancher pratique — Google exige une
bonne lisibilité.
## 7. Conformité & RGPD
Requis pour le go-live, en particulier au Luxembourg.
| Champ | Requis ? | Champ BDD | Notes |
|---|---|---|---|
| URL de la politique de confidentialité | ✅ UE | `privacy_url` | Lié depuis le formulaire d'inscription. Leur site ou votre CMS |
| CGU de fidélité | ✅ | `terms_text` ou `terms_cms_page_slug` | Texte en ligne ou slug de page CMS. À couvrir : fonctionnement des points, expiration, conditions de révocation, contact en cas de litige |
| Contact DPO (si applicable) | dépend de la taille | pas dans le schéma | Règle LU : >5 000 enregistrements clients → DPO désigné requis |
| Consentement marketing | implicite | (case à cocher sur le formulaire d'inscription) | Obligatoire si envoi d'e-mails anniversaire / réengagement |
## 8. Tablettes (si vous fournissez le matériel POS)
| Question | Pourquoi |
|---|---|
| Combien de tablettes par point de vente ? | Détermine le nombre d'appairages à configurer |
| Tablettes Android existantes ? Modèle + OS ? | Nécessite Android 8.0+ (API 26), Play Services. Fire OS ne fonctionnera pas |
| Sourcer les tablettes pour eux ? | Lenovo Tab M11 ou Samsung Galaxy Tab A9 (100200 € l'unité) — modèles éprouvés |
| Montage ? Support comptoir ou mural ? | Influe sur le plan d'alimentation/recharge |
| Wi-Fi à chaque emplacement | La file d'attente hors ligne gère les coupures brèves, pas une journée entière. Confirmer le Wi-Fi au comptoir |
## 9. Migration de données initiales (optionnel mais utile)
| Question | Pourquoi |
|---|---|
| Liste clients fidélité existante ? | S'ils passent d'un autre système ou de cartes papier, import en masse via CSV (e-mail, nom, solde, date d'inscription) |
| Nombre approximatif de clients initiaux ? | Dimensionne la discussion de capacité |
| Volume mensuel actuel de transactions | Idem |
## 10. Réutilisation de matériel marketing (pour le futur module marketing)
Beaucoup de nos premiers commerçants sont franchisés (chaînes de mode,
restauration, beauté) et reçoivent en continu du matériel de campagne
du franchiseur — bannières, modèles d'e-mails, publications sociales,
promotions saisonnières. Le futur module marketing vise à
**republier** ce matériel au niveau du point de vente local : un
franchisé ne devrait pas avoir à concevoir lui-même un e-mail
anniversaire alors que le siège en produit cinq par an.
À l'intégration, posez les questions sous forme d'autorisation pour
avoir le feu vert *avant* de construire la chaîne d'ingestion. On ne
leur demande rien aujourd'hui — on inventorie les sources et on
obtient une autorisation écrite.
### 10.1 Matériel marketing existant — inventaire
| Question | Pourquoi |
|---|---|
| Le commerçant est-il franchisé ? De quelle enseigne ? | Détermine les sources institutionnelles potentielles |
| Reçoit-il une **newsletter régulière** du franchiseur ? Nous transférer 35 récentes | Permet d'évaluer le type de contenu, la fréquence, le mix linguistique et le formatage |
| Existe-t-il un **extranet / portail franchiseur** où sont publiées les campagnes ? URL + identifiants en lecture seule | Source idéale : structurée, datée, multilingue |
| **Comptes sociaux** sur lesquels le franchiseur publie (Instagram, Facebook, LinkedIn) | Public, scrapable, mais ratio signal/bruit faible |
| **Groupes WhatsApp / Telegram** de diffusion auxquels ils appartiennent | Parfois le seul canal — transfert manuel peut être l'unique voie |
| Packs **PDF / images** reçus par e-mail ou lien de téléchargement | Fréquent pour les campagnes saisonnières ; demander les derniers |
| Fréquence : hebdomadaire / mensuelle / par campagne | Dimensionne le volume d'ingestion |
| Langues de publication du franchiseur | Détermine si on réutilise tel quel ou si on retraduit |
### 10.2 Autorisation de réutilisation — à obtenir par écrit
Avant tout scraping ou republication, le commerçant doit nous
autoriser. À recueillir à l'intégration (case à cocher sur le
formulaire ou clause dans le contrat commerçant) :
-**« J'autorise {plateforme} à ingérer le matériel marketing que je
transfère, partage ou rends accessible, et à le republier en mon nom
via les canaux de fidélité/marketing offerts par la plateforme. »**
-**« Je confirme avoir le droit (en tant que franchisé) d'utiliser
ce matériel en marketing local, et que les chartes de marque ou
conditions de licence du franchiseur autorisent la republication via
des plateformes tierces. »**
- ✅ Nous transférer les **chartes de marque / politique de marketing
collaboratif** du franchiseur si elle existe. Cela détermine ce qui
peut être fait en toute sécurité.
### 10.3 Voies pratiques de capture (du plus simple au plus complexe)
Pour le futur module marketing, par ordre de facilité de mise en
œuvre :
1. **Transfert vers une boîte dédiée.** Donner au commerçant un alias
par point de vente, ex. `marketing+FASHIONHUB@rewardflow.lu`. Il
transfère tout e-mail du franchiseur là-bas. On parse et on
catégorise.
2. **Téléversement glisser-déposer.** Une page où le commerçant colle
une URL ou téléverse un PDF/image ; on OCR/parse et on met en file
pour validation.
3. **Récupération authentifiée sur extranet.** Ils nous fournissent
des identifiants en lecture seule au portail franchiseur ; on
sonde régulièrement. Signal le plus élevé, exigence légale la plus
haute — nécessite l'aval explicite du franchiseur.
4. **Scraping social public.** En dernier recours, uniquement pour le
matériel publié ouvertement par le franchiseur. Respecter
`robots.txt` et les CGU des plateformes.
### 10.4 Canaux de republication sur lesquels brancher
Ce qu'on **fait** de ce matériel une fois capté :
- Rotation de la **bannière storefront** (déjà dans le CMS)
- E-mails **fidélité bienvenue / réengagement / anniversaire** (module
loyalty v1 — modèles fixes ; module marketing v2 — pilotés par
campagne)
- **SMS / WhatsApp** (module marketing v2, différé)
- **Notifications push** via la carte Wallet (Google Wallet prend en
charge les mises à jour au niveau de la classe — push à chaque
téléphone client)
### 10.5 Signaux d'alerte à remonter au commerçant
Si l'un de ces points apparaît à l'intégration, signaler pour revue
juridique **avant** de toucher au matériel :
- Présence de marques du franchiseur sans licence explicite du
franchisé pour cet usage
- Références à des marques tierces (co-promotions) — ces marques n'ont
pas consenti à notre plateforme
- Matériel dans une langue que la clientèle du commerçant ne parle pas
— on ne traduit pas automatiquement sans autorisation
- Données personnelles de personnes nommées (témoignages, photos) —
la chaîne de consentement RGPD ne s'étend peut-être pas à nous
## 11. Client de test pour vérification
Avant d'ouvrir aux vrais clients, dérouler les 8 tests utilisateur de
bout en bout avec deux comptes de test :
- L'e-mail personnel du propriétaire commerçant
- Un membre de l'équipe ou votre propre e-mail
La liste de tests E2E se trouve dans
[`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md).
---
## Ordre de saisie suggéré dans l'interface d'administration
Le modèle a des dépendances de clés étrangères strictes, donc à
remplir dans cet ordre :
1. **Créer le commerçant** (`/admin/merchants/new`) — nécessite § 1.
2. **Inviter le propriétaire** — nécessite § 2.
3. **Créer le(s) point(s) de vente** (`/admin/stores/new`) —
nécessite § 3.
4. **Créer le programme de fidélité**
(`/admin/loyalty/programs/new`) — nécessite §§ 5, 6, 7.
5. **Créer les codes PIN du personnel**
(`/admin/loyalty/pins/new` par point de vente) — nécessite § 4.
6. **Configurer les paramètres multi-emplacement**
(`/admin/loyalty/settings/{merchant}`) — nécessite § 5.5.
7. **(Optionnel) importer les clients en masse** via CSV —
nécessite § 9.
8. **Appairer la/les tablette(s)** à chaque point de vente
(`/admin/loyalty/terminals/new`, QR ou manuel) — nécessite § 8.
---
## Référence
- Modèles de base de données : `models/database/{merchants,stores,loyalty}.py`
- Plan de lancement production fidélité : [`app/modules/loyalty/docs/production-launch-plan.md`](../modules/loyalty/production-launch-plan.md)
- Synthèse de prêt-au-lancement : [`loyalty-go-live-readiness.md`](loyalty-go-live-readiness.md)
- Tests E2E utilisateur : [`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md)
- Version anglaise : [`merchant-intake-checklist.md`](merchant-intake-checklist.md)

View File

@@ -0,0 +1,323 @@
# Merchant Intake Checklist — First Store Onboarding
A practical list of every datum to collect from a new merchant before
flipping their first store live on the loyalty platform. Each field is
mapped to the actual database column behind it, so what you collect in
the intake meeting maps 1:1 to the admin UI you'll fill in afterwards.
Use this as a sharable form, or as a script for the first onboarding
call.
## How to use this doc
1. Send the merchant the **"What you need to send us"** sections (17)
ahead of the kickoff call, so they have logos / VAT numbers / staff
info ready.
2. Use the **business decisions** sections (3, 5) to drive the actual
conversation — these are the choices only they can make.
3. After the call, populate the admin UI in the order below; the model
enforces this dependency chain (merchant → store → loyalty program →
staff).
## TL;DR — what you absolutely cannot launch without
- Legal business name + VAT number + business address
- Owner email (login + invitation target)
- Store name + store code (URL slug)
- Loyalty program type (stamps / points / hybrid) + reward structure
- Privacy policy URL (GDPR)
- Brand logo (square PNG, transparent bg)
- Brand primary color (hex)
- At least one staff PIN
Everything else can be filled in iteratively post-launch.
---
## 1. Business identity (the **merchant** entity)
One per merchant — feeds the `merchants` table.
| Field | Required? | DB column | Notes |
|---|---|---|---|
| Legal business name | ✅ | `name` | Appears on invoices + email footers |
| Trading name (if different) | optional | (use `name`) | The name customers see |
| Business description | recommended | `description` | One-line tagline |
| VAT / tax number | ✅ EU | `tax_number` | LU format: `LU12345678` |
| Business registration number | ✅ | `tax_number` (or separate field if available) | LU: `RCS Lxxxxxx` |
| Business address | ✅ | `business_address` | Street + city + ZIP + country |
| Primary contact email | ✅ | `contact_email` | Where invoices + platform alerts go |
| Primary contact phone | recommended | `contact_phone` | For support callbacks |
| Website URL | optional | `website` | Linked from storefront footer |
## 2. Owner / admin account
The human who logs in as merchant owner.
| Field | Required? | Notes |
|---|---|---|
| Full name | ✅ | Shown on audit logs |
| Email (login) | ✅ | Invitation goes here — must be deliverable |
| Phone | recommended | |
| Preferred UI language (en / fr / de / lb) | ✅ | Drives the admin dashboard's language |
## 3. Each store / point of sale
Repeat per location. Feeds the `stores` table. The platform supports
multi-location from day one — even single-store merchants live in this
shape.
| Field | Required? | DB column | Notes |
|---|---|---|---|
| Store name | ✅ | `name` | E.g. "Fashion Hub City Concorde" |
| Internal store code | ✅ | `store_code` | UPPERCASE slug, no spaces — used in URLs (`/store/FASHIONHUB`). Pick once, hard to change |
| Subdomain | optional | `subdomain` | E.g. `fashionhub.rewardflow.lu` for the storefront |
| Physical address | ✅ | `business_address` | Per-location, overrides merchant address if different |
| Local contact email | optional | `contact_email` | Overrides merchant-level if store has its own |
| Local phone | optional | `contact_phone` | |
| Default tax rate % | optional | `letzshop_default_tax_rate` | Defaults to 17% (LU standard VAT) |
| Default UI language | ✅ | `default_language` / `dashboard_language` | What the staff terminal opens in |
| Customer-facing languages | ✅ | `storefront_languages` | Subset of `{en, fr, de, lb}` for LU |
| Opening hours | optional | not in schema yet | Useful context for support |
## 4. Staff (per store, for each cashier)
Feeds `users` + `store_users` (invitation flow).
| Field | Required? | Notes |
|---|---|---|
| Full name | ✅ | Shown on the tablet PIN screen |
| Email | ✅ if web access needed | Owner-only PIN-on-tablet operators don't need an account |
| 4-digit PIN | ✅ | They pick it; you set it on `/admin/loyalty/pins`; staff types it on the tablet for every transaction |
| Employee ID / staff number | optional | Shown alongside name in audit trail |
| Role | ✅ | One of: `merchant_owner`, `store_admin`, `store_staff` |
## 5. Loyalty program — the **business decisions**
One program per merchant. Feeds the `loyalty_programs` table. This is
where most of the kickoff conversation happens — help them choose.
### 5.1 Program type
| Type | When to recommend |
|---|---|
| **Stamps** | "Buy 10, get 1 free" model. Cafés, hairdressers, lunch spots, anywhere with a clear unit of sale |
| **Points** | "Earn X points per €, redeem on rewards menu". Retail with varied basket sizes |
| **Hybrid** | Both at once. Rare for v1 — defer |
### 5.2 If stamps
| Field | Typical value | Notes |
|---|---|---|
| `stamps_target` | 10 | Stamps to fill a card |
| `stamps_reward_description` | "Free coffee" | Customer-facing reward name |
| `stamps_reward_value_cents` | 500 (= €5) | Internal value for analytics |
### 5.3 If points
| Field | Typical value | Notes |
|---|---|---|
| `points_per_euro` | 1, 5, or 10 | The earn rate |
| `welcome_bonus_points` | 50100 | Awarded on enrollment, makes new customers feel rewarded |
| `minimum_purchase_cents` | 500 (= €5) | Minimum sale to earn anything — kills chewing-gum farming |
| `minimum_redemption_points` | 100 | Minimum points to redeem anything |
| `points_expiration_days` | 365 | After this, unredeemed points lapse |
| `points_rewards` (JSON list) | see below | The reward menu |
Each entry in `points_rewards` needs: `name`, `points_required`,
`description`, `value`. Example:
```json
[
{"name": "€5 voucher", "points_required": 100, "description": "Apply at checkout", "value": 500},
{"name": "€15 voucher", "points_required": 250, "description": "Apply at checkout", "value": 1500},
{"name": "Free product XYZ", "points_required": 400, "description": "Pickup in store", "value": null}
]
```
### 5.4 Anti-fraud knobs
| Field | Default | When to change |
|---|---|---|
| `cooldown_minutes` | 0 | Set 3060 for high-volume cafés to stop staff abuse |
| `max_daily_stamps` | 10 | Lower if low-volume; this caps a single card's daily stamps |
| `require_staff_pin` | true | Recommended `true` for >1 staff member; tablet handles offline |
### 5.5 Cross-location behaviour
This is on the merchant settings (`merchant_loyalty_settings`), not the
program:
| Setting | Default | Notes |
|---|---|---|
| `allow_cross_location_redemption` | ✅ on | Earn at Store A, redeem at Store B? Yes for chains, no for indies sharing infrastructure |
| `allow_void_transactions` | ✅ on | Some merchants disable for fraud control |
| `require_order_reference` | off | Useful if integrating with their POS/till — links each loyalty transaction to a sale receipt |
## 6. Branding assets
Collect as **files**, not links — we host them.
| Asset | Format | Size | DB field |
|---|---|---|---|
| Primary logo | PNG, transparent bg | 600×600 min, square | `logo_url` |
| Hero banner | JPG / PNG | 1200×400 | `hero_image_url` |
| Brand primary color | Hex | e.g. `#7C3AED` | `card_color` |
| Brand secondary color | Hex | e.g. `#10B981` | `card_secondary_color` |
| Card display name | Short string | e.g. "Fashion Hub Rewards" | `card_name` |
The logo doubles as the **Google Wallet pass logo**, so 600×600 minimum
with transparent background is the practical floor — Google enforces
clarity.
## 7. Compliance & GDPR
Required for go-live, especially in Luxembourg.
| Field | Required? | DB field | Notes |
|---|---|---|---|
| Privacy policy URL | ✅ EU | `privacy_url` | Linked from enrollment form. Their site or your CMS |
| Loyalty T&C | ✅ | `terms_text` or `terms_cms_page_slug` | Inline text or CMS page slug. Cover: how points work, expiry, when rewards can be revoked, contact for issues |
| DPO contact (if applicable) | depends on size | not in schema | LU rule: >5k customer records → designated DPO |
| Marketing consent | implicit | (enrollment form has the checkbox) | Required if you'll send birthday / re-engagement emails |
## 8. Tablets (if you provide the POS hardware)
| Question | Why |
|---|---|
| How many tablets per store? | Determines how many device pairings to set up |
| Existing Android tablets? Model + OS? | Need Android 8.0+ (API 26), Play Services. Fire OS won't work |
| Source tablets for them? | Lenovo Tab M11 or Samsung Galaxy Tab A9 (€100200 each) — known good |
| Mounting? Counter stand or wall? | Affects power/charging plan |
| Wifi at each location | Offline queue handles brief outages, not all-day. Confirm wifi at the counter |
## 9. Initial data migration (nice-to-have)
| Question | Why |
|---|---|
| Existing loyalty customer list? | If they're switching from another system or paper cards, bulk-import via CSV (email, name, balance, enrollment date) |
| Approximate initial customer count? | Sizes capacity discussion |
| Current monthly transaction volume | Same |
## 10. Marketing material reuse (for the future marketing module)
Many of our early merchants are franchisees (e.g. fashion, food, beauty
chains) and receive a steady stream of campaign material from their
franchisor — banners, email templates, social posts, seasonal
promotions. The future marketing module aims to **republish** this
material at the local-store level: a franchisee shouldn't have to design
their own birthday email when corporate already produces five a year.
At intake, ask permission-style questions so we have a green light
*before* we build the ingestion pipeline. We are not asking them to do
anything today — we're inventorying sources and getting written
authorization.
### 10.1 Existing marketing material — what to inventory
| Question | Why we ask |
|---|---|
| Is the merchant a franchisee? Of which brand? | Determines which corporate sources may exist |
| Do they receive a regular **email newsletter** from the franchisor? Forward us 35 recent ones | Lets us assess content type, frequency, language mix, formatting |
| Is there a **franchisor extranet / portal** where campaigns are published? URL + their login (read-only) | The ideal source: structured, dated, multilingual |
| **Social handles** the franchisor posts under (Instagram, Facebook, LinkedIn) | Public, scrapeable, but low signal-to-noise |
| **WhatsApp / Telegram broadcast groups** they're part of | Sometimes the only channel — manual forwarding may be the only path |
| **PDF / image packs** received by email or download link | Common for seasonal campaigns; ask them to share the latest |
| Frequency: weekly / monthly / per-campaign | Sizes ingestion volume |
| Languages the franchisor publishes in | Influences whether we re-use or re-translate |
### 10.2 Reuse authorization — get this in writing
Before any scraping or republishing happens, the merchant must
authorize us. Capture in the intake (a checkbox on the onboarding form
or a clause in the merchant agreement):
-**"I authorize {platform} to ingest marketing material I forward,
share, or grant access to, and to republish it on my behalf via the
loyalty/marketing channels offered by the platform."**
-**"I confirm I have the right (as franchisee) to use this material
in local marketing, and that the franchisor's brand guidelines or
licensing terms allow republication via third-party platforms."**
- ✅ Forward us the **franchisor's brand guidelines / co-op marketing
policy** if one exists. This determines what we can and can't do
safely.
### 10.3 Practical capture paths (least-effort first)
For the future marketing module, in order of how easy they are to
implement:
1. **Forward-to-inbox.** Give the merchant a per-store email alias like
`marketing+FASHIONHUB@rewardflow.lu`. They forward any
franchisor email there. We parse + categorize.
2. **Drag-and-drop upload.** A page where the merchant pastes a URL or
uploads a PDF/image; we OCR/parse + queue for review.
3. **Authenticated extranet pull.** They give us read-only credentials
to the franchisor portal; we poll. Highest signal, highest legal
bar — needs explicit franchisor clearance.
4. **Public social scrape.** Last resort, only for material the
franchisor publishes openly. Respect robots.txt and platform ToS.
### 10.4 Republish channels we can plug into
What we *do* with this material once captured:
- **Storefront banner** rotation (already exists in CMS)
- **Loyalty welcome / re-engagement / birthday emails** (loyalty module
v1 — fixed templates; marketing module v2 — campaign-driven)
- **SMS / WhatsApp** (marketing module v2, deferred)
- **Push notifications** via the Wallet pass (Google Wallet supports
this — class-level updates push to every customer's phone)
### 10.5 Red flags to surface back to the merchant
If during intake any of these come up, flag for legal review **before**
we touch the material:
- Material contains the franchisor's trademarks in a way the franchisee
doesn't have explicit license to use
- Material references third-party brands (co-promotions with other
brands) — those brands didn't consent to our platform
- Material is in a language the merchant's customer base doesn't speak
— we don't auto-translate without permission
- Personal data of named individuals (testimonials, photos) — GDPR
consent chain may not extend to us
## 11. Test customer for verification
Before sending the merchant to real customers, run the 8 user-journey
tests with two test accounts:
- The merchant owner's personal email
- A team member or your own email
The end-to-end test list lives in
[`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md).
---
## Suggested fill-in order in the admin UI
The model has hard FK dependencies, so do it in this order:
1. **Create the merchant** (`/admin/merchants/new`) — needs § 1.
2. **Invite the owner** — needs § 2.
3. **Create the store(s)** (`/admin/stores/new`) — needs § 3.
4. **Create the loyalty program** (`/admin/loyalty/programs/new`) —
needs § 5 + § 6 + § 7.
5. **Create staff PINs** (`/admin/loyalty/pins/new` per store) — needs
§ 4.
6. **Configure cross-location settings**
(`/admin/loyalty/settings/{merchant}`) — needs § 5.5.
7. **(Optional) bulk-import customers** via CSV — needs § 9.
8. **Pair tablet(s)** at each store (`/admin/loyalty/terminals/new`,
QR or manual) — needs § 8.
---
## Reference
- Database models: `models/database/{merchants,stores,loyalty}.py`
- Loyalty production launch plan: [`app/modules/loyalty/docs/production-launch-plan.md`](../modules/loyalty/production-launch-plan.md)
- Go-live readiness snapshot: [`loyalty-go-live-readiness.md`](loyalty-go-live-readiness.md)
- User-journey E2E tests: [`app/modules/loyalty/docs/user-journeys.md`](../modules/loyalty/user-journeys.md)

View File

@@ -339,6 +339,8 @@ nav:
- Store Login Platform Detection: proposals/store-login-platform-detection.md - Store Login Platform Detection: proposals/store-login-platform-detection.md
- Test API Deps Auth Dependencies: proposals/test-api-deps-auth-dependencies.md - Test API Deps Auth Dependencies: proposals/test-api-deps-auth-dependencies.md
- Post Soft-Delete Follow-ups: proposals/post-soft-delete-followups.md - Post Soft-Delete Follow-ups: proposals/post-soft-delete-followups.md
- Merchant Intake Checklist: proposals/merchant-intake-checklist.md
- "Merchant Intake Checklist (FR)": proposals/merchant-intake-checklist-fr.md
# --- Archive --- # --- Archive ---
- Archive: - Archive: