# 📚 Documentation API - Plateforme de Gestion de Commandes **Version:** 5.3.0 **Date:** 2026-05-15 **Base URL prod:** `https://mln-uber.club` (HTTPS via WAF nginx + ModSecurity) **Base URL dev:** `http://localhost:8080` **Technologies:** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19 + Vite, Expo 54 (React Native), TomTom API --- ## 📋 Table des MatiĂšres 1. [Vue d'ensemble](#vue-densemble) 2. [DĂ©ploiement Production](#-dĂ©ploiement-production) 3. [Authentication](#authentication) 4. [API Client (v1)](#api-client-v1) 5. [API Admin (v2)](#api-admin-v2) 6. [API Cabine (v1)](#api-cabine-v1) 7. [API Livreur (v1)](#api-livreur-v1) 8. [Notifications Push & Telegram](#-notifications-push--telegram) 9. [Paiements Crypto](#-paiements-crypto) 10. [Systeme GPS Integre](#-systeme-gps-integre) 11. [Codes d'Erreur](#codes-derreur) --- ## 🎯 Vue d'ensemble Cette API REST complĂšte gĂšre une plateforme de livraison avec assignation automatique GPS des livreurs, suivi en temps rĂ©el, et systĂšme de pĂ©nalitĂ©s avancĂ©. ### ✹ FonctionnalitĂ©s Principales - **🔐 Authentication JWT** multi-rĂŽles (Client, Admin, Livreur, Cabine) avec rĂ©vocation de token - **🚗 Auto-assignation GPS** des livreurs les plus proches (worker toutes les 1 minute) - **📍 Suivi temps rĂ©el** avec ETA et gĂ©olocalisation - **🔄 SystĂšme de queues** Redis optimisĂ© pour les livraisons - **⚡ Workers automatiques** (nettoyage, assignation, notifications) - **🎯 SystĂšme de pĂ©nalitĂ©s clients** — amendes progressives sur annulations (livreurs non concernĂ©s) - **đŸ€– Notifications Telegram** pour clients, livreurs et admins (push Expo abandonnĂ©) - **💾 Paiements crypto** via NowPayments (webhook HMAC) - **đŸ›Ąïž WAF nginx + ModSecurity** (OWASP CRS) en production - **đŸ“± Applications mobiles** Expo 54 (client + admin/livreur) - **đŸ·ïž Prix par quantitĂ© activables/dĂ©sactivables** — visibilitĂ© client filtrĂ©e automatiquement - **⏱ Bouton "Client absent"** — timer 5 min au statut `arrived`, amende automatique appliquĂ©e au **client** si absent - **đŸ“Č OTA updates** canaux nommĂ©s par rĂŽle (`pre-prod-client`, `production-client`, `pre-prod-admin`, etc.) ### đŸ—ïž Stack Technique | Composant | Technologie | |-----------|-------------| | **Backend** | Go 1.24 + Gin · port 8080 | | **Base de donnĂ©es** | PostgreSQL 16 | | **Cache / Sessions / Queues** | Redis 7 | | **Frontend web** | React 19 + TypeScript + Vite (`frontend-prep/`) | | **App mobile client** | Expo 54 + React Native (`mobile/`) | | **App mobile admin/livreur** | Expo 54 + React Native (`frontend-admin/`) | | **WAF / Reverse proxy** | Nginx + ModSecurity OWASP CRS | | **GĂ©ocodage** | Nominatim (OpenStreetMap) | | **Routing / ETA** | TomTom Routing API | | **Push notifications** | Expo Push Notifications | | **Paiements** | NowPayments (crypto) | | **Messagerie** | Telegram Bot API | --- ### đŸ—ïž Architecture Globale du Systeme ```mermaid graph TB subgraph Frontend["đŸ–„ïž Frontend (React/TypeScript)"] UI_CLIENT[Interface Client] UI_ADMIN[Interface Admin] UI_LIVREUR[Interface Livreur] UI_CABINE[Interface Cabine] TOMTOM_MAP[TomTomMap Component] end subgraph API_GATEWAY["🌐 API Gateway (Go/Gin)"] AUTH[Middleware Auth JWT] ROUTES[Router] end subgraph API_V1["đŸ“± API v1"] CLIENT_API["/api/v1/client"] LIVREUR_API["/api/v1/livreur"] CABINE_API["/api/v1/cabine"] PUBLIC_API["/api/v1/public"] end subgraph API_V2["đŸ‘šâ€đŸ’Œ API v2 Admin"] ADMIN_API["/api/v2/admin"] end subgraph HANDLERS["⚙ Handlers"] H_AUTH[Auth Handler] H_PRODUCT[Product Handler] H_PANIER[Panier Handler] H_COMMAND[Command Handler] H_DELIVERY[Delivery Handler] H_GPS[GPS Handler] H_ETA[ETA Handler] H_PENALTY[Penalty Handler] end subgraph SERVICES["🔧 Services"] S_GEO[Geo Service] S_TOMTOM[TomTom Service] S_QUEUE[Queue Service] S_NOTIF[Notification Service] end subgraph EXTERNAL["🌍 APIs Externes"] NOMINATIM[Nominatim API] TOMTOM_API[TomTom API] end subgraph STORAGE["đŸ’Ÿ Stockage"] POSTGRES[(PostgreSQL)] REDIS[(Redis)] end subgraph WORKERS["⚡ Workers"] W_ASSIGN[Auto-Assign Worker] W_CLEANUP[Cleanup Worker] W_ETA[ETA Update Worker] end %% Frontend connections UI_CLIENT --> AUTH UI_ADMIN --> AUTH UI_LIVREUR --> AUTH UI_CABINE --> AUTH TOMTOM_MAP --> TOMTOM_API %% API Gateway AUTH --> ROUTES ROUTES --> API_V1 ROUTES --> API_V2 %% API to Handlers CLIENT_API --> H_AUTH CLIENT_API --> H_PANIER CLIENT_API --> H_COMMAND LIVREUR_API --> H_DELIVERY LIVREUR_API --> H_GPS CABINE_API --> H_COMMAND PUBLIC_API --> H_PRODUCT ADMIN_API --> H_COMMAND ADMIN_API --> H_DELIVERY ADMIN_API --> H_GPS ADMIN_API --> H_PENALTY %% Handlers to Services H_GPS --> S_GEO H_GPS --> S_TOMTOM H_ETA --> S_TOMTOM H_DELIVERY --> S_QUEUE H_COMMAND --> S_NOTIF %% Services to External S_GEO --> NOMINATIM S_TOMTOM --> TOMTOM_API %% Services to Storage S_GEO --> REDIS S_QUEUE --> REDIS H_COMMAND --> POSTGRES H_PRODUCT --> POSTGRES H_DELIVERY --> POSTGRES %% Workers W_ASSIGN --> S_GEO W_ASSIGN --> S_QUEUE W_ASSIGN --> POSTGRES W_CLEANUP --> REDIS W_CLEANUP --> POSTGRES W_ETA --> S_TOMTOM W_ETA --> REDIS ``` ### 🔄 Flux des Donnees par Role ```mermaid flowchart LR subgraph Client["đŸ‘€ Client"] C1[Consulter Produits] C2[Gerer Panier + Stock auto] C3[Passer Commande] C4[Suivre Livraison + ETA] C5[Approuver/Annuler] C6[Notifications Push] C7[Solde Parrainage] end subgraph Admin["đŸ‘šâ€đŸ’Œ Admin"] A1[Gerer Produits/Clients] A2[Voir/Modifier Commandes] A3[Assigner Livreurs GPS] A4[Gerer Penalites] A5[Surveiller GPS + Queues] A6[Parametres Globaux] end subgraph Livreur["🚚 Livreur"] L1[Voir Mes Livraisons] L2[Mettre a Jour Position GPS] L3[Changer Statut] L4[Voir Ma Queue] L5[Alertes Police] L6[Notifications Push] end subgraph Cabine["🏭 Cabine/Cuisine"] K1[Voir Articles Commande] K2[Preparer/Marquer Pret] K3[Assigner Livreur] K4[Gerer Penalites Client] K5[Voir Alertes] end subgraph Backend["⚙ Backend"] API[API REST] GPS[Service GPS] QUEUE[Gestion Queue] end C1 & C2 & C3 & C4 & C5 & C6 & C7 --> API A1 & A2 & A3 & A4 & A5 & A6 --> API L1 & L2 & L3 & L4 & L5 & L6 --> API K1 & K2 & K3 & K4 & K5 --> API API --> GPS API --> QUEUE ``` ### 📡 Architecture des Endpoints API ```mermaid graph TD subgraph PUBLIC["🔓 Endpoints Publics (sans auth)"] P1["GET /api/v1/products"] P2["GET /api/v1/products/:id"] P3["GET /api/v1/products/category/:category"] P4["GET /api/v1/categories"] P5["GET /api/v1/app-settings"] P6["POST /api/v1/webhooks/nowpayments"] P7["POST /webhook/telegram"] end subgraph AUTH_PUBLIC["🔑 Auth Publique (rate-limited)"] AP1["POST /api/v1/auth/login"] AP2["POST /api/v1/auth/logout"] AP3["PUT /api/v1/auth/change-password (JWT)"] end subgraph CLIENT_AUTH["🔐 Client (JWT Required)"] C1["POST /api/v1/panier/add"] C2["GET /api/v1/panier/:username"] C3["DELETE /api/v1/panier/remove"] C4["DELETE /api/v1/panier/clear"] C5["POST /api/v1/checkout"] C6["GET /api/v1/my-commands"] C7["GET /api/v1/my-commands/history"] C8["GET /api/v1/my-commands/history/detailed"] C9["GET /api/v1/commands/:id"] C10["GET /api/v1/commands/:id/status"] C11["GET /api/v1/commands/:id/tracking"] C12["GET /api/v1/commands/:id/eta"] C13["GET /api/v1/commands/:id/items"] C14["GET /api/v1/commands/:id/history"] C15["POST /api/v1/commands/:id/approve"] C16["POST /api/v1/commands/:id/cancel"] C17["POST /api/v1/commands/:id/address/respond"] C18["GET /api/v1/my-cancellation-history"] C19["GET /api/v1/penalties"] C20["GET /api/v1/notifications"] C21["POST /api/v1/notifications/read"] C22["GET /api/v1/profile"] C23["PUT /api/v1/profile/update"] C24["GET /api/v1/referral/balance"] C25["GET /api/v1/parrain"] C26["GET /api/v1/commands/:id/payment-status"] C27["POST /api/v1/telegram/link-token"] C28["GET /api/v1/telegram/status"] C29["DELETE /api/v1/telegram/unlink"] end subgraph ADMIN_AUTH["đŸ‘šâ€đŸ’Œ Admin v2 (JWT Required)"] A1["GET /api/v2/admin/protected/orders"] A2["GET /api/v2/admin/protected/orders/:id"] A3["PUT /api/v2/admin/protected/orders/:id/status"] A4["PUT /api/v2/admin/protected/orders/:id/address"] A5["POST /api/v2/admin/protected/orders/:id/notify-client"] A6["POST /api/v2/admin/protected/orders/:id/auto-assign"] A7["POST /api/v2/admin/protected/orders/auto-assign-all"] A8["POST /api/v2/admin/protected/orders/:id/force-validate"] A9["POST /api/v2/admin/protected/orders/:id/confirm-reception"] A10["GET /api/v2/admin/protected/delivery-persons"] A11["GET /api/v2/admin/protected/delivery-persons/:username"] A12["GET /api/v2/admin/protected/delivery-persons/:username/location"] A13["GET /api/v2/admin/protected/delivery/queues"] A14["POST /api/v2/admin/protected/delivery/distances"] A15["POST/GET /api/v2/admin/protected/products/:id"] A16["POST /api/v2/admin/protected/categories"] A17["POST /api/v2/admin/protected/penalty"] A18["GET /api/v2/admin/protected/all/clients"] A19["PUT /api/v2/admin/protected/clients/:id"] A20["GET /api/v2/admin/protected/alerts"] A21["GET/PUT /api/v2/admin/protected/settings"] A22["GET /api/v2/admin/protected/penalties/all"] A23["GET /api/v2/admin/protected/addresses"] end subgraph LIVREUR_AUTH["🚚 Livreur (JWT Required)"] L1["GET /api/v1/livreur/deliveries"] L2["GET /api/v1/livreur/deliveries/:id"] L3["POST /api/v1/livreur/deliveries/:id/start"] L4["PUT /api/v1/livreur/deliveries/:id/status"] L5["GET /api/v1/livreur/deliveries/:id/nav-link"] L6["POST /api/v1/livreur/location/update"] L7["GET /api/v1/livreur/location"] L8["POST /api/v1/livreur/update/status"] L9["GET /api/v1/livreur/status"] L10["GET /api/v1/livreur/queue"] L11["POST /api/v1/livreur/alert"] L12["DELETE /api/v1/livreur/alert/:id"] L13["GET /api/v1/livreur/alerts"] L14["GET /api/v1/livreur/notifications"] L15["POST /api/v1/livreur/notifications/read"] L16["POST /api/v1/livreur/telegram/link-token"] L17["GET /api/v1/livreur/telegram/status"] L18["DELETE /api/v1/livreur/telegram/unlink"] end subgraph CABINE_AUTH["🏭 Cabine (JWT Required)"] K1["GET /api/v1/cabine/commands/:id/items"] K2["PUT /api/v1/cabine/commands/:id/status"] K3["PUT /api/v1/cabine/items/:item_id/status"] K4["POST /api/v1/cabine/commands/:id/confirm-reception"] K5["POST /api/v1/cabine/commands/:id/assign"] K6["POST /api/v1/cabine/commands/:id/notify-client"] K7["GET /api/v1/cabine/all/deliveryman"] K8["GET /api/v1/cabine/alerts"] K9["GET /api/v1/cabine/penalties/all"] K10["GET /api/v1/cabine/addresses"] K11["GET /api/v1/cabine/notifications"] K12["POST /api/v1/cabine/telegram/link-token"] end GW[API Gateway :8080] GW --> PUBLIC GW --> AUTH_PUBLIC GW --> CLIENT_AUTH GW --> ADMIN_AUTH GW --> LIVREUR_AUTH GW --> CABINE_AUTH ``` ### 🔐 Flux d'Authentification ```mermaid sequenceDiagram participant U as Utilisateur participant F as Frontend participant API as Backend API participant DB as PostgreSQL participant R as Redis participant TG as Telegram rect rgb(200, 230, 200) Note over U,R: Registration U->>F: Remplir formulaire inscription F->>API: POST /auth/register API->>DB: Creer utilisateur DB-->>API: User cree API-->>F: 201 Created + User info F-->>U: Compte cree end rect rgb(200, 200, 230) Note over U,TG: Login Standard (2FA desactivee) U->>F: Entrer credentials F->>API: POST /auth/login API->>DB: Verifier credentials DB-->>API: User valide API->>API: Generer JWT API->>R: Stocker session API-->>F: 200 OK + { access_token, user } F->>F: Stocker token F-->>U: Connecte end rect rgb(255, 240, 200) Note over U,TG: Login avec 2FA (Telegram active) U->>F: Entrer credentials F->>API: POST /auth/login API->>DB: Verifier credentials + verifier 2FA active DB-->>API: User valide, 2FA requise API->>R: Stocker session_token (TTL 5min) API->>TG: Envoyer code 6 chiffres via bot Telegram API-->>F: 200 OK + { requires_2fa: true, session_token } F-->>U: Afficher saisie du code Telegram U->>F: Entrer code recu sur Telegram F->>API: POST /auth/2fa/verify { session_token, code } API->>R: Verifier code + session_token R-->>API: Code valide API->>API: Generer JWT API->>R: Stocker session API-->>F: 200 OK + { access_token, user } F->>F: Stocker token F-->>U: Connecte end rect rgb(230, 200, 200) Note over U,R: Requete Authentifiee U->>F: Action protegee F->>API: Request + Authorization: Bearer JWT API->>API: Valider JWT API->>R: Verifier session active R-->>API: Session valide API->>DB: Executer action DB-->>API: Resultat API-->>F: Response F-->>U: Resultat affiche end ``` ### 📩 Flux Complet d'une Commande ```mermaid sequenceDiagram participant C as Client participant F as Frontend participant API as Backend participant GPS as Service GPS participant Q as Queue Service participant L as Livreur participant DB as PostgreSQL participant R as Redis rect rgb(220, 240, 220) Note over C,R: 1. Ajout au panier C->>F: Ajouter produit F->>API: POST /panier/add {product_id, quantity} API->>DB: Verifier stock (GetProductStockByID) DB-->>API: Stock OK API->>DB: Decrementer stock (DecrementProductStockByID) API->>DB: Ajouter ligne panier API-->>F: 201 Created F-->>C: Panier mis a jour Note over C,R: Si suppression panier → stock restitue automatiquement end rect rgb(230, 245, 230) Note over C,R: 2. Checkout C->>F: Valider panier F->>API: POST /checkout {address} API->>GPS: Geocoder adresse GPS->>R: Check cache R-->>GPS: Miss GPS->>GPS: Appel Nominatim GPS->>R: Cache resultat (7j) GPS-->>API: Coordonnees API->>DB: Creer commande API->>Q: Chercher livreur proche Q->>R: Get positions livreurs Q->>Q: Calcul distances Haversine Q->>GPS: Calculer ETA (TomTom) Q-->>API: Livreur assigne API->>DB: Update commande API->>R: Ajouter a queue livreur API-->>F: Commande creee + ETA F-->>C: Confirmation end rect rgb(230, 230, 245) Note over C,R: 3. Livraison L->>API: GET /livreur/deliveries API-->>L: Liste commandes L->>API: POST /deliveries/:id/start L->>API: POST /location/update {lat, lng} API->>R: Update position API->>R: Publish update R-->>F: Notification position F-->>C: Carte mise a jour L->>API: PUT /deliveries/:id/status {en_route} Note over L,API: ETA calcule et stocke dans Redis L->>API: PUT /deliveries/:id/status {arrived} L->>API: PUT /deliveries/:id/status {livre} end rect rgb(245, 230, 230) Note over C,R: 4. Finalisation C->>F: Approuver livraison F->>API: POST /commands/:id/approve {rating} API->>DB: Update statut approved API->>R: Retirer de queue API-->>F: Confirmation F-->>C: Livraison terminee end ``` ### đŸ’Ÿ Schema de la Base de Donnees ```mermaid erDiagram CLIENTS { int id PK string username UK string password string nom string prenom string telephone UK int command int point int points_zipette float amende int cancellations_count timestamp created_at } ADMINS { int id PK string username UK string password string role timestamp created_at } LIVREURS { int id PK string username UK string password string status timestamp created_at } CABINES { int id PK string username UK string password timestamp created_at } PRODUCTS { int id PK string nom string description string category int stock float prix timestamp created_at } PRODUCT_PRICES { int id PK int product_id FK int quantity float price boolean active_price } PRODUCT_MEDIA { int id PK int product_id FK string type string url } PANIER { int id PK string username FK string name_product string category int quantity float price timestamp created_at } COMMANDS { int id PK string username FK string status float total string adresse float dest_latitude float dest_longitude string livreur_assign FK timestamp created_at timestamp updated_at } COMMAND_ITEMS { int id PK int command_id FK string produit int quantite float prix string status string category } RATINGS { int id PK int command_id FK string deliveryman int rating string comment timestamp created_at } CLIENTS ||--o{ PANIER : "possede" CLIENTS ||--o{ COMMANDS : "passe" PRODUCTS ||--o{ PRODUCT_PRICES : "a" PRODUCTS ||--o{ PRODUCT_MEDIA : "a" COMMANDS ||--o{ COMMAND_ITEMS : "contient" COMMANDS ||--o| RATINGS : "a" LIVREURS ||--o{ COMMANDS : "livre" ``` ### đŸ—„ïž Structure Redis ```mermaid graph LR subgraph Sessions["🔐 Sessions"] S1["session:{token}
TTL: 5h (client) / 2h (admin)"] end subgraph Positions["📍 Positions GPS"] P1["delivery:location:{username}
TTL: 2h"] P2["delivery:status:{username}
TTL: 1h"] end subgraph Queues["📋 Queues Livraison"] Q1["livreur:queue:{username}
List of command_ids"] Q2["queue:size:{username}
Integer"] end subgraph Cache["đŸ’Ÿ Cache"] C1["geocode:cache:{hash}
TTL: 7j"] C2["command:destination:{id}
TTL: 4h"] C3["product:cache:{id}
TTL: 1h"] end subgraph PubSub["ïżœïżœ Pub/Sub Channels"] PS1["channel:position_updates"] PS2["channel:order_status"] PS3["channel:notifications"] end REDIS[(Redis Server)] REDIS --- Sessions REDIS --- Positions REDIS --- Queues REDIS --- Cache REDIS --- PubSub ``` --- ## 🚀 DĂ©ploiement Production ### PrĂ©requis - Docker + Docker Compose - Certificats SSL : `fullchain.pem` + `privkey.pem` (Let's Encrypt recommandĂ©) - Fichier `.env` complĂ©tĂ© (voir `.env.example`) ### Structure des fichiers ``` docker/ ├── backend/ │ ├── Dockerfile # Stage builder (Go), runtime, waf (nginx+ModSec) │ ├── nginx.conf # Config nginx hardened (TLS 1.2/1.3, HSTS, CSP) │ ├── custom-rules.conf # RĂšgles ModSecurity personnalisĂ©es │ └── entrypoint.sh # Entrypoint backend Go ├── frontend/ │ ├── Dockerfile # Build React/Vite + nginx interne │ └── nginx.conf # Nginx interne (HTTP, sans TLS) ├── certs/ # Certificats SSL (non versionnĂ©s) │ ├── fullchain.pem │ └── privkey.pem └── docker-compose-prod.yml ``` ### DĂ©ploiement ```bash # 1. Copier les certificats mkdir -p docker/certs cp /etc/letsencrypt/live/votre-domaine/fullchain.pem docker/certs/ cp /etc/letsencrypt/live/votre-domaine/privkey.pem docker/certs/ chmod 644 docker/certs/privkey.pem # 2. Configurer les variables d'environnement cp docker/.env.example docker/.env # Éditer docker/.env avec vos valeurs # 3. Lancer la stack docker compose -f docker/docker-compose-prod.yml up -d --build # 4. VĂ©rifier les logs docker compose -f docker/docker-compose-prod.yml logs -f waf ``` ### Services Docker | Service | Image | RĂŽle | |---------|-------|------| | `waf` | owasp/modsecurity-crs:nginx-alpine | Point d'entrĂ©e HTTPS (ports 80/443) | | `backend` | Go 1.24 alpine | API REST (port 8080 interne) | | `frontend` | nginx:alpine | SPA React (port 80 interne) | | `postgres` | postgres:16-alpine | Base de donnĂ©es | | `redis` | redis:7-alpine | Cache + sessions + queues | ### Variables d'environnement requises ```bash DB_PASSWORD= # Mot de passe PostgreSQL USER_JWT_SECRET= # Secret JWT clients (min 32 chars) ADMIN_JWT_SECRET= # Secret JWT admin/livreur/cabine (min 32 chars) REDIS_PASSWORD= # Mot de passe Redis TOMTOM_API_KEY= # ClĂ© API TomTom (principale / legacy) TOMTOM_API_KEY_1= # ClĂ© TomTom #1 (rotation automatique) TOMTOM_API_KEY_2= # ClĂ© TomTom #2 (rotation automatique) TOMTOM_API_KEY_3= # ClĂ© TomTom #3 (rotation automatique) SESSION_SECRET= # Secret sessions TELEGRAM_WEBHOOK_URL= # URL webhook Telegram TELEGRAM_WEBHOOK_SECRET= # Secret webhook Telegram NOWPAYMENTS_IPN_SECRET= # Secret IPN NowPayments ``` --- ## 🔔 Notifications Les notifications clients et livreurs sont gĂ©rĂ©es **exclusivement via Telegram** — les push notifications Expo (iOS/Android) ne sont plus utilisĂ©es. ### Notifications Telegram Clients, livreurs et admins reçoivent leurs alertes via un bot Telegram liĂ© Ă  leur compte. **Types de notifications envoyĂ©es :** - `assigned` — Commande assignĂ©e Ă  un livreur - `en_route` — Livreur en route (avec ETA) - `arrived` — Livreur arrivĂ© - `livre` — Commande livrĂ©e - `ready_pickup` — Cabine : "descendez chercher votre commande" - `address_proposal` — Proposition de changement d'adresse Les admins et livreurs peuvent lier leur compte Telegram pour recevoir des alertes. #### GĂ©nĂ©rer un token de liaison (Admin) ```bash POST /api/v2/admin/protected/telegram/link-token Authorization: Bearer ``` **RĂ©ponse:** ```json { "link_token": "abc123xyz", "expires_in": 300, "bot_url": "https://t.me/votre_bot?start=abc123xyz" } ``` #### GĂ©nĂ©rer un token de liaison (Livreur) ```bash POST /api/v1/livreur/telegram/link-token Authorization: Bearer ``` #### Statut Telegram (Livreur) ```bash GET /api/v1/livreur/telegram/status Authorization: Bearer ``` #### DĂ©lier Telegram ```bash DELETE /api/v1/livreur/telegram/unlink Authorization: Bearer ``` --- ## 💾 Paiements Crypto Le systĂšme intĂšgre NowPayments pour les paiements en cryptomonnaie. ### Webhook IPN (Instant Payment Notification) Endpoint public sĂ©curisĂ© par signature HMAC-SHA512. ```bash POST /api/v1/webhooks/nowpayments Content-Type: application/json x-nowpayments-sig: ``` Le webhook met automatiquement Ă  jour le statut de paiement de la commande correspondante. #### VĂ©rifier le statut de paiement d'une commande ```bash GET /api/v1/commands/:id/payment-status Authorization: Bearer ``` **RĂ©ponse:** ```json { "command_id": 1234, "payment_status": "confirmed", "amount_paid": 25.00, "currency": "USDT" } ``` --- ## 🔐 Authentication ### CrĂ©ation de compte client Les comptes clients sont créés **uniquement par un administrateur** via `POST /api/v2/admin/protected/clients`. Il n'existe pas d'endpoint d'auto-inscription. --- ### Login Client **Se connecter et obtenir un token JWT** ```bash POST /api/v1/auth/login Content-Type: application/json ``` **RequĂȘte:** ```json { "username": "jean_dupont", "password": "SecurePass123!" } ``` **RĂ©ponse standard (200 OK) — 2FA dĂ©sactivĂ©e:** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 18000, "user": { "username": "jean_dupont", "role": "client" } } ``` **RĂ©ponse avec 2FA (200 OK) — quand 2FA activĂ©e par le client ET Telegram liĂ© ET admin l'a activĂ©e:** ```json { "requires_2fa": true, "session_token": "550e8400-e29b-41d4-a716-446655440000" } ``` > Un code Ă  6 chiffres est envoyĂ© automatiquement sur le compte Telegram liĂ©. Le `session_token` expire dans **5 minutes**. **Erreurs possibles:** - `400` - DonnĂ©es manquantes - `401` - Identifiants incorrects --- ### VĂ©rifier le code 2FA **Valider le code Telegram reçu pour finaliser la connexion** ```bash POST /api/v1/auth/2fa/verify Content-Type: application/json ``` **RequĂȘte:** ```json { "session_token": "550e8400-e29b-41d4-a716-446655440000", "code": "483721" } ``` **RĂ©ponse (200 OK):** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 18000, "user": { "username": "jean_dupont", "role": "client" } } ``` **Erreurs possibles:** - `400` - `session_token` ou `code` manquant - `401` - Code incorrect ou session expirĂ©e (TTL 5 min) - `429` - Trop de tentatives (rate limiting) --- ### Statut 2FA du compte **Obtenir l'Ă©tat de la 2FA pour le compte connectĂ©** ```bash GET /api/v1/two-fa/status Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "two_fa_enabled": true, "telegram_linked": true, "admin_2fa_enabled": true } ``` | Champ | Description | |-------|-------------| | `two_fa_enabled` | La 2FA est activĂ©e sur ce compte client | | `telegram_linked` | Un compte Telegram est liĂ© (prĂ©requis pour activer) | | `admin_2fa_enabled` | L'admin a activĂ© la 2FA dans les paramĂštres globaux | --- ### Activer / DĂ©sactiver la 2FA **Basculer l'Ă©tat de la 2FA sur son propre compte** ```bash POST /api/v1/two-fa/toggle Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "enabled": true } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "two_fa_enabled": true } ``` **PrĂ©requis pour activer (`"enabled": true`):** 1. Le client doit avoir liĂ© son compte Telegram (via `/api/v1/auth/link-telegram`) 2. L'administrateur doit avoir activĂ© `telegram_2fa_enabled` dans les paramĂštres globaux **Erreurs possibles:** - `400` - Telegram non liĂ© (impossible d'activer sans compte Telegram) - `403` - La 2FA n'est pas autorisĂ©e par l'administrateur - `401` - Non authentifiĂ© > **Interface utilisateur:** Sur le frontend web (page profil) et l'app mobile, un toggle permet d'activer/dĂ©sactiver la 2FA directement depuis les paramĂštres du compte. --- ### Logout Client **Se dĂ©connecter (invalider le token)** ```bash POST /api/v1/auth/logout Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "DĂ©connexion rĂ©ussie" } ``` --- ## 🛒 API Client (v1) **Toutes les routes clients nĂ©cessitent le header:** ``` Authorization: Bearer ``` ### Produits (Public - Pas d'auth requis) > **Filtrage des prix inactifs :** Chaque prix d'un produit porte un flag `active_price` (boolĂ©en). Les endpoints publics et client **n'exposent que les prix actifs** (`active_price = true`). Les rĂŽles `admin` et `cabine` reçoivent tous les prix (actifs et inactifs) pour permettre la gestion complĂšte. CĂŽtĂ© frontend (web + mobile), les prix sont Ă©galement filtrĂ©s au chargement (`filter(p => p.active_price !== false)`). #### Liste des produits ```bash GET /api/v1/products ``` **RĂ©ponse (200 OK):** ```json { "success": true, "products": [ { "id": 1, "nom": "Pizza Margherita", "description": "Pizza traditionnelle avec tomate, mozzarella et basilic", "category": "pizza", "stock": 50, "prix": 12.50, "prices": [ { "id": 1, "product_id": 1, "quantity": 1, "price": 12.50, "active_price": true } ], "media": [ { "id": 1, "product_id": 1, "type": "image", "url": "/uploads/pizza_margherita.jpg" } ], "created_at": "2025-01-18T00:00:00Z" } ], "total": 1 } ``` --- #### Produit par ID ```bash GET /api/v1/products/:id ``` **RĂ©ponse (200 OK):** ```json { "success": true, "product": { "id": 1, "nom": "Pizza Margherita", "description": "Pizza traditionnelle avec tomate, mozzarella et basilic", "category": "pizza", "stock": 50, "prix": 12.50, "prices": [...], "media": [...], "created_at": "2025-01-18T00:00:00Z" } } ``` **Erreurs possibles:** - `404` - Produit non trouvĂ© --- ### Gestion du Panier > **Note stock :** Le stock est dĂ©crĂ©mentĂ© dĂšs l'ajout au panier et restituĂ© automatiquement lors de la suppression d'un article ou du vidage du panier. #### Ajouter au panier ```bash POST /api/v1/panier/add Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "name_product": "Pizza Margherita", "category": "pizza", "quantity": 2 } ``` **RĂ©ponse (201 Created):** ```json { "success": true, "message": "Produit ajoutĂ© au panier avec succĂšs", "panier": { "id": 15, "username": "jean_dupont", "name_product": "Pizza Margherita", "category": "pizza", "quantity": 2, "price": 25.00, "created_at": "2025-01-18T14:35:00Z" } } ``` **Erreurs possibles:** - `400` - DonnĂ©es invalides ou stock insuffisant - `401` - Non authentifiĂ© - `404` - Produit non trouvĂ© --- #### Voir mon panier ```bash GET /api/v1/panier/:username Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Panier rĂ©cupĂ©rĂ© avec succĂšs", "panier": [ { "id": 15, "username": "jean_dupont", "name_product": "Pizza Margherita", "category": "pizza", "quantity": 2, "price": 25.00, "created_at": "2025-01-18T14:35:00Z" } ], "count": 1, "total_amount": 25.00 } ``` **Erreurs possibles:** - `403` - AccĂšs au panier d'un autre utilisateur - `404` - Utilisateur non trouvĂ© --- #### Supprimer du panier ```bash DELETE /api/v1/panier/remove Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "id": 15 } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Produit supprimĂ© du panier avec succĂšs", "item_id": 15 } ``` **Erreurs possibles:** - `403` - Tentative de supprimer l'article d'un autre utilisateur - `404` - Article non trouvĂ© --- #### Vider le panier ```bash DELETE /api/v1/panier/clear Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Panier vidĂ© avec succĂšs" } ``` --- ### Commandes #### Valider le panier (Checkout avec Auto-assignation) ```bash POST /api/v1/checkout Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "delivery_address": "15 Rue de la Paix, 75002 Paris, France" } ``` **RĂ©ponse avec auto-assignation (200 OK):** ```json { "success": true, "message": "Commande créée et livreur assignĂ© automatiquement", "command_id": 1234, "delivery_address": "15 Rue de la Paix, 75002 Paris, France", "status": "assigned", "auto_assigned": true, "assigned_to": { "username": "john_deliveryman", "distance_km": 1.5, "travel_time": 8 } } ``` **RĂ©ponse sans auto-assignation (200 OK):** ```json { "success": true, "message": "Commande créée - En attente d'assignation", "command_id": 1234, "delivery_address": "15 Rue de la Paix, 75002 Paris, France", "status": "pending", "auto_assigned": false } ``` **Erreurs possibles:** - `400` - Panier vide ou adresse manquante - `401` - Non authentifiĂ© - `500` - Erreur crĂ©ation commande --- #### Mes commandes avec suivi ```bash GET /api/v1/my-commands Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "commands": [ { "id": 1234, "username": "jean_dupont", "status": "assigned", "total": 25.00, "delivery_address": "15 Rue de la Paix, 75002 Paris", "livreur_assign": "john_deliveryman", "created_at": "2025-01-18T14:40:00Z", "updated_at": "2025-01-18T14:42:00Z", "tracking": { "eta_minutes": 25, "estimated_arrival": "15:05", "deliveryman_status": "en_route", "queue_position": 2 } } ], "total": 1 } ``` --- #### Statut temps rĂ©el d'une commande ```bash GET /api/v1/commands/:id/status Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "command_id": 1234, "status": "en_route", "deliveryman": "john_deliveryman", "updated_at": "2025-01-18T15:20:00Z" } ``` **Statuts possibles:** | Statut | Description | Qui peut le dĂ©finir | |--------|-------------|---------------------| | `pending` | En attente d'assignation | SystĂšme (checkout) | | `assigned` | AssignĂ©e Ă  un livreur | SystĂšme (auto-assign), Admin | | `en_route` | Livreur en chemin | Livreur, Admin | | `arrived` | Livreur arrivĂ© Ă  destination | Livreur, Admin, Cabine (bouton "Le livreur est lĂ ") | | `livre` | LivrĂ©e, en attente d'approbation | Livreur (validation GPS), Admin | | `approved` | ApprouvĂ©e et finalisĂ©e | Client (approve), Admin, Cabine | | `cancelled` | AnnulĂ©e | Client, Livreur, Admin | --- #### Suivi dĂ©taillĂ© avec ETA ```bash GET /api/v1/commands/:id/tracking Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "command_id": 1234, "status": "en_route", "deliveryman": "john_deliveryman", "eta": { "minutes": 22, "estimated_arrival": "15:02", "arrival_time_unix": 1705669320, "updated_at": "2025-01-18T14:40:00Z" }, "queue_position": 2, "total_queue_size": 5 } ``` --- #### ETA de livraison ```bash GET /api/v1/commands/:id/eta Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "command_id": 1234, "eta_minutes": 22, "estimated_arrival": "15:02:00", "queue_position": 2, "updated_at": "2025-01-18T14:40:00Z" } ``` --- #### Approuver une livraison ```bash POST /api/v1/commands/:id/approve Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "rating": 5, "comment": "Excellent service, livreur trĂšs professionnel !" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Livraison approuvĂ©e avec succĂšs", "command_id": 1234, "status": "approved", "rating": { "deliveryman": "john_deliveryman", "rating": 5, "comment": "Excellent service, livreur trĂšs professionnel !", "created_at": "2025-01-18T15:05:00Z" } } ``` --- #### Annuler une commande ```bash POST /api/v1/commands/:id/cancel Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "reason": "Changement de plans" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Commande annulĂ©e avec succĂšs", "command_id": 1234, "penalty_applied": true, "penalty_details": { "cancellations_count": 1, "penalty_amount": 20.0, "warning": "PĂ©nalitĂ© appliquĂ©e pour annulation" } } ``` --- ### Profil et Historique #### Mettre Ă  jour mon profil ```bash PUT /api/v1/profile/update Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "nom": "Nouveau Nom", "prenom": "Nouveau PrĂ©nom", "telephone": "+33687654321", "password": "NewSecurePass456!" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Profil mis Ă  jour avec succĂšs", "client": { "id": 42, "username": "jean_dupont", "nom": "Nouveau Nom", "prenom": "Nouveau PrĂ©nom", "telephone": "+33687654321", "updated_at": "2025-01-18T15:30:00Z" } } ``` --- #### Mes pĂ©nalitĂ©s ```bash GET /api/v1/penalties Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "client": { "username": "jean_dupont", "amende": 20.0, "cancellations_count": 1, "last_penalty_reason": "Annulation tardive" }, "penalty_scale": { "1st": 20.0, "2nd": 50.0, "3rd": 100.0, "4th+": 150.0 }, "next_penalty": 50.0, "warning": "La prochaine annulation entraĂźnera une pĂ©nalitĂ© de 50 points" } ``` --- ## đŸ‘šâ€đŸ’Œ API Admin (v2) **Toutes les routes admin nĂ©cessitent le header:** ``` Authorization: Bearer ``` ### Authentication Admin #### Login Admin ```bash POST /api/v2/admin/auth/login Content-Type: application/json ``` **RequĂȘte:** ```json { "username": "admin_master", "password": "AdminSecure123!" } ``` **RĂ©ponse (200 OK):** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 7200, "user": { "username": "admin_master", "role": "admin" } } ``` --- ### Gestion des Commandes #### Liste des commandes ```bash GET /api/v2/admin/protected/orders Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "orders": [ { "id": 1234, "username": "jean_dupont", "status": "assigned", "total": 25.00, "delivery_address": "15 Rue de la Paix, 75002 Paris", "livreur_assign": "john_deliveryman", "dest_latitude": 48.8698, "dest_longitude": 2.3311, "created_at": "2025-01-18T14:40:00Z", "updated_at": "2025-01-18T14:42:00Z" } ], "total": 1 } ``` --- #### Modifier le statut d'une commande ```bash PUT /api/v2/admin/protected/orders/:id/status Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "status": "arrived" } ``` **Statuts acceptĂ©s:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Statut mis Ă  jour", "command_id": 1234, "new_status": "arrived" } ``` **Erreurs possibles:** - `400` - Statut invalide - `403` - RĂŽle insuffisant (admin ou cabine requis) - `404` - Commande non trouvĂ©e --- #### Notifier le client de descendre Envoie une notification (Telegram + in-app) au client pour rĂ©cupĂ©rer sa commande, puis passe le statut en `arrived`. ```bash POST /api/v2/admin/protected/orders/:id/notify-client Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Client notifiĂ©", "client_username": "jean_dupont" } ``` --- #### Assignation automatique GPS ```bash POST /api/v2/admin/protected/orders/:id/auto-assign Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Commande assignĂ©e automatiquement", "command_id": 1234, "assigned_to": "john_deliveryman", "distance": { "km": 1.5, "eta_minutes": 8 }, "queue_position": 3 } ``` **Erreurs possibles:** - `404` - Commande non trouvĂ©e - `400` - Aucun livreur disponible - `409` - Commande dĂ©jĂ  assignĂ©e --- #### Assigner toutes les commandes en attente ```bash POST /api/v2/admin/protected/orders/auto-assign-all Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Auto-assignation effectuĂ©e", "assigned_count": 5, "failed_count": 0, "details": [ { "command_id": 1234, "assigned_to": "john_deliveryman", "distance_km": 1.5, "eta_minutes": 8 } ] } ``` --- ### Gestion des Livreurs #### Livreurs disponibles ```bash GET /api/v2/admin/protected/delivery-persons Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "delivery_persons": [ { "username": "john_deliveryman", "status": "available", "current_command": 0, "queue_size": 2, "location": { "latitude": 48.8566, "longitude": 2.3522, "last_update": "2025-01-18T16:30:00Z" } } ], "total": 1 } ``` --- #### Position d'un livreur ```bash GET /api/v2/admin/protected/delivery-persons/:username/location Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "deliveryman": "john_deliveryman", "location": { "latitude": 48.8566, "longitude": 2.3522, "last_update": "2025-01-18T16:25:00Z" }, "status": "available" } ``` --- #### Queues des livreurs ```bash GET /api/v2/admin/protected/delivery/queues Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "queues": { "john_deliveryman": { "queue_size": 3, "commands": [1234, 1235, 1236], "can_accept_more": true, "capacity": "3/10", "status": "available" } }, "total_pending": 3 } ``` --- ### Gestion des Produits #### CrĂ©er un produit ```bash POST /api/v2/admin/protected/products Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "nom": "Pizza Pepperoni", "category": "pizza", "description": "Pizza avec pepperoni et mozzarella", "stock": 30, "prix": 14.50 } ``` **RĂ©ponse (201 Created):** ```json { "success": true, "message": "Produit créé avec succĂšs", "product": { "id": 10, "nom": "Pizza Pepperoni", "category": "pizza", "description": "Pizza avec pepperoni et mozzarella", "stock": 30, "prix": 14.50, "created_at": "2025-01-18T16:10:00Z" } } ``` --- ### SystĂšme de PĂ©nalitĂ©s (Clients uniquement) Les amendes s'appliquent **uniquement aux clients**. Les livreurs n'ont pas d'amende. Les amendes sont stockĂ©es dans `clients.amende` (PostgreSQL). Tant que `amende > 0`, le middleware `BlockClientIfPenalty` bloque toute tentative de checkout. **Échelle progressive (annulations) :** | Nbre d'annulations | Amende | |---|---| | 1Ăšre | 20 € | | 2Ăšme | 50 € | | 3Ăšme | 100 € | | 4Ăšme et + | 150 € | **Sources d'amende :** - Client annule sa propre commande → `ApplyCancellationPenalty` (incrĂ©mente `cancellations_count`) - Livreur marque le client absent depuis le statut `arrived` → amende appliquĂ©e automatiquement sur le **client** **Message d'erreur au checkout bloquĂ© :** ```json { "error": "Commande bloquĂ©e : vous avez une amende de 20€ en attente de paiement. Prenez attache avec Milieu Nantais sur signal pour rĂ©gulariser votre situation..", "amende": 20.0, "blocked": true } ``` #### Appliquer une pĂ©nalitĂ© (Admin) ```bash POST /api/v2/admin/protected/penalty Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "username": "jean_dupont", "amount": 50.0, "reason": "Comportement inappropriĂ©" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "PĂ©nalitĂ© appliquĂ©e", "client": "jean_dupont", "penalty": { "amount": 50.0, "reason": "Comportement inappropriĂ©", "applied_at": "2025-01-18T17:00:00Z" }, "total_amende": 70.0 } ``` --- ## đŸȘ API Cabine (v1) **Toutes les routes cabine nĂ©cessitent le header:** ``` Authorization: Bearer ``` #### Articles d'une commande ```bash GET /api/v1/cabine/commands/:id/items Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "command_id": 1234, "items": [ { "id": 1, "command_id": 1234, "produit": "Pizza Margherita", "quantite": 2, "prix": 25.0, "status": "pending", "category": "pizza" } ], "total": 1 } ``` --- #### Modifier le statut d'une commande ```bash PUT /api/v1/cabine/commands/:id/status Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "status": "arrived" } ``` **Statuts acceptĂ©s:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Statut mis Ă  jour", "command_id": 1234, "new_status": "arrived" } ``` --- #### Notifier le client de descendre ```bash POST /api/v1/cabine/commands/:id/notify-client Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Client notifiĂ©", "client_username": "jean_dupont" } ``` --- #### Modifier le statut d'un article ```bash PUT /api/v1/cabine/items/:item_id/status Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "status": "prepared" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Statut mis Ă  jour", "item": { "id": 1, "command_id": 1234, "status": "prepared", "updated_at": "2025-01-18T17:15:00Z" } } ``` **Statuts disponibles:** `pending`, `prepared`, `packed`, `ready` --- #### Confirmer rĂ©ception (approbation finale) ```bash POST /api/v1/cabine/commands/:id/confirm-reception Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "RĂ©ception confirmĂ©e", "command_id": 1234, "new_status": "approved" } ``` --- #### Assigner un livreur ```bash POST /api/v1/cabine/commands/:id/assign Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "username": "john_deliveryman" } ``` --- #### Livreurs disponibles ```bash GET /api/v1/cabine/all/deliveryman Authorization: Bearer ``` --- ## 🚚 API Livreur (v1) **Toutes les routes livreur nĂ©cessitent le header:** ``` Authorization: Bearer ``` ### Livraisons #### Mes livraisons (donnĂ©es filtrĂ©es) ```bash GET /api/v1/livreur/deliveries Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "deliveries": [ { "id": 1234, "status": "assigned", "adresse": "15 Rue de la Paix, 75002 Paris", "total_prix": 25.0, "created_at": "2025-01-18T14:40:00Z", "client_info": { "nom": "Dupont", "prenom": "Jean" }, "items": [ { "produit": "Pizza Margherita", "quantite": 2, "prix": 25.0 } ], "items_count": 1, "eta": { "minutes": 25, "estimated_arrival": "15:05" } } ], "count": 1 } ``` **Note:** Les donnĂ©es sensibles comme le tĂ©lĂ©phone client sont filtrĂ©es pour les livreurs. --- #### Mettre Ă  jour le statut d'une livraison ```bash PUT /api/v1/livreur/deliveries/:id/status Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "status": "en_route", "notes": "En route vers le client", "latitude": 48.8566, "longitude": 2.3522 } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Statut mis Ă  jour", "command_id": 1234, "status": "en_route", "eta_minutes": 15, "eta_message": "ArrivĂ©e prĂ©vue dans 15 minutes" } ``` **Statuts valides pour livreur (via cet endpoint) :** - `en_route` — En route vers le client (dĂ©clenche calcul ETA + notification Telegram avec ETA) - `arrived` — ArrivĂ© Ă  destination (notifie le client de descendre) - `livre` — LivrĂ© (via validation GPS uniquement, voir endpoint `force-validate`) - `cancelled` — Annulation par le livreur > **Note :** Le passage Ă  `livre` via ce endpoint requiert des coordonnĂ©es GPS valides pour la validation de proximitĂ©. > **Amende automatique :** si le livreur annule une commande dont le statut courant est `arrived`, une amende progressive est automatiquement appliquĂ©e au client (`ApplyCancellationPenalty`) — Ă©chelle : 1Ăšre=20€, 2Ăšme=50€, 3Ăšme=100€, 4Ăšme+=150€. Le livreur peut dĂ©clencher cette annulation via le bouton **"Client absent"** (cf. section ci-dessous). --- ### Bouton "Client absent" (DashboardScreen) Lorsque le statut d'une livraison passe Ă  `arrived`, un timer de **5 minutes** dĂ©marre automatiquement dans l'app livreur (`frontend-admin`). Pendant ce dĂ©lai, un compte Ă  rebours est affichĂ© sur la carte de livraison. Une fois les 5 minutes Ă©coulĂ©es, le bouton **"Client absent"** apparaĂźt. **Comportement :** - Appui sur "Client absent" → annulation de la commande avec la raison `client_absent` - L'API reçoit `status: cancelled` + `issue_type: client_absent` - Le backend applique automatiquement `ApplyCancellationPenalty(clientUsername)` (amende progressive) - Le timer est stockĂ© dans un `useRef` (pas de re-render) et comparĂ© Ă  `Date.now()` toutes les secondes **ImplĂ©mentation :** `frontend-admin/src/screens/delivery/DashboardScreen.tsx` — constante `ABSENT_TIMEOUT_SECS = 300`, ref `arrivedAtRef`, Ă©tat `elapsedSeconds`. --- ### Position GPS #### Mettre Ă  jour ma position ```bash POST /api/v1/livreur/location/update Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "latitude": 48.8566, "longitude": 2.3522 } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Position mise Ă  jour", "location": { "latitude": 48.8566, "longitude": 2.3522, "updated_at": "2025-01-18T17:35:00Z" } } ``` --- #### Ma position actuelle ```bash GET /api/v1/livreur/location Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "location": { "latitude": 48.8566, "longitude": 2.3522, "last_update": "2025-01-18T17:35:00Z" } } ``` --- ### Statut et Queue #### Mettre Ă  jour mon statut ```bash POST /api/v1/livreur/update/status Authorization: Bearer Content-Type: application/json ``` **RequĂȘte:** ```json { "status": "available" } ``` **RĂ©ponse (200 OK):** ```json { "success": true, "message": "Statut mis Ă  jour", "status": { "username": "john_deliveryman", "status": "available", "updated_at": "2025-01-18T17:40:00Z" } } ``` **Statuts disponibles:** `available`, `busy`, `offline` --- #### Ma queue de livraisons ```bash GET /api/v1/livreur/queue Authorization: Bearer ``` **RĂ©ponse (200 OK):** ```json { "success": true, "deliveryman": "john_deliveryman", "queue_size": 3, "commands": [ { "position": 1, "command_id": 1234, "address": "15 Rue de la Paix, 75002 Paris", "estimated_eta": 12, "total_prix": 25.0 } ] } ``` --- ## đŸ—ș Systeme GPS Integre Le systeme GPS est au coeur de la plateforme de livraison. Il permet l'assignation automatique des livreurs, le calcul d'ETA en temps reel, et le suivi des livraisons. ### Architecture GPS #### Flux GPS Complet ```mermaid flowchart TD A[1. Commande creee par le Client] --> B[2. Geocodage adresse] B --> |Nominatim API| C[(Cache Redis 7 jours)] C --> D[3. Worker Auto-Assignation] D --> |Toutes les 5 min| E[4. Recherche livreur le plus proche] E --> |Haversine + TomTom| F[5. Calcul ETA avec trafic reel] F --> G[6. Assignation a la queue du livreur] G --> H[7. Livreur accepte et met a jour sa position] H --> |Redis Pub/Sub| I[8. Client/Admin recoivent les mises a jour] I --> J[9. Navigation vers destination] J --> |TomTom Routing| K[10. Livraison terminee] ``` #### Architecture des Services GPS ```mermaid graph TB subgraph Frontend MAP[TomTomMap Component] UI[Interface Utilisateur] end subgraph Backend GEO[Service Geocodage] DIST[Service Distance] ETA[Service ETA] ASSIGN[Service Auto-Assignation] end subgraph APIs Externes NOM[Nominatim API] TOM[TomTom API] end subgraph Stockage REDIS[(Redis Cache)] PG[(PostgreSQL)] end UI --> MAP MAP --> TOM GEO --> NOM GEO --> REDIS DIST --> GEO ETA --> TOM ETA --> DIST ASSIGN --> DIST ASSIGN --> ETA ASSIGN --> PG ASSIGN --> REDIS ``` ### Services GPS Utilises | Service | Utilisation | Cache | |---------|-------------|-------| | **Nominatim** (OpenStreetMap) | Geocodage d'adresses | Redis 7 jours | | **TomTom Routing API** | ETA avec trafic reel, itineraires | Non | | **Haversine** (local) | Calcul de distance a vol d'oiseau | Non | ### Configuration Requise **Variables d'environnement Backend:** ```bash TOMTOM_API_KEY= ``` **Variables d'environnement Frontend:** ```bash VITE_TOMTOM_API_KEY= ``` > **Note:** Obtenez une cle API TomTom gratuite sur [developer.tomtom.com](https://developer.tomtom.com/) --- ### Geocodage d'Adresses Le geocodage convertit une adresse textuelle en coordonnees GPS (latitude/longitude). #### Geocoder une adresse ```bash POST /api/v1/geocode Content-Type: application/json ``` **Requete:** ```json { "address": "15 Rue de la Paix, 75002 Paris, France" } ``` **Reponse (200 OK):** ```json { "success": true, "address": "15 Rue de la Paix, 75002 Paris, France", "coordinates": { "latitude": 48.8698, "longitude": 2.3311 }, "cached": false } ``` **Erreurs possibles:** - `400` - Adresse manquante ou invalide - `404` - Adresse non trouvee (geocodage echoue) --- #### Valider une adresse Verifie si une adresse peut etre geocodee sans la stocker. ```bash POST /api/v1/validate-address Content-Type: application/json ``` **Requete:** ```json { "address": "15 Rue de la Paix, 75002 Paris" } ``` **Reponse (200 OK):** ```json { "success": true, "valid": true, "address": "15 Rue de la Paix, 75002 Paris", "coordinates": { "latitude": 48.8698, "longitude": 2.3311 } } ``` --- ### Calcul de Distance et ETA #### Formule Haversine Le systeme utilise la formule Haversine pour calculer la distance a vol d'oiseau entre deux points GPS: ``` a = sinÂČ(Δlat/2) + cos(lat1) × cos(lat2) × sinÂČ(Δlon/2) c = 2 × atan2(√a, √(1-a)) distance = R × c Ou R = 6371 km (rayon de la Terre) ``` #### ETA avec Trafic (TomTom) L'ETA est calcule en utilisant l'API TomTom qui prend en compte: - Les conditions de trafic en temps reel - Les incidents routiers - Les travaux - L'heure de la journee **Rotation automatique des clĂ©s :** jusqu'Ă  3 clĂ©s TomTom peuvent ĂȘtre configurĂ©es (`TOMTOM_API_KEY`, `TOMTOM_API_KEY_1`, `TOMTOM_API_KEY_2`, `TOMTOM_API_KEY_3`). En cas de quota dĂ©passĂ© (HTTP 403/429), le systĂšme passe automatiquement Ă  la clĂ© suivante sans interruption de service. ```mermaid flowchart TD A[Demande ETA] --> B{ClĂ©s TomTom configurĂ©es?} B -->|Non| E[Calcul Haversine] B -->|Oui| C[Appel TomTom — clĂ© active] C --> Q{Quota dĂ©passĂ© 403/429?} Q -->|Oui| R{ClĂ© suivante disponible?} R -->|Oui| C R -->|Non| E Q -->|Non| D[ETA avec trafic rĂ©el] E --> F[Distance Ă  vol d'oiseau] F --> G[Vitesse moyenne 30 km/h] G --> H[ETA estimĂ©] D --> I[Retourner ETA] H --> I ``` **Fallback:** Si toutes les clĂ©s TomTom sont Ă©puisĂ©es ou indisponibles, le systĂšme utilise un calcul local base sur: - Distance Haversine - Vitesse moyenne estimee (30 km/h en ville) --- ### Auto-Assignation GPS Le systeme assigne automatiquement les commandes au livreur le plus proche. #### Processus d'Auto-Assignation ```mermaid flowchart LR A[Nouvelle Commande] --> B{Adresse geocodee?} B -->|Non| C[Geocoder adresse] C --> D B -->|Oui| D[Recuperer livreurs disponibles] D --> E[Calculer distances Haversine] E --> F[Trier par proximite] F --> G{Livreur avec capacite?} G -->|Oui| H[Calculer ETA TomTom] G -->|Non| I{Forcer assignation?} I -->|Oui| H I -->|Non| J[Commande en attente] H --> K[Assigner a la queue] K --> L[Notifier livreur] ``` #### Regles de Capacite - Chaque livreur peut avoir jusqu'a **10 commandes** dans sa queue - Si tous les livreurs sont a capacite maximale, le systeme peut forcer l'assignation - Les livreurs avec le statut `offline` ne recoivent pas de commandes #### Auto-assigner une commande specifique ```bash POST /api/v2/admin/protected/orders/:id/auto-assign Authorization: Bearer ``` **Reponse (200 OK):** ```json { "success": true, "message": "Commande assignee automatiquement", "command_id": 1234, "assigned_to": "john_deliveryman", "assignment_details": { "distance_km": 1.5, "eta_minutes": 8, "queue_position": 3, "method": "nearest_available" } } ``` --- #### Lister les livreurs par distance Obtient la liste des livreurs tries par distance depuis une adresse. ```bash POST /api/v2/admin/protected/delivery/distances Authorization: Bearer Content-Type: application/json ``` **Requete:** ```json { "address": "15 Rue de la Paix, 75002 Paris" } ``` **Reponse (200 OK):** ```json { "success": true, "address": "15 Rue de la Paix, 75002 Paris", "destination": { "latitude": 48.8698, "longitude": 2.3311 }, "delivery_persons": [ { "username": "john_deliveryman", "distance_km": 1.5, "eta_minutes": 8, "status": "available", "queue_size": 2, "location": { "latitude": 48.8566, "longitude": 2.3522 } }, { "username": "jane_delivery", "distance_km": 3.2, "eta_minutes": 15, "status": "available", "queue_size": 5, "location": { "latitude": 48.8456, "longitude": 2.3789 } } ] } ``` --- ### Liens de Navigation Le systeme genere des liens vers plusieurs applications de cartographie. #### Obtenir les liens carte d'un livreur ```bash GET /api/v2/admin/protected/delivery-persons/:username/map-links Authorization: Bearer ``` **Reponse (200 OK):** ```json { "success": true, "deliveryman": "john_deliveryman", "location": { "latitude": 48.8566, "longitude": 2.3522 }, "map_links": { "google_maps": "https://www.google.com/maps?q=48.8566,2.3522", "google_maps_app": "comgooglemaps://?center=48.8566,2.3522", "waze": "https://www.waze.com/ul?ll=48.8566,2.3522&navigate=yes", "waze_app": "waze://?ll=48.8566,2.3522&navigate=yes", "apple_maps": "https://maps.apple.com/?ll=48.8566,2.3522", "openstreetmap": "https://www.openstreetmap.org/?mlat=48.8566&mlon=2.3522", "bing_maps": "https://www.bing.com/maps?cp=48.8566~2.3522", "here_maps": "https://share.here.com/l/48.8566,2.3522" } } ``` --- #### Obtenir les liens de navigation pour une commande Genere des liens de navigation depuis la position du livreur vers la destination de livraison. ```bash GET /api/v2/admin/protected/commands/:id/navigation-links Authorization: Bearer ``` **Reponse (200 OK):** ```json { "success": true, "command_id": 1234, "origin": { "latitude": 48.8566, "longitude": 2.3522, "description": "Position du livreur" }, "destination": { "latitude": 48.8698, "longitude": 2.3311, "address": "15 Rue de la Paix, 75002 Paris" }, "navigation_links": { "google_maps": "https://www.google.com/maps/dir/48.8566,2.3522/48.8698,2.3311", "waze": "https://www.waze.com/ul?ll=48.8698,2.3311&navigate=yes&from=48.8566,2.3522", "apple_maps": "https://maps.apple.com/?saddr=48.8566,2.3522&daddr=48.8698,2.3311" } } ``` --- ### Gestion des Positions en Temps Reel #### Flux de Mise a Jour de Position ```mermaid sequenceDiagram participant L as Livreur (App) participant API as Backend API participant R as Redis participant PS as Redis Pub/Sub participant A as Admin/Client L->>API: POST /location/update {lat, lng} API->>API: Valider coordonnees API->>R: SET delivery:location:{username} API->>PS: PUBLISH position_update PS-->>A: Notification temps reel API-->>L: 200 OK {location, updated_at} ``` #### Mise a jour de position (Livreur) ```bash POST /api/v1/livreur/location/update Authorization: Bearer Content-Type: application/json ``` **Requete:** ```json { "latitude": 48.8566, "longitude": 2.3522 } ``` **Validation des coordonnees:** - Latitude: entre -90 et +90 - Longitude: entre -180 et +180 **Reponse (200 OK):** ```json { "success": true, "message": "Position mise a jour", "location": { "latitude": 48.8566, "longitude": 2.3522, "updated_at": "2025-01-18T17:35:00Z" } } ``` **Erreurs possibles:** - `400` - Coordonnees invalides (hors limites) - `401` - Non authentifie --- ### Stockage Redis des Positions Les positions GPS sont stockees dans Redis pour un acces rapide: ```mermaid graph LR subgraph Redis Cache A[delivery:location:username
TTL: 2h] B[delivery:status:username
TTL: 1h] C[geocode:cache:address_hash
TTL: 7j] D[command:destination:id
TTL: 4h] end subgraph Donnees A --> A1[latitude, longitude, updated_at] B --> B1[status, latitude, longitude] C --> C1[latitude, longitude, address] D --> D1[dest_lat, dest_lng] end ``` | `command:destination:{command_id}` | Coordonnees destination | 4 heures | --- ### Composant Carte Frontend (TomTomMap) Le frontend inclut un composant React/TypeScript pour afficher la carte interactive. #### Fonctionnalites du Composant - **Carte interactive** avec marqueurs livreur/destination - **Calcul d'itineraire** en temps reel - **Instructions de navigation** (tourner a gauche, a droite, rond-point, etc.) - **Affichage distance/duree** du trajet - **Mode 3D** avec orientation selon la direction - **Support francais** pour toutes les instructions #### Exemple d'Utilisation ```tsx import TomTomMap from './components/TomTomMap'; function DeliveryTracking() { return ( ); } ``` #### Props du Composant | Prop | Type | Description | |------|------|-------------| | `driverPosition` | `{lat: number, lng: number}` | Position du livreur | | `destinationPosition` | `{lat: number, lng: number}` | Destination de livraison | | `showRoute` | `boolean` | Afficher l'itineraire | | `showInstructions` | `boolean` | Afficher le panneau d'instructions | | `onEtaUpdate` | `(eta: number) => void` | Callback quand l'ETA change | --- ### Worker d'Auto-Assignation Un worker CRON s'execute automatiquement pour assigner les commandes en attente. #### Configuration - **Frequence:** Toutes les 1 minute - **Fichier:** `backend/gestion/workers/cron_auto_assign.go` #### Processus du Worker ```mermaid flowchart TD START((Demarrage CRON)) --> A[Recuperer commandes pending] A --> B{Commandes a traiter?} B -->|Non| END((Fin)) B -->|Oui| C[Prendre commande suivante] C --> D{Adresse geocodee?} D -->|Non| E[Geocoder via Nominatim] E --> F D -->|Oui| F[Recuperer livreurs disponibles] F --> G[Calculer distances] G --> H[Selectionner le plus proche] H --> I{Capacite disponible?} I -->|Non| J[Marquer pour retry] I -->|Oui| K[Calculer ETA TomTom] K --> L[Assigner commande] L --> M[Mettre a jour statut] M --> N{Autres commandes?} J --> N N -->|Oui| C N -->|Non| O[Logger resultats] O --> END ``` #### Logs d'Exemple ``` [AUTO-ASSIGN] Processing 5 pending orders [AUTO-ASSIGN] Order #1234: Geocoded to (48.8698, 2.3311) [AUTO-ASSIGN] Order #1234: Nearest driver is john_deliveryman (1.5 km) [AUTO-ASSIGN] Order #1234: ETA calculated: 8 minutes [AUTO-ASSIGN] Order #1234: Assigned successfully [AUTO-ASSIGN] Completed: 5 assigned, 0 failed ``` --- ### Cycle de Vie d'une Livraison (GPS) ```mermaid stateDiagram-v2 [*] --> pending: Commande creee (checkout) pending --> assigned: Auto-assignation GPS / Admin pending --> cancelled: Annulation client ou admin assigned --> en_route: Livreur demarre (start) assigned --> cancelled: Annulation en_route --> arrived: Livreur arrive (GPS ou Admin/Cabine) en_route --> en_route: Mise a jour position GPS en_route --> livre: Validation GPS livreur / Admin arrived --> livre: Remise au client (validation GPS livreur) arrived --> approved: Admin/Cabine force approbation livre --> approved: Client confirme / Admin / Cabine approved --> [*] cancelled --> [*] note right of arrived Declenche par : - Livreur PUT status=arrived - Admin/Cabine bouton "Le livreur est la" Notifie le client (Telegram + push) Timer 5min → bouton "Client absent" Annulation depuis arrived → amende client end note note right of en_route ETA calcule et stocke dans Redis (command:eta:{id}) Utilise pour les notifications Telegram end note ``` --- ### Gestion des Erreurs GPS #### Geocodage Echoue Si une adresse ne peut pas etre geocodee: - La commande reste en statut `pending` - Un log d'erreur est genere - L'admin peut corriger l'adresse manuellement #### API TomTom Indisponible / Quota dĂ©passĂ© Le systĂšme tente d'abord toutes les clĂ©s disponibles en rotation, puis bascule sur le calcul local : - Utilise la formule Haversine pour la distance - Estime l'ETA avec une vitesse moyenne de 30 km/h - Un flag `fallback_used: true` est ajoute a la reponse --- ### Bonnes Pratiques #### Pour les Livreurs 1. **Mettre a jour la position** frequemment (toutes les 30 secondes recommande) 2. **Verifier le statut** avant de commencer une livraison 3. **Utiliser les liens de navigation** generes par l'API #### Pour les Admins 1. **Surveiller les queues** des livreurs pour eviter la surcharge 2. **Verifier les adresses** qui echouent au geocodage 3. **Utiliser l'endpoint distances** pour l'assignation manuelle si necessaire #### Pour les Developpeurs 1. **Toujours valider** les coordonnees avant stockage 2. **Utiliser le cache** Redis pour les adresses frequentes 3. **Implementer un fallback** si TomTom est indisponible 4. **Logger les erreurs** de geocodage pour analyse --- ## ❌ Codes d'Erreur ### Erreurs HTTP Standards | Code | Signification | Description | |------|---------------|-------------| | 200 | OK | SuccĂšs | | 201 | Created | Ressource créée | | 400 | Bad Request | DonnĂ©es invalides | | 401 | Unauthorized | Non authentifiĂ© | | 403 | Forbidden | Non autorisĂ© | | 404 | Not Found | Ressource introuvable | | 409 | Conflict | Conflit (username dĂ©jĂ  pris) | | 422 | Unprocessable Entity | Validation Ă©chouĂ©e | | 500 | Internal Server Error | Erreur serveur | ### Exemples d'Erreurs #### 400 - Bad Request ```json { "error": "DonnĂ©es invalides", "details": "Le champ 'username' est requis" } ``` #### 401 - Unauthorized ```json { "error": "Token invalide ou expirĂ©", "message": "Veuillez vous reconnecter" } ``` #### 403 - Forbidden ```json { "error": "AccĂšs refusĂ©", "message": "Vous n'avez pas les permissions nĂ©cessaires" } ``` #### 403 - Checkout bloquĂ© (amende en attente) ```json { "error": "Commande bloquĂ©e : vous avez une amende de 20€ en attente de paiement. Prenez attache avec Milieu Nantais sur signal pour rĂ©gulariser votre situation..", "amende": 20.0, "blocked": true } ``` #### 404 - Not Found ```json { "error": "Ressource introuvable", "message": "Commande avec l'ID 9999 introuvable" } ``` #### 409 - Conflict ```json { "error": "Conflit", "message": "Ce nom d'utilisateur est dĂ©jĂ  pris" } ``` #### 422 - Unprocessable Entity ```json { "error": "Validation Ă©chouĂ©e", "details": { "field": "telephone", "message": "Format de tĂ©lĂ©phone invalide" } } ``` #### 500 - Internal Server Error ```json { "error": "Erreur serveur interne", "message": "Une erreur inattendue s'est produite" } ``` --- ## 📝 Notes Importantes ### SĂ©curitĂ© - **Tokens JWT** : Expiration variable selon le rĂŽle (Client: 5h · Admin/Livreur/Cabine: 10h) - **Validation GPS** : Requis pour certaines actions de livraison - **Filtrage des donnĂ©es** : Les livreurs n'ont pas accĂšs aux tĂ©lĂ©phones clients - **Rate Limiting** : Protection contre les abus ### Format des DonnĂ©es - **Dates** : Format ISO 8601 (`2025-01-18T14:30:00Z`) - **CoordonnĂ©es** : Latitude/Longitude en dĂ©cimal (WGS84) - **Prix** : En euros avec 2 dĂ©cimales - **TĂ©lĂ©phones** : Format international (`+33612345678`) ### Workers Automatiques - **Auto-Assignment Cron** : Toutes les 1 minute (`workers/cron_auto_assign.go`) - **Queue Cleanup** : Toutes les 5 minutes - **ETA Updates** : Toutes les 30 secondes - **Stock Cleanup** : Toutes les 5 minutes ### FonctionnalitĂ©s AvancĂ©es - **Assignation GPS automatique** au checkout - **Calcul ETA temps rĂ©el** avec TomTom API - **SystĂšme de queues optimisĂ©** pour les livreurs - **Nettoyage automatique** des commandes invalides - **Notifications temps rĂ©el** via Redis Pub/Sub --- ## 📋 Changelog ### v5.4.0 — 2026-05-18 - **Rotation automatique des clĂ©s TomTom** : jusqu'Ă  3 clĂ©s configurables (`TOMTOM_API_KEY_1/2/3`). En cas de quota dĂ©passĂ© (403/429), le systĂšme passe Ă  la clĂ© suivante automatiquement sans interruption. Fallback Haversine si toutes les clĂ©s sont Ă©puisĂ©es. - **SĂ©curitĂ© — crĂ©ation d'utilisateurs** : seul un `admin` peut crĂ©er des comptes `livreur` ou `cabine` via l'API. La crĂ©ation de compte `admin` est entiĂšrement bloquĂ©e via l'application — uniquement possible en base de donnĂ©es directement. - **SĂ©curitĂ© — cabine** : le rĂŽle `cabine` n'a plus aucun droit de crĂ©ation d'utilisateurs ou de clients (retirĂ© cĂŽtĂ© backend). - **Fix frontend** : `createUserByAdmin` appelait `/admin/auth/register` (inexistant) → corrigĂ© vers `/admin/protected/users`. - **WAF ModSecurity logs** : les logs nginx et l'audit log ModSecurity sont dĂ©sormais montĂ©s sur l'hĂŽte (`/var/log/waf/nginx/` et `/var/log/waf/modsec/`) via volumes Docker stables. La variable `MODSEC_AUDIT_LOG` redirige l'audit log vers un fichier (au lieu de stdout) pour collecte Wazuh. - **Notifications Telegram uniquement** : les push notifications Expo (iOS/Android) sont abandonnĂ©es. Clients et livreurs reçoivent dĂ©sormais toutes leurs alertes via Telegram. ### v5.3.0 — 2026-05-15 - **Prix inactifs filtrĂ©s cĂŽtĂ© client** : le flag `active_price` sur `PRODUCT_PRICES` permet de dĂ©sactiver un tarif sans le supprimer. Les endpoints publics/client et les frontends web+mobile masquent automatiquement les prix inactifs. Admin et cabine voient tous les prix. - **DĂ©sactivation au lieu de suppression** : dans la modal d'Ă©dition produit (admin), retirer un prix existant le dĂ©sactive (`active_price = false`) plutĂŽt que de le supprimer de la base. - **Amende automatique client — livreur annule depuis `arrived`** : quand le livreur marque le client absent (`arrived` → `cancelled`), `ApplyCancellationPenalty` est appelĂ© automatiquement sur le **client** (amende progressive : 20→50→100→150€). - **Bouton "Client absent"** : aprĂšs 5 minutes au statut `arrived`, l'app livreur affiche un bouton "Client absent" qui dĂ©clenche l'annulation avec amende sur le client. - **Message d'erreur checkout avec contact** : le message de blocage inclut dĂ©sormais "Prenez attache avec Milieu Nantais sur signal pour rĂ©gulariser votre situation." - **OTA channels par rĂŽle** : les canaux Expo OTA sont dĂ©sormais nommĂ©s `pre-prod-client` / `production-client` / `pre-prod-admin` / `production-admin` pour Ă©viter les mises Ă  jour croisĂ©es entre builds. ### v5.2.0 — 2026-05-01 - Activation/dĂ©sactivation des prix par quantitĂ© (admin) - Gestion de stock amĂ©liorĂ©e - Refactoring handlers produits --- **Documentation mise Ă  jour le :** 2026-05-18 **Version API :** 5.4.0 **Technologies :** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19, Expo 54, TomTom API, ModSecurity WAF **DĂ©ploiement :** Docker Compose · Nginx + ModSecurity OWASP CRS · TLS 1.2/1.3 **Base URL prod :** `https://mln-uber.club`