# Cahier des Charges — API & Base de Données ## Projet : Laverie Connectée — Backend **Version :** 2.0 **Stack principale :** Laravel 11, MySQL 8, Redis, Docker **Positionnement :** V1 démontrable, extensible vers V2 sans refonte majeure --- ## 1. Contexte & Objectifs ### 1.1 Contexte Développement d'une API centrale servant : - une application utilisateur multi-plateforme, - un back-office exploitant, - une couche d'intégration avec les systèmes machines fournis par le client ou ses partenaires techniques. L'API est le **point d'entrée unique de la logique métier**. Les clients applicatifs ne communiquent jamais directement avec la base de données. ### 1.2 Objectifs V1 - authentifier les utilisateurs finaux et les exploitants, - gérer plusieurs laveries et plusieurs exploitants dans une même plateforme, - exposer les données établissements / machines / disponibilités, - gérer un porte-monnaie électronique avec traçabilité, - permettre la réservation d'un créneau, - permettre le démarrage d'un lavage via intégration machine, - historiser les événements machines, - fournir un socle d'audit, de sécurité et de statistiques simples. ### 1.3 Principes de conception - **MVP strict** : seules les fonctionnalités nécessaires à la démonstration et à la première mise en service sont incluses en V1. - **Extensibilité** : fidélité, abonnements, parrainage, campagnes marketing et analytics avancées sont prévues mais non implémentées en V1. - **Traçabilité** : tout flux critique doit être rejouable et auditable. - **Isolation métier** : chaque exploitant ne voit que ses établissements, ses machines et ses chiffres. - **Intégration externe souple** : la couche machine doit supporter aussi bien un modèle où notre système appelle une API partenaire qu'un modèle où le partenaire pousse des événements vers notre API. --- ## 2. Périmètre fonctionnel ### 2.1 MVP V1 #### Utilisateur final - inscription / connexion, - consultation des laveries, - consultation des machines et de leur statut, - consultation du solde wallet, - historique simple des transactions, - rechargement du wallet, - réservation d'un créneau, - démarrage d'un lavage, - historique des lavages, - notifications transactionnelles. #### Exploitant - connexion superviseur, - consultation du parc machines, - consultation d'un tableau de bord simple, - consultation des réservations et lavages, - gestion simple de la tarification, - visualisation des alertes techniques de base. #### Plateforme - multi-exploitants, - audit logs, - agrégats journaliers simples, - intégration machine simulable pour démonstration. ### 2.2 V2 prévue - fidélité, - parrainage, - abonnements, - campagnes marketing, - promotions avancées, - exports RGPD complets, - analytics détaillées, - règles tarifaires complexes, - exports métier avancés. ### 2.3 Hors périmètre V1 - chat, - avis, - FAQ dynamique, - recommandation de cycle par photo, - moteur prédictif / deep learning, - orchestration matérielle bas niveau. --- ## 3. Stack Technique | Composant | Technologie | Version | Commentaire | |-----------|-------------|---------|-------------| | Framework API | Laravel | 11.x | Coeur métier principal | | Base de données | MySQL | 8.0 | Référentiel transactionnel | | Cache / queues | Redis | 7.x | Queues, verrous applicatifs, cache court | | Workers | Laravel Horizon | 5.x | Supervision des jobs | | Scheduler | Laravel Scheduler | natif | Cron applicatif | | Documentation API | Scramble ou L5-Swagger | — | OpenAPI | | Conteneurisation | Docker | — | Environnements reproductibles | | CI/CD | GitHub Actions | — | Tests + build | | Monitoring erreurs | Sentry | — | Backend | | Logs | JSON structurés | — | Corrélation et exploitation | ### 3.1 Authentification recommandée - **App mobile / web utilisateur** : access token court + refresh token. - **Back-office exploitant** : session cookie Laravel. - **Intégrations machines / partenaires** : API key technique ou signature HMAC. Cette séparation évite de mélanger les contraintes des apps utilisateurs, du back-office et des systèmes techniques. --- ## 4. Architecture logique ### 4.1 Domaines métier principaux - Authentification - Organisations / exploitants - Établissements - Machines - Tarification - Wallet & paiements - Réservations - Lavages - Notifications - Intégrations machines - Audit & statistiques ### 4.2 Multi-tenant logique Le système n'est pas multi-base ni multi-schémas. L'isolation est assurée au niveau applicatif par : - `organization_id`, - policies Laravel, - query scopes explicites, - ressources d'API filtrées selon l'utilisateur connecté. ### 4.3 Structure recommandée Laravel ``` app/ ├── Domain/ │ ├── Auth/ │ ├── Organization/ │ ├── Machine/ │ ├── Wallet/ │ ├── Booking/ │ ├── Wash/ │ ├── Pricing/ │ ├── Notification/ │ └── Integration/ ├── Http/ │ ├── Controllers/ │ ├── Middleware/ │ └── Requests/ ├── Jobs/ ├── Events/ ├── Listeners/ ├── Policies/ ├── Models/ └── Support/ ``` --- ## 5. Base de Données — Schéma révisé ## 5.0 Connexion bdd de test hostname: localhost port: 3306 user: root password: shema: laverie ## 5.1 Exploitants et périmètre ### Table `organizations` ```sql CREATE TABLE organizations ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, name VARCHAR(200) NOT NULL, code VARCHAR(50) UNIQUE NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL ); ``` ### Table `establishments` ```sql CREATE TABLE establishments ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT UNSIGNED NOT NULL, uuid CHAR(36) UNIQUE NOT NULL, name VARCHAR(200) NOT NULL, address TEXT NOT NULL, city VARCHAR(100) NULL, zip_code VARCHAR(10) NULL, latitude DECIMAL(10,8) NULL, longitude DECIMAL(11,8) NULL, timezone VARCHAR(50) NOT NULL DEFAULT 'Europe/Paris', is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_establishments_organization FOREIGN KEY (organization_id) REFERENCES organizations(id) ); ``` ### Table `supervisors` ```sql CREATE TABLE supervisors ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT UNSIGNED NOT NULL, establishment_id BIGINT UNSIGNED NULL, uuid CHAR(36) UNIQUE NOT NULL, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, role ENUM('platform_admin','owner','manager','viewer') NOT NULL DEFAULT 'manager', is_active BOOLEAN NOT NULL DEFAULT TRUE, last_login_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_supervisors_organization FOREIGN KEY (organization_id) REFERENCES organizations(id), CONSTRAINT fk_supervisors_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id) ); ``` ## 5.2 Utilisateurs finaux ### Table `users` ```sql CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, phone VARCHAR(20) UNIQUE NULL, birthdate DATE NULL, email_verified_at TIMESTAMP NULL, password VARCHAR(255) NOT NULL, locale VARCHAR(10) NOT NULL DEFAULT 'fr', is_active BOOLEAN NOT NULL DEFAULT TRUE, anonymized_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL ); ``` ### Table `gdpr_consents` ```sql CREATE TABLE gdpr_consents ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, type ENUM('data_processing','marketing','analytics') NOT NULL, accepted BOOLEAN NOT NULL, policy_version VARCHAR(50) NOT NULL, policy_text_hash VARCHAR(255) NOT NULL, source ENUM('mobile','web','backoffice') NOT NULL, consent_language VARCHAR(10) NOT NULL DEFAULT 'fr', ip_address VARCHAR(45) NULL, user_agent TEXT NULL, accepted_at TIMESTAMP NOT NULL, revoked_at TIMESTAMP NULL, CONSTRAINT fk_gdpr_consents_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ### Table `user_devices` ```sql CREATE TABLE user_devices ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, platform ENUM('android','ios','web') NOT NULL, push_token VARCHAR(255) NOT NULL, app_version VARCHAR(50) NULL, device_name VARCHAR(100) NULL, last_seen_at TIMESTAMP NULL, revoked_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_user_device_token (push_token), CONSTRAINT fk_user_devices_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ### Table `notification_preferences` ```sql CREATE TABLE notification_preferences ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, transaction_enabled BOOLEAN NOT NULL DEFAULT TRUE, reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE, marketing_enabled BOOLEAN NOT NULL DEFAULT FALSE, system_enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_notification_preferences_user (user_id), CONSTRAINT fk_notification_preferences_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ## 5.3 Machines et intégrations ### Table `machines` ```sql CREATE TABLE machines ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, establishment_id BIGINT UNSIGNED NOT NULL, uuid CHAR(36) UNIQUE NOT NULL, name VARCHAR(100) NOT NULL, type ENUM('washer_small','washer_large','dryer_small','dryer_large') NOT NULL, qr_code VARCHAR(255) UNIQUE NOT NULL, status ENUM('available','reserved','running','maintenance','offline','error') NOT NULL DEFAULT 'available', current_user_id BIGINT UNSIGNED NULL, cycle_started_at TIMESTAMP NULL, cycle_ends_at TIMESTAMP NULL, last_heartbeat_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_machines_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id), CONSTRAINT fk_machines_current_user FOREIGN KEY (current_user_id) REFERENCES users(id) ); ``` ### Table `machine_integrations` ```sql CREATE TABLE machine_integrations ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, machine_id BIGINT UNSIGNED NOT NULL, provider VARCHAR(100) NOT NULL, external_machine_id VARCHAR(100) NOT NULL, external_site_id VARCHAR(100) NULL, mode ENUM('pull','push','hybrid','simulated') NOT NULL DEFAULT 'simulated', config JSON NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_machine_provider_external (provider, external_machine_id), CONSTRAINT fk_machine_integrations_machine FOREIGN KEY (machine_id) REFERENCES machines(id) ); ``` ### Table `machine_commands` ```sql CREATE TABLE machine_commands ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, machine_id BIGINT UNSIGNED NOT NULL, provider VARCHAR(100) NOT NULL, command_type ENUM('start_cycle','stop_cycle','refresh_status') NOT NULL, payload JSON NULL, status ENUM('pending','sent','acknowledged','failed','timeout','cancelled') NOT NULL DEFAULT 'pending', external_reference VARCHAR(255) NULL, correlation_id VARCHAR(255) NULL, requested_by_user_id BIGINT UNSIGNED NULL, requested_by_supervisor_id BIGINT UNSIGNED NULL, sent_at TIMESTAMP NULL, responded_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_machine_commands_machine FOREIGN KEY (machine_id) REFERENCES machines(id), CONSTRAINT fk_machine_commands_user FOREIGN KEY (requested_by_user_id) REFERENCES users(id), CONSTRAINT fk_machine_commands_supervisor FOREIGN KEY (requested_by_supervisor_id) REFERENCES supervisors(id) ); ``` ### Table `machine_events` ```sql CREATE TABLE machine_events ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, machine_id BIGINT UNSIGNED NOT NULL, provider VARCHAR(100) NOT NULL, external_event_id VARCHAR(255) NULL, event_type ENUM('heartbeat','machine_online','machine_offline','cycle_started','cycle_completed','cycle_failed','error_reported','status_changed') NOT NULL, payload JSON NULL, occurred_at TIMESTAMP NOT NULL, received_at TIMESTAMP NOT NULL, processed_at TIMESTAMP NULL, processing_status ENUM('pending','processed','failed','ignored') NOT NULL DEFAULT 'pending', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_machine_events_machine FOREIGN KEY (machine_id) REFERENCES machines(id) ); ``` ### Table `machine_status_history` ```sql CREATE TABLE machine_status_history ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, machine_id BIGINT UNSIGNED NOT NULL, previous_status VARCHAR(50) NULL, new_status VARCHAR(50) NOT NULL, source VARCHAR(100) NOT NULL, reason VARCHAR(255) NULL, created_at TIMESTAMP NULL, CONSTRAINT fk_machine_status_history_machine FOREIGN KEY (machine_id) REFERENCES machines(id) ); ``` ## 5.4 Tarification ### Table `pricing_rules` ```sql CREATE TABLE pricing_rules ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, establishment_id BIGINT UNSIGNED NOT NULL, machine_id BIGINT UNSIGNED NULL, machine_type ENUM('washer_small','washer_large','dryer_small','dryer_large') NULL, day_type ENUM('weekday','weekend','holiday','all') NOT NULL DEFAULT 'all', slot_start TIME NOT NULL, slot_end TIME NOT NULL, price DECIMAL(8,2) NOT NULL, label VARCHAR(100) NULL, requires_app BOOLEAN NOT NULL DEFAULT FALSE, priority SMALLINT NOT NULL DEFAULT 100, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_pricing_rules_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id), CONSTRAINT fk_pricing_rules_machine FOREIGN KEY (machine_id) REFERENCES machines(id) ); ``` ### Table `promotions` ```sql CREATE TABLE promotions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, establishment_id BIGINT UNSIGNED NOT NULL, machine_type ENUM('washer_small','washer_large','dryer_small','dryer_large','all') NOT NULL DEFAULT 'all', discount_type ENUM('percent','fixed') NOT NULL, discount_value DECIMAL(8,2) NOT NULL, starts_at TIMESTAMP NOT NULL, ends_at TIMESTAMP NOT NULL, description TEXT NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_promotions_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id) ); ``` ## 5.5 Wallet et paiements ### Table `wallets` ```sql CREATE TABLE wallets ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, currency CHAR(3) NOT NULL DEFAULT 'EUR', current_balance DECIMAL(10,2) NOT NULL DEFAULT 0.00, status ENUM('active','blocked','closed') NOT NULL DEFAULT 'active', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_wallets_user (user_id), CONSTRAINT fk_wallets_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ### Table `payment_transactions` ```sql CREATE TABLE payment_transactions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, user_id BIGINT UNSIGNED NOT NULL, provider VARCHAR(50) NOT NULL, provider_payment_id VARCHAR(255) NULL, amount DECIMAL(10,2) NOT NULL, currency CHAR(3) NOT NULL DEFAULT 'EUR', status ENUM('initiated','pending','succeeded','failed','cancelled','refunded') NOT NULL DEFAULT 'initiated', idempotency_key VARCHAR(100) NOT NULL, return_url VARCHAR(255) NULL, raw_payload JSON NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_payment_idempotency (idempotency_key), CONSTRAINT fk_payment_transactions_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ### Table `wallet_transactions` ```sql CREATE TABLE wallet_transactions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, wallet_id BIGINT UNSIGNED NOT NULL, type ENUM('credit','debit','hold','release','refund','adjustment') NOT NULL, amount DECIMAL(10,2) NOT NULL, balance_before DECIMAL(10,2) NOT NULL, balance_after DECIMAL(10,2) NOT NULL, source_type VARCHAR(100) NOT NULL, source_id BIGINT UNSIGNED NULL, idempotency_key VARCHAR(100) NULL, metadata JSON NULL, created_at TIMESTAMP NULL, CONSTRAINT fk_wallet_transactions_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ); ``` ## 5.6 Réservations et lavages ### Table `bookings` ```sql CREATE TABLE bookings ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, user_id BIGINT UNSIGNED NOT NULL, machine_id BIGINT UNSIGNED NOT NULL, slot_start TIMESTAMP NOT NULL, slot_end TIMESTAMP NOT NULL, booking_fee DECIMAL(8,2) NOT NULL DEFAULT 0.00, reserved_amount DECIMAL(8,2) NOT NULL DEFAULT 0.00, penalty_amount DECIMAL(8,2) NOT NULL DEFAULT 0.00, status ENUM('pending','confirmed','cancelled','expired','active','completed','no_show') NOT NULL DEFAULT 'pending', cancelled_at TIMESTAMP NULL, penalty_applied_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_bookings_user FOREIGN KEY (user_id) REFERENCES users(id), CONSTRAINT fk_bookings_machine FOREIGN KEY (machine_id) REFERENCES machines(id) ); ``` ### Table `washes` ```sql CREATE TABLE washes ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, uuid CHAR(36) UNIQUE NOT NULL, user_id BIGINT UNSIGNED NOT NULL, machine_id BIGINT UNSIGNED NOT NULL, booking_id BIGINT UNSIGNED NULL, machine_command_id BIGINT UNSIGNED NULL, trigger_method ENUM('qr_code','booking','supervisor','system') NOT NULL, status ENUM('pending_start','running','completed','failed','cancelled') NOT NULL DEFAULT 'pending_start', program VARCHAR(100) NULL, started_at TIMESTAMP NULL, ended_at TIMESTAMP NULL, duration_minutes INT UNSIGNED NULL, cost DECIMAL(8,2) NOT NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_washes_user FOREIGN KEY (user_id) REFERENCES users(id), CONSTRAINT fk_washes_machine FOREIGN KEY (machine_id) REFERENCES machines(id), CONSTRAINT fk_washes_booking FOREIGN KEY (booking_id) REFERENCES bookings(id), CONSTRAINT fk_washes_command FOREIGN KEY (machine_command_id) REFERENCES machine_commands(id) ); ``` ## 5.7 Notifications, audit et statistiques ### Table `push_notifications` ```sql CREATE TABLE push_notifications ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NULL, type VARCHAR(100) NOT NULL, title VARCHAR(255) NOT NULL, body TEXT NOT NULL, data JSON NULL, status ENUM('pending','sent','failed') NOT NULL DEFAULT 'pending', sent_at TIMESTAMP NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, CONSTRAINT fk_push_notifications_user FOREIGN KEY (user_id) REFERENCES users(id) ); ``` ### Table `audit_logs` ```sql CREATE TABLE audit_logs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, actor_type VARCHAR(50) NOT NULL, actor_id BIGINT UNSIGNED NOT NULL, organization_id BIGINT UNSIGNED NULL, establishment_id BIGINT UNSIGNED NULL, action VARCHAR(100) NOT NULL, target_type VARCHAR(100) NOT NULL, target_id BIGINT UNSIGNED NULL, before_data JSON NULL, after_data JSON NULL, ip_address VARCHAR(45) NULL, created_at TIMESTAMP NULL ); ``` ### Table `daily_establishment_stats` ```sql CREATE TABLE daily_establishment_stats ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, establishment_id BIGINT UNSIGNED NOT NULL, stat_date DATE NOT NULL, total_washes INT UNSIGNED NOT NULL DEFAULT 0, total_revenue DECIMAL(10,2) NOT NULL DEFAULT 0.00, bookings_count INT UNSIGNED NOT NULL DEFAULT 0, no_show_count INT UNSIGNED NOT NULL DEFAULT 0, top_up_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, occupancy_rate DECIMAL(5,2) NOT NULL DEFAULT 0.00, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, UNIQUE KEY uniq_daily_establishment_stats (establishment_id, stat_date), CONSTRAINT fk_daily_establishment_stats_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id) ); ``` --- ## 6. Contrat d'intégration machine ### 6.1 Principe Le backend doit supporter deux scénarios : - **pull** : notre système appelle une API partenaire pour envoyer une commande ou lire un statut, - **push** : le partenaire pousse des événements vers notre système, - **hybrid** : combinaison des deux, - **simulated** : mode démonstration / sandbox. ### 6.2 Abstraction applicative ```php interface MachineProviderInterface { public function startCycle(Machine $machine, array $payload): MachineCommandResult; public function stopCycle(Machine $machine, array $payload = []): MachineCommandResult; public function refreshStatus(Machine $machine): MachineStatusSnapshot; public function handleInboundEvent(array $payload): void; } ``` ### 6.3 Événements entrants attendus - `heartbeat` - `machine_online` - `machine_offline` - `cycle_started` - `cycle_completed` - `cycle_failed` - `error_reported` - `status_changed` ### 6.4 Endpoints d'intégration proposés | Méthode | Endpoint | Usage | |---------|----------|-------| | POST | `/api/v1/integrations/machines/events` | Réception d'événements machine | | POST | `/api/v1/integrations/machines/heartbeat` | Heartbeat passerelle / machine | | POST | `/api/v1/integrations/machines/commands/{uuid}/ack` | Accusé de réception optionnel | ### 6.5 Payload minimal d'événement ```json { "provider": "client_gateway", "event_type": "cycle_completed", "occurred_at": "2026-06-27T10:15:00Z", "machine_external_id": "MACH-001", "correlation_id": "cmd_12345", "data": { "status": "available", "duration_minutes": 42, "error_code": null } } ``` ### 6.6 Exigences minimales côté partenaire - identifiant machine externe stable, - horodatage fiable, - type d'événement, - état courant de la machine, - identifiant de corrélation si une commande a été envoyée par notre système, - stratégie d'authentification technique, - documentation des erreurs métiers. ### 6.7 Mode démonstration Un provider simulé doit permettre : - démarrage immédiat d'un cycle, - passage en statut `running`, - émission d'un faux événement `cycle_completed` après un délai configurable, - retour en statut `available`. Ce mode permet une démo sans matériel réel. --- ## 7. Endpoints API — V1 ### 7.1 Auth utilisateur | Méthode | Endpoint | Description | |---------|----------|-------------| | POST | `/api/v1/auth/register` | Inscription | | POST | `/api/v1/auth/login` | Connexion utilisateur | | POST | `/api/v1/auth/refresh` | Renouvellement token | | POST | `/api/v1/auth/logout` | Déconnexion | | GET | `/api/v1/auth/me` | Profil courant | ### 7.2 Wallet | Méthode | Endpoint | Description | |---------|----------|-------------| | GET | `/api/v1/wallet` | Solde | | GET | `/api/v1/wallet/transactions` | Historique | | POST | `/api/v1/wallet/top-up/initiate` | Démarrer un rechargement | | POST | `/api/v1/wallet/top-up/confirm` | Confirmation de rechargement | | POST | `/api/v1/wallet/top-up/webhook/{provider}` | Webhook PSP | ### 7.3 Établissements & machines | Méthode | Endpoint | Description | |---------|----------|-------------| | GET | `/api/v1/establishments` | Liste établissements | | GET | `/api/v1/establishments/{uuid}` | Détail établissement | | GET | `/api/v1/machines/{uuid}` | Détail machine | | GET | `/api/v1/machines/{uuid}/availability` | Créneaux disponibles | | GET | `/api/v1/machines/{uuid}/pricing` | Tarif actif | ### 7.4 Réservations | Méthode | Endpoint | Description | |---------|----------|-------------| | POST | `/api/v1/bookings` | Créer une réservation | | GET | `/api/v1/bookings` | Mes réservations | | GET | `/api/v1/bookings/{uuid}` | Détail réservation | | PATCH | `/api/v1/bookings/{uuid}/cancel` | Annuler | | PATCH | `/api/v1/bookings/{uuid}/move` | Déplacer | ### 7.5 Lavages | Méthode | Endpoint | Description | |---------|----------|-------------| | POST | `/api/v1/washes/start` | Démarrer un lavage | | GET | `/api/v1/washes` | Historique | | GET | `/api/v1/washes/{uuid}` | Détail | ### 7.6 Superviseur | Méthode | Endpoint | Description | |---------|----------|-------------| | POST | `/api/v1/supervisor/auth/login` | Connexion superviseur | | POST | `/api/v1/supervisor/auth/logout` | Déconnexion | | GET | `/api/v1/supervisor/dashboard` | Dashboard | | GET | `/api/v1/supervisor/machines` | Parc machines | | GET | `/api/v1/supervisor/bookings` | Réservations | | GET | `/api/v1/supervisor/washes` | Lavages | | GET | `/api/v1/supervisor/pricing` | Tarifs | | POST | `/api/v1/supervisor/pricing` | Création tarif | | PUT | `/api/v1/supervisor/pricing/{id}` | Modification tarif | | GET | `/api/v1/supervisor/promotions` | Promotions | --- ## 8. Règles métier critiques ### 8.1 Wallet - aucune écriture directe de solde hors `WalletService`, - toute opération critique porte une clé d'idempotence, - séparation stricte entre transaction de paiement externe et mouvement de wallet interne. ### 8.2 Réservations - un créneau machine ne peut jamais être réservé deux fois, - création de réservation sous transaction avec verrou logique, - pénalité et remboursement centralisés dans `BookingService`. ### 8.3 Lavages - un lavage ne peut démarrer que si la machine est éligible, - toute commande machine doit être historisée, - toute fin de cycle doit être confirmée par événement ou simulation contrôlée. ### 8.4 Scoping exploitant - toute requête superviseur doit être filtrée par `organization_id`, - si `establishment_id` est renseigné sur le superviseur, la visibilité est limitée à cet établissement. --- ## 9. Tâches planifiées | Fréquence | Job | Description | |-----------|-----|-------------| | Toutes les 5 min | `CheckNoShowBookings` | Détection des no-shows | | Toutes les 5 min | `RefreshOfflineMachines` | Contrôle heartbeat / statuts | | Toutes les heures | `SendScheduledNotifications` | Notifications en attente | | Chaque nuit | `GenerateDailyStats` | Agrégats journaliers | | Chaque nuit | `PurgeRevokedTokens` | Nettoyage technique | | Chaque semaine | `AnonymizeInactiveUsers` | Politique RGPD | --- ## 10. Sécurité ### 10.1 Général - HTTPS obligatoire, - validation stricte des entrées, - rate limiting sur auth, paiements et endpoints techniques, - headers de sécurité côté web, - UUID publics dans l'API. ### 10.2 Paiements - vérification systématique des webhooks, - aucune confiance dans le retour client seul, - conservation du payload brut du PSP. ### 10.3 Intégrations machines - API key dédiée ou signature HMAC, - journalisation des appels, - horodatage et contrôle anti-rejeu si possible. --- ## 11. Observabilité - Sentry backend, - logs structurés JSON, - suivi des jobs Laravel Horizon, - métriques minimales : - taux d'échec commandes machines, - taux d'échec paiements, - machines offline, - jobs en échec, - no-shows. --- ## 12. Tests | Type | Outil | Portée | |------|-------|--------| | Unitaires | Pest | Wallet, Booking, Pricing, Machine provider | | Intégration | Pest + MySQL réel | Endpoints critiques | | Contrat | Payloads partenaires | Intégration machine / paiement | | Charge | k6 | Wallet, booking, start wash | ### Cas critiques obligatoires - double webhook paiement, - double réservation même créneau, - timeout commande machine, - événement machine reçu en doublon, - annulation et no-show simultanés, - scoping superviseur inter-organisation. --- ## 13. Livrables attendus - code source Laravel, - migrations et seeders, - documentation OpenAPI, - `.env.example`, - Docker pour dev, - suite de tests critique, - mode simulation machine, - données de démonstration multi-laveries.