From 29b2170448d7f460f5a489cbb880e74b6728b5e8 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Tue, 12 May 2026 23:08:35 +0200 Subject: [PATCH] docs(onboarding): merchant intake checklist (EN + FR) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../proposals/merchant-intake-checklist-fr.md | 340 ++++++++++++++++++ docs/proposals/merchant-intake-checklist.md | 323 +++++++++++++++++ mkdocs.yml | 2 + 3 files changed, 665 insertions(+) create mode 100644 docs/proposals/merchant-intake-checklist-fr.md create mode 100644 docs/proposals/merchant-intake-checklist.md diff --git a/docs/proposals/merchant-intake-checklist-fr.md b/docs/proposals/merchant-intake-checklist-fr.md new file mode 100644 index 00000000..ea047ddb --- /dev/null +++ b/docs/proposals/merchant-intake-checklist-fr.md @@ -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 »** (1–7) 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` | 50–100 | 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 30–60 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 (100–200 € 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 3–5 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) diff --git a/docs/proposals/merchant-intake-checklist.md b/docs/proposals/merchant-intake-checklist.md new file mode 100644 index 00000000..1ea68ec9 --- /dev/null +++ b/docs/proposals/merchant-intake-checklist.md @@ -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 (1–7) + 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` | 50–100 | 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 30–60 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 (€100–200 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 3–5 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) diff --git a/mkdocs.yml b/mkdocs.yml index 4285c99b..47559b64 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -339,6 +339,8 @@ nav: - Store Login Platform Detection: proposals/store-login-platform-detection.md - Test API Deps Auth Dependencies: proposals/test-api-deps-auth-dependencies.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: