# Documentation Technique API RESTful PHP 8.3 ## Table des matières 1. [Structure du projet](#structure-du-projet) 2. [Configuration du serveur](#configuration-du-serveur) 3. [Flux d'une requête](#flux-dune-requête) 4. [Architecture des composants](#architecture-des-composants) 5. [Base de données](#base-de-données) 6. [Sécurité](#sécurité) 7. [Gestion des mots de passe (NIST SP 800-63B)](#gestion-des-mots-de-passe-nist-sp-800-63b) 8. [Endpoints API](#endpoints-api) 9. [Paiements Stripe Connect](#paiements-stripe-connect) 10. [Changements récents](#changements-récents) ## Structure du projet ```plaintext /api/ ├── docs/ │ └── TECHBOOK.md ├── src/ │ ├── Controllers/ │ │ └── UserController.php │ ├── Core/ │ │ ├── Router.php │ │ ├── Request.php │ │ ├── Response.php │ │ ├── Session.php │ │ └── Database.php │ └── Config/ │ └── config.php ├── index.php └── .htaccess ``` ## Configuration du serveur ### Prérequis - Debian 12 - NGINX - PHP 8.3-FPM - MariaDB 10.11 ### Configuration NGINX Le serveur NGINX est configuré pour rediriger toutes les requêtes vers le point d'entrée `index.php` de l'API. ### Configuration PHP-FPM PHP-FPM est configuré pour gérer les processus PHP avec des paramètres optimisés pour une API. ## Flux d'une requête Exemple détaillé du parcours d'une requête POST /api/users : 1. **Entrée de la requête** - La requête arrive sur le serveur NGINX - NGINX redirige vers PHP-FPM via le socket unix - Le fichier .htaccess redirige vers index.php 2. **Initialisation (index.php)** - Chargement des dépendances - Initialisation de la configuration - Démarrage de la session - Configuration des headers CORS - Initialisation du routeur 3. **Routage** - Le Router analyse la méthode HTTP (POST) - Analyse de l'URI (/api/users) - Correspondance avec les routes enregistrées - Instanciation du Controller approprié 4. **Traitement (UserController)** - Vérification de l'authentification - Récupération des données JSON - Validation des données reçues - Traitement métier - Interaction avec la base de données - Préparation de la réponse 5. **Réponse** - Formatage de la réponse en JSON - Configuration des headers de réponse - Envoi au client ## Architecture des composants ### Core Components #### Router - Gère le routage des requêtes - Associe les URLs aux Controllers - Gère les paramètres d'URL - Dispatch vers les méthodes appropriées #### Request - Parse les données entrantes - Gère les différents types de contenu - Nettoie et valide les entrées - Fournit une interface unifiée pour accéder aux données #### Response - Formate les réponses en JSON - Gère les codes HTTP - Configure les headers de réponse - Assure la cohérence des réponses #### Session - Gère l'état des sessions - Stocke les données d'authentification - Vérifie les permissions - Sécurise les données de session #### Database - Gère la connexion à MariaDB - Fournit une interface PDO - Gère le pool de connexions - Assure la sécurité des requêtes ## Base de données ### Architecture des containers MariaDB Depuis janvier 2025, les bases de données sont hébergées dans des containers MariaDB dédiés : | Environnement | Container API | Container DB | Serveur | IP DB | Nom BDD | Utilisateur | Source des données | |---------------|--------------|--------------|---------|-------|---------|-------------|-------------------| | **DEV** | dva-geo | maria3 | IN3 | 13.23.33.4 | dva_geo | dva_geo_user | Migré depuis dva-geo/geo_app | | **RECETTE** | rca-geo | maria3 | IN3 | 13.23.33.4 | rca_geo | rca_geo_user | Migré depuis rca-geo/geo_app | | **PRODUCTION** | pra-geo | maria4 | IN4 | 13.23.33.4 | pra_geo | pra_geo_user | **Dupliqué depuis maria3/rca_geo** | **Note importante :** La base de production `pra_geo` est créée en dupliquant `rca_geo` depuis IN3/maria3 vers IN4/maria4. **Avantages de cette architecture :** - Isolation des données par environnement - Performances optimisées (containers dédiés) - Sauvegardes indépendantes - Maintenance simplifiée - Séparation physique Production/Recette (serveurs différents) **Migration :** Utiliser le script `scripts/migrate_to_maria_containers.sh` pour migrer les données. ### Structure des tables principales #### Table `users` - `encrypted_user_name` : Identifiant de connexion chiffré (unique) - `encrypted_email` : Email chiffré (unique) - `user_pass_hash` : Hash du mot de passe - `encrypted_name`, `encrypted_phone`, `encrypted_mobile` : Données personnelles chiffrées - Autres champs : `first_name`, `sect_name`, `fk_role`, `fk_entite`, etc. #### Table `entites` (Amicales) - `chk_mdp_manuel` (DEFAULT 0) : Gestion manuelle des mots de passe - `chk_username_manuel` (DEFAULT 0) : Gestion manuelle des identifiants - `chk_stripe` : Activation des paiements Stripe - Données chiffrées : `encrypted_name`, `encrypted_email`, `encrypted_phone`, etc. #### Table `medias` - `support` : Type de support (entite, user, operation, passage) - `support_id` : ID de l'élément associé - `file_category` : Catégorie (logo, export, carte, etc.) - `file_path` : Chemin complet du fichier - `processed_width/height` : Dimensions après traitement - Utilisée pour stocker les logos des entités ### Chiffrement des données Toutes les données sensibles sont chiffrées avec AES-256-CBC : - Emails, noms, téléphones - Identifiants de connexion - Informations bancaires (IBAN, BIC) ### Migration de base de données Script SQL pour ajouter les nouveaux champs : ```sql -- Ajout de la gestion manuelle des usernames ALTER TABLE `entites` ADD COLUMN `chk_username_manuel` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Gestion des usernames manuelle (1) ou automatique (0)' AFTER `chk_mdp_manuel`; -- Index pour optimiser la vérification d'unicité ALTER TABLE `users` ADD INDEX `idx_encrypted_user_name` (`encrypted_user_name`); ``` ## Sécurité ### Mesures implémentées - Validation stricte des entrées - Protection contre les injections SQL (PDO) - Hachage sécurisé des mots de passe - Headers de sécurité HTTP - Gestion des CORS - Session sécurisée - Authentification requise - Chiffrement AES-256 des données sensibles - Envoi séparé des identifiants par email ## Gestion des mots de passe (NIST SP 800-63B) ### Vue d'ensemble L'API implémente un système de gestion des mots de passe conforme aux recommandations NIST SP 800-63B, avec quelques adaptations spécifiques demandées par le client. ### Service PasswordSecurityService Le service `PasswordSecurityService` (`src/Services/PasswordSecurityService.php`) gère : - Validation des mots de passe selon NIST - Vérification contre les bases de données de mots de passe compromis (HIBP) - Génération de mots de passe sécurisés - Estimation de la force des mots de passe ### Conformités NIST respectées | Recommandation NIST | Notre Implémentation | Status | |-------------------|---------------------|--------| | **Longueur minimale : 8 caractères** | ✅ MIN = 8 caractères | ✅ CONFORME | | **Longueur maximale : 64 caractères minimum** | ✅ MAX = 64 caractères | ✅ CONFORME | | **Accepter TOUS les caractères ASCII imprimables** | ✅ Aucune restriction sur les caractères | ✅ CONFORME | | **Accepter les espaces** | ✅ Espaces acceptés (début, milieu, fin) | ✅ CONFORME | | **Accepter Unicode (émojis, accents, etc.)** | ✅ Support UTF-8 avec `mb_strlen()` | ✅ CONFORME | | **Vérifier contre les mots de passe compromis** | ✅ API Have I Been Pwned avec k-anonymity | ✅ CONFORME | | **Pas d'obligation de composition** | ✅ Pas d'erreur si manque majuscules/chiffres/spéciaux | ✅ CONFORME | | **Pas de changement périodique forcé** | ✅ Aucune expiration automatique | ✅ CONFORME | | **Permettre les phrases de passe** | ✅ "Mon chat Félix a 3 ans!" accepté | ✅ CONFORME | ### Déviations par choix du client | Recommandation NIST | Notre Implémentation | Raison | |-------------------|---------------------|--------| | **Email unique par compte** | ❌ Plusieurs comptes par email autorisés | Demande client | | **Mot de passe ≠ identifiant** | ❌ Mot de passe = identifiant autorisé | Demande client | | **Vérifier contexte utilisateur** | ❌ Pas de vérification nom/email dans mdp | Demande client | ### Vérification contre les mots de passe compromis #### Have I Been Pwned (HIBP) API L'implémentation utilise l'API HIBP avec la technique **k-anonymity** pour préserver la confidentialité : 1. **Hash SHA-1** du mot de passe 2. **Envoi des 5 premiers caractères** du hash à l'API 3. **Comparaison locale** avec les suffixes retournés 4. **Aucun mot de passe en clair** n'est transmis #### Mode "Fail Open" En cas d'erreur de l'API HIBP : - Le système laisse passer le mot de passe - Un avertissement est enregistré dans les logs - L'utilisateur n'est pas bloqué ### Exemples de mots de passe #### Acceptés (conformes NIST) - `monmotdepasse` → Accepté (≥8 caractères, pas compromis) - `12345678` → Accepté SI pas dans HIBP - `Mon chat s'appelle Félix!` → Accepté (phrase de passe) - ` ` → Accepté si ≥8 espaces - `😀🎉🎈🎁🎂🍰🎊🎀` → Accepté (8 émojis) - `jean.dupont` → Accepté même si = username #### Refusés - `pass123` → Refusé (< 8 caractères) - `password` → Refusé (compromis dans HIBP) - `123456789` → Refusé (compromis dans HIBP) - Mot de passe > 64 caractères → Refusé ### Force des mots de passe Le système privilégie la **LONGUEUR** sur la complexité (conforme NIST) : | Longueur | Force | Score | |----------|-------|-------| | < 8 car. | Trop court | 0-10 | | 8-11 car. | Acceptable | 20-40 | | 12-15 car. | Bon | 40-60 | | 16-19 car. | Fort | 60-80 | | ≥20 car. | Très fort | 80-100 | | Compromis | Compromis | ≤10 | ### Génération automatique Pour la génération automatique, le système reste **strict** pour garantir des mots de passe forts : - Longueur : 12-16 caractères - Contient : majuscules + minuscules + chiffres + spéciaux - Vérifié contre HIBP (10 tentatives max) - Exemple : `Xk9#mP2$nL5!` ### Gestion des comptes multiples par email Depuis janvier 2025, le système permet plusieurs comptes avec le même email : #### Fonction `lostPassword` adaptée - Recherche **TOUS** les comptes avec l'email fourni - Génère **UN SEUL** mot de passe pour tous ces comptes - Met à jour **TOUS** les comptes en une requête - Envoie **UN SEUL** email avec la liste des usernames concernés #### Exemple de comportement Si 3 comptes partagent l'email `contact@amicale.fr` : - `jean.dupont` - `marie.martin` - `paul.durand` L'email contiendra : ``` Bonjour, Voici votre nouveau mot de passe pour les comptes : jean.dupont, marie.martin, paul.durand Mot de passe : XyZ123!@# ``` ### Endpoints API dédiés aux mots de passe #### Vérification de force (public) ```http POST /api/password/check Content-Type: application/json { "password": "monmotdepasse", "check_compromised": true } ``` **Réponse :** ```json { "status": "success", "valid": false, "errors": [ "Ce mot de passe a été trouvé 23 547 fois dans des fuites de données." ], "warnings": [ "Suggestion : Évitez les séquences communes pour plus de sécurité" ], "strength": { "score": 20, "strength": "Faible", "feedback": ["Ce mot de passe a été compromis"], "length": 13, "diversity": 1 }, "compromised": { "compromised": true, "occurrences": 23547, "message": "Ce mot de passe a été trouvé 23 547 fois dans des fuites de données" } } ``` #### Vérification de compromission uniquement (public) ```http POST /api/password/compromised Content-Type: application/json { "password": "monmotdepasse" } ``` #### Génération automatique (authentifié) ```http GET /api/password/generate?length=14 Authorization: Bearer {session_id} ``` **Réponse :** ```json { "status": "success", "password": "Xk9#mP2$nL5!qR", "length": 14, "strength": { "score": 85, "strength": "Très fort", "feedback": [] } } ``` ### Configuration et sécurité #### Paramètres de sécurité - **Timeout API HIBP** : 5 secondes - **Cache** : 15 minutes pour les vérifications répétées - **Logging** : Aucun mot de passe en clair dans les logs - **K-anonymity** : Seuls 5 caractères du hash SHA-1 envoyés #### Points d'intégration - `LoginController::register` : Validation lors de l'inscription - `LoginController::lostPassword` : Génération sécurisée - `UserController::createUser` : Validation si mot de passe manuel - `UserController::updateUser` : Validation lors du changement - `ApiService::generateSecurePassword` : Génération avec vérification HIBP ### Résumé ✅ **100% CONFORME NIST** pour les aspects techniques ✅ **Adapté aux besoins du client** (emails multiples, mdp=username) ✅ **Sécurité maximale** avec vérification HIBP ✅ **Expérience utilisateur optimale** (souple mais sécurisé) ## Endpoints API ### Routes Publiques vs Privées L'API distingue deux types de routes : #### Routes Publiques - POST /api/login - POST /api/register - GET /api/health #### Routes Privées (Nécessitent une session authentifiée) - Toutes les autres routes ### Authentification L'authentification utilise le système de session PHP natif. #### Login ```http POST /api/login Content-Type: application/json { "email": "user@example.com", "password": "SecurePassword123" } ``` **Réponse réussie :** ```json { "message": "Connecté avec succès", "user": { "id": 123, "email": "user@example.com" } } ``` **Notes importantes :** - Un cookie de session PHP sécurisé est automatiquement créé - Le cookie est httpOnly, secure et SameSite=Strict - L'ID de session est régénéré à chaque login réussi #### Logout ```http POST /api/logout ``` **Réponse réussie :** ```json { "message": "Déconnecté avec succès" } ``` ### Sécurité des Sessions La configuration des sessions inclut : - Sessions PHP natives sécurisées - Protection contre la fixation de session - Cookies httpOnly (protection XSS) - Mode strict pour les cookies - Validation côté serveur à chaque requête - use_strict_mode = 1 - cookie_httponly = 1 - cookie_secure = 1 - cookie_samesite = Strict - Régénération de l'ID de session après login - Destruction complète de la session au logout ### Users #### Création d'utilisateur La création d'utilisateur s'adapte aux paramètres de l'entité (amicale) : ```http POST /api/users Content-Type: application/json Authorization: Bearer {session_id} { "email": "john@example.com", "name": "John Doe", "first_name": "John", "role": 1, "fk_entite": 5, "username": "j.doe38", // Requis si chk_username_manuel=1 pour l'entité "password": "SecurePass123", // Requis si chk_mdp_manuel=1 pour l'entité "phone": "0476123456", "mobile": "0612345678", "sect_name": "Secteur A", "date_naissance": "1990-01-15", "date_embauche": "2020-03-01" } ``` **Comportement selon les paramètres de l'entité :** | chk_username_manuel | chk_mdp_manuel | Comportement | |---------------------|----------------|--------------| | 0 | 0 | Username et password générés automatiquement | | 0 | 1 | Username généré, password requis dans le payload | | 1 | 0 | Username requis dans le payload, password généré | | 1 | 1 | Username et password requis dans le payload | **Validation du username (si manuel) :** - Format : 10-30 caractères - Commence par une lettre - Caractères autorisés : a-z, 0-9, ., -, _ - Doit être unique dans toute la base **Réponse réussie :** ```json { "status": "success", "message": "Utilisateur créé avec succès", "id": 123, "username": "j.doe38", // Toujours retourné "password": "xY7#mK9@pL2" // Retourné seulement si généré automatiquement } ``` **Envoi d'emails :** - **Email 1** : Identifiant de connexion (toujours envoyé) - **Email 2** : Mot de passe (toujours envoyé, 1 seconde après le premier) **Codes de statut :** - 201: Création réussie - 400: Données invalides ou username/password manquant si requis - 401: Non authentifié - 403: Accès non autorisé (rôle insuffisant) - 409: Email ou username déjà utilisé - 500: Erreur serveur #### Vérification de disponibilité du username ```http POST /api/users/check-username Content-Type: application/json Authorization: Bearer {session_id} { "username": "j.doe38" } ``` **Réponse si disponible :** ```json { "status": "success", "available": true, "message": "Nom d'utilisateur disponible", "username": "j.doe38" } ``` **Réponse si déjà pris :** ```json { "status": "success", "available": false, "message": "Ce nom d'utilisateur est déjà utilisé", "suggestions": ["j.doe38_42", "j.doe381234", "j.doe3825"] } ``` #### Autres endpoints - GET /api/users - GET /api/users/{id} - PUT /api/users/{id} - DELETE /api/users/{id} - POST /api/users/{id}/reset-password ### Entités (Amicales) #### Upload du logo d'une entité ```http POST /api/entites/{id}/logo Content-Type: multipart/form-data Authorization: Bearer {session_id} Body: logo: File (image/png, image/jpeg, image/jpg) ``` **Restrictions :** - Réservé aux administrateurs d'amicale (fk_role == 2) - L'admin ne peut uploader que le logo de sa propre amicale - Un seul logo actif par entité (le nouveau remplace l'ancien) **Traitement de l'image :** - Formats acceptés : PNG, JPG, JPEG - Redimensionnement automatique : 250x250px maximum (ratio conservé) - Résolution : 72 DPI (standard web) - Préservation de la transparence pour les PNG **Stockage :** - Chemin : `/uploads/entites/{id}/logo/logo_{id}_{timestamp}.{ext}` - Enregistrement dans la table `medias` - Suppression automatique de l'ancien logo **Réponse réussie :** ```json { "status": "success", "message": "Logo uploadé avec succès", "media_id": 42, "file_name": "logo_5_1234567890.jpg", "file_path": "/entites/5/logo/logo_5_1234567890.jpg", "dimensions": { "width": 250, "height": 180 } } ``` #### Récupération du logo d'une entité ```http GET /api/entites/{id}/logo Authorization: Bearer {session_id} ``` **Réponse :** ```json { "status": "success", "logo": { "id": 42, "data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...", "file_name": "logo_5_1234567890.png", "mime_type": "image/png", "width": 250, "height": 180, "size": 15234 } } ``` **Note :** Le logo est également inclus automatiquement dans la réponse du login si disponible. #### Mise à jour d'une entité ```http PUT /api/entites/{id} Content-Type: application/json Authorization: Bearer {session_id} { "name": "Amicale de Grenoble", "adresse1": "123 rue de la Caserne", "adresse2": "", "code_postal": "38000", "ville": "Grenoble", "phone": "0476123456", "mobile": "0612345678", "email": "contact@amicale38.fr", "chk_stripe": true, // Activation paiement Stripe "chk_mdp_manuel": false, // Génération auto des mots de passe "chk_username_manuel": false, // Génération auto des usernames "chk_copie_mail_recu": true, "chk_accept_sms": false } ``` **Paramètres de gestion des membres :** | Paramètre | Type | Description | |-----------|------|-------------| | chk_mdp_manuel | boolean | `true`: L'admin saisit les mots de passe
`false`: Génération automatique | | chk_username_manuel | boolean | `true`: L'admin saisit les identifiants
`false`: Génération automatique | | chk_stripe | boolean | Active/désactive les paiements Stripe | **Note :** Ces paramètres sont modifiables uniquement par les administrateurs (fk_role > 1). #### Réponse du login avec paramètres entité Lors du login, les paramètres de l'entité sont retournés dans le groupe `amicale` : ```json { "status": "success", "session_id": "abc123...", "session_expiry": "2025-01-09T15:30:00+00:00", "user": { "id": 9999980, "fk_entite": 5, "fk_role": 2, "fk_titre": null, "first_name": "Pierre", "sect_name": "", "date_naissance": "1990-01-15", // Maintenant correctement récupéré "date_embauche": "2020-03-01", // Maintenant correctement récupéré "username": "pv_admin", "name": "VALERY ADM", "phone": "0476123456", // Maintenant correctement récupéré "mobile": "0612345678", // Maintenant correctement récupéré "email": "contact@resalice.com" }, "amicale": { "id": 5, "name": "Amicale de Grenoble", "chk_mdp_manuel": 0, "chk_username_manuel": 0, "chk_stripe": 1, "logo": { // Logo de l'entité (si disponible) "id": 42, "data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...", "file_name": "logo_5_1234567890.png", "mime_type": "image/png", "width": 250, "height": 180 } // ... autres champs } } ``` Ces paramètres permettent à l'application Flutter d'adapter dynamiquement le formulaire de création de membre. ## Paiements Stripe Connect ### Vue d'ensemble L'API intègre un système complet de paiements via Stripe Connect, permettant aux amicales de recevoir des paiements pour leurs calendriers via deux méthodes : - **Paiements Web** : Interface de paiement dans un navigateur - **Tap to Pay** : Paiements NFC via l'application mobile Flutter ### Architecture Stripe Connect #### Tables de base de données **Table `stripe_accounts` :** ```sql CREATE TABLE `stripe_accounts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `fk_entite` int(10) unsigned NOT NULL, `stripe_account_id` varchar(50) NOT NULL, `account_type` enum('express','standard','custom') DEFAULT 'express', `charges_enabled` tinyint(1) DEFAULT 0, `payouts_enabled` tinyint(1) DEFAULT 0, `details_submitted` tinyint(1) DEFAULT 0, `country` varchar(2) DEFAULT 'FR', `default_currency` varchar(3) DEFAULT 'eur', `business_name` varchar(255) DEFAULT NULL, `support_email` varchar(255) DEFAULT NULL, `onboarding_completed_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE KEY `stripe_account_id` (`stripe_account_id`), KEY `fk_entite` (`fk_entite`), CONSTRAINT `stripe_accounts_ibfk_1` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ); ``` **Ajout du champ `stripe_payment_id` dans `ope_pass` :** ```sql ALTER TABLE `ope_pass` ADD COLUMN `stripe_payment_id` VARCHAR(50) DEFAULT NULL COMMENT 'ID du PaymentIntent Stripe (pi_xxx)'; ALTER TABLE `ope_pass` ADD INDEX `idx_stripe_payment` (`stripe_payment_id`); ``` #### Services principaux **StripeService** (`src/Services/StripeService.php`) : - Gestion des PaymentIntents - Communication avec l'API Stripe - Gestion des comptes Stripe Connect **StripeController** (`src/Controllers/StripeController.php`) : - Endpoints pour la création de PaymentIntents - Gestion des webhooks Stripe - API pour les comptes Connect ### Flow de paiement #### 1. Création du compte Stripe Connect (Onboarding) ```http POST /api/stripe/accounts/create Authorization: Bearer {session_id} Content-Type: application/json { "amicale_id": 45, "type": "express", "country": "FR", "email": "contact@amicale-pompiers.fr", "business_profile": { "name": "Amicale des Pompiers", "product_description": "Vente de calendriers des pompiers", "mcc": "8398" } } ``` **Réponse :** ```json { "success": true, "stripe_account_id": "acct_1O3ABC456DEF789", "onboarding_url": "https://connect.stripe.com/express/oauth/authorize?...", "status": "pending" } ``` #### 2. Création d'un PaymentIntent (Tap to Pay) **Flow actuel (v2) :** 1. L'application crée/modifie d'abord le passage pour obtenir un ID réel 2. Puis crée le PaymentIntent avec cet ID ```http POST /api/stripe/payments/create-intent Authorization: Bearer {session_id} Content-Type: application/json { "amount": 2500, // 25€ en centimes "passage_id": 456, // ID RÉEL du passage (jamais 0) "payment_method_types": ["card_present"], // Tap to Pay "location_id": "tml_xxx", "amicale_id": 45, "member_id": 67, "stripe_account": "acct_1234" } ``` **Réponse :** ```json { "success": true, "client_secret": "pi_3QaXYZ_secret_xyz", "payment_intent_id": "pi_3QaXYZ123ABC456", "amount": 2500, "currency": "eur", "passage_id": 456, "type": "tap_to_pay" } ``` #### 3. Traitement du paiement **Côté application Flutter :** - Utilisation du SDK Stripe Terminal - Collecte NFC avec le `client_secret` - Traitement automatique du paiement **Mise à jour automatique :** - Le `stripe_payment_id` est automatiquement ajouté au passage lors de la création du PaymentIntent - Lien bidirectionnel entre le passage et le paiement Stripe ### Endpoints Stripe #### Gestion des comptes - `POST /api/stripe/accounts/create` : Création d'un compte Connect - `GET /api/stripe/accounts/{id}` : Statut d'un compte - `PUT /api/stripe/accounts/{id}` : Mise à jour d'un compte #### Gestion des paiements - `POST /api/stripe/payments/create-intent` : Création d'un PaymentIntent - `GET /api/stripe/payments/{id}` : Statut d'un paiement - `POST /api/stripe/payments/confirm` : Confirmation d'un paiement #### Gestion des devices Tap to Pay - `GET /api/stripe/devices/certified-android` : Liste des appareils Android certifiés - `POST /api/stripe/devices/check-tap-to-pay` : Vérification de compatibilité d'un appareil - `GET /api/stripe/config` : Configuration publique Stripe - `GET /api/stripe/stats` : Statistiques de paiement #### Webhooks - `POST /api/stripe/webhooks` : Réception des événements Stripe - `account.updated` : Mise à jour du statut d'un compte - `payment_intent.succeeded` : Confirmation d'un paiement réussi - `payment_intent.payment_failed` : Échec d'un paiement ### Sécurité et validation #### Prérequis pour les paiements : - ✅ Compte Stripe Connect activé (`charges_enabled = 1`) - ✅ Virements activés (`payouts_enabled = 1`) - ✅ Onboarding terminé (`details_submitted = 1`) - ✅ Passage existant avec montant correspondant - ✅ Utilisateur authentifié et autorisé #### Validation des montants : - Minimum : 1€ (100 centimes) - Maximum : 999€ (99 900 centimes) - Vérification de correspondance avec le passage #### Sécurité des transactions : - Headers CORS configurés - Validation côté serveur obligatoire - Logs de toutes les transactions - Gestion des erreurs robuste ### États et statuts #### États des comptes Stripe : - `pending` : Onboarding en cours - `restricted` : Informations manquantes - `active` : Opérationnel pour les paiements - `rejected` : Refusé par Stripe #### États des paiements : - `requires_payment_method` : En attente de paiement - `processing` : Traitement en cours - `succeeded` : Paiement réussi - `canceled` : Paiement annulé - `requires_action` : Action utilisateur requise ### Intégration avec l'application #### Flutter (Tap to Pay) : - SDK Stripe Terminal pour iOS/Android - Interface NFC native - Gestion des états du terminal - Validation en temps réel #### Web (Paiements navigateur) : - Stripe.js pour l'interface - Formulaire de carte sécurisé - Confirmation 3D Secure automatique ### Monitoring et logs #### Logs importants : - Création/mise à jour des comptes Connect - Succès/échecs des paiements - Erreurs webhook Stripe - Tentatives de paiement frauduleuses #### Métriques de suivi : - Taux de succès des paiements par amicale - Montants moyens des transactions - Temps de traitement des paiements - Erreurs par type d'appareil ### Configuration environnement #### Architecture des clés Stripe Depuis janvier 2025, les clés Stripe sont **séparées par environnement** dans `src/Config/AppConfig.php` : | Environnement | URL | Mode | Clés utilisées | Status | |---------------|-----|------|----------------|--------| | **DEV** | https://dapp.geosector.fr | `test` | Clés TEST Pierre (dev plateforme) | ✅ Opérationnel | | **RECETTE** | https://rapp.geosector.fr | `test` | Clés TEST du client | ⏳ À configurer | | **PRODUCTION** | https://app.geosector.fr | `live` | Clés LIVE du client | ⏳ À configurer | **Emplacement dans le code :** - **DEV** : `AppConfig.php` lignes 175-187 (section `dapp.geosector.fr`) - **RECETTE** : `AppConfig.php` lignes 150-162 (section `rapp.geosector.fr`) - **PRODUCTION** : `AppConfig.php` lignes 126-138 (section `app.geosector.fr`) #### Configuration des clés client Pour configurer les clés Stripe du client : 1. **Récupérer les clés depuis le Dashboard Stripe du client** - Se connecter sur https://dashboard.stripe.com - Aller dans **Développeurs → Clés API** - Pour les clés TEST : Mode Test activé - Pour les clés LIVE : Mode Live activé 2. **Remplacer les placeholders dans AppConfig.php** - **RECETTE** (ligne 152-153) : Remplacer `CLIENT_PK_TEST_A_REMPLACER` et `CLIENT_SK_TEST_A_REMPLACER` - **PRODUCTION** (ligne 130-131) : Remplacer `CLIENT_PK_LIVE_A_REMPLACER` et `CLIENT_SK_LIVE_A_REMPLACER` 3. **Déployer selon l'environnement** ```bash # Déployer en RECETTE ./deploy-api.sh rca # Déployer en PRODUCTION ./deploy-api.sh pra ``` **⚠️ Sécurité :** Voir `TODO-API.md` section "Sécurisation des clés Stripe" pour étudier une approche plus sécurisée (variables d'environnement, fichiers séparés). #### Comptes Connect - Type : Express (simplifié pour les associations) - Pays : France (FR) - Devise : Euro (EUR) - Frais : Standard Stripe Connect - Pas de commission plateforme (100% pour l'amicale) ### Gestion des appareils certifiés Tap to Pay #### Table `stripe_android_certified_devices` Stocke la liste des appareils Android certifiés pour Tap to Pay en France : - **95+ appareils** pré-chargés lors de l'installation - **Mise à jour automatique** hebdomadaire via CRON - **Vérification de compatibilité** via endpoints dédiés #### Endpoint de vérification de compatibilité ```http POST /api/stripe/devices/check-tap-to-pay Content-Type: application/json { "platform": "ios" | "android", "manufacturer": "Samsung", // Requis pour Android "model": "SM-S921B" // Requis pour Android } ``` **Réponse Android compatible :** ```json { "status": "success", "tap_to_pay_supported": true, "message": "Tap to Pay disponible sur cet appareil", "min_android_version": 14 } ``` **Réponse iOS :** ```json { "status": "success", "message": "Vérification iOS à faire côté client", "requirements": "iPhone XS ou plus récent avec iOS 16.4+", "details": "iOS 16.4 minimum requis pour le support PIN complet" } ``` #### Requirements Tap to Pay | Plateforme | Appareil minimum | OS minimum | Notes | |------------|------------------|------------|-------| | **iOS** | iPhone XS (2018+) | iOS 16.4+ | Support PIN complet | | **Android** | Variable | Android 11+ | NFC obligatoire, non rooté | ### Documentation technique complète Pour le flow détaillé complet, voir : - **`docs/STRIPE-TAP-TO-PAY-FLOW.md`** : Documentation technique complète du flow de paiement - **`docs/PLANNING-STRIPE-API.md`** : Planification et architecture Stripe - **`docs/STRIPE-TAP-TO-PAY-REQUIREMENTS.md`** : Requirements officiels et liste complète des devices certifiés ## Intégration Frontend ### Configuration des Requêtes Toutes les requêtes API depuis le frontend doivent inclure : ```javascript fetch('/api/endpoint', { credentials: 'include', // Important pour les cookies de session // ... autres options }); ``` ### Gestion des Sessions - Les cookies de session sont automatiquement gérés par le navigateur - Pas besoin de stocker ou gérer des tokens manuellement - Redirection vers /login si session expirée (401) ## Système de tâches CRON ### Vue d'ensemble L'API utilise des scripts CRON pour automatiser les tâches de maintenance et de traitement. Les scripts sont situés dans `/scripts/cron/` et s'exécutent dans les containers Incus Alpine. ### Tâches CRON configurées | Script | Fréquence | Fonction | Container | |--------|-----------|----------|-----------| | `process_email_queue.php` | */5 * * * * | Traite la queue d'emails (reçus, notifications) | DVA, RCA | | `cleanup_security_data.php` | 0 2 * * * | Nettoie les données de sécurité obsolètes | DVA, RCA | | `update_stripe_devices.php` | 0 3 * * 0 | Met à jour la liste des devices certifiés Tap to Pay | DVA, RCA | ### Configuration des CRONs Sur les containers Alpine (dva-geo, rca-geo, pra-geo) : ```bash # Vérifier les crons actifs crontab -l # Éditer les crons crontab -e # Format des lignes cron */5 * * * * /usr/bin/php /var/www/geosector/api/scripts/cron/process_email_queue.php >> /var/www/geosector/api/logs/email_queue.log 2>&1 0 2 * * * /usr/bin/php /var/www/geosector/api/scripts/cron/cleanup_security_data.php >> /var/www/geosector/api/logs/cleanup_security.log 2>&1 0 3 * * 0 /usr/bin/php /var/www/geosector/api/scripts/cron/update_stripe_devices.php >> /var/www/geosector/api/logs/stripe_devices.log 2>&1 ``` ### Script `process_email_queue.php` - **Fonction** : Envoie les emails en attente dans la table `email_queue` - **Batch** : 50 emails maximum par exécution - **Lock file** : `/tmp/process_email_queue.lock` (évite l'exécution simultanée) - **Gestion d'erreur** : 3 tentatives max par email ### Script `cleanup_security_data.php` - **Fonction** : Purge les données de sécurité selon la politique de rétention - **Rétention** : - Métriques de performance : 30 jours - Tentatives de login échouées : 7 jours - Alertes résolues : 90 jours - IPs expirées : Déblocage immédiat ### Script `update_stripe_devices.php` - **Fonction** : Maintient à jour la liste des appareils certifiés Tap to Pay - **Source** : Liste de 95+ devices intégrée + fichier JSON optionnel - **Actions** : - Ajoute les nouveaux appareils certifiés - Met à jour les versions Android minimales - Désactive les appareils obsolètes - Envoie une notification email si changements importants - **Personnalisation** : Possibilité d'ajouter des devices via `/data/stripe_certified_devices.json` ### Monitoring des CRONs Les logs sont stockés dans `/var/www/geosector/api/logs/` : - `email_queue.log` : Logs du traitement des emails - `cleanup_security.log` : Logs du nettoyage sécurité - `stripe_devices.log` : Logs de mise à jour des devices ## Maintenance et Déploiement ### Logs - Logs d'accès NGINX : /var/log/nginx/api-access.log - Logs d'erreur NGINX : /var/log/nginx/api-error.log - Logs PHP : /var/log/php/php-error.log ### Déploiement Le script `deploy-api.sh` gère le déploiement sur les 3 environnements : ```bash # Déploiement DEV : code local → container dva-geo sur IN3 ./deploy-api.sh # Déploiement RECETTE : container dva-geo → container rca-geo sur IN3 ./deploy-api.sh rca # Déploiement PRODUCTION : container rca-geo (IN3) → container pra-geo (IN4) ./deploy-api.sh pra ``` Flux de déploiement : 1. **DEV** : Archive du code local, déploiement sur container `dva-geo` sur IN3 (195.154.80.116) - URL publique : https://dapp.geosector.fr/api/ - IP interne : http://13.23.33.43/api/ 2. **RECETTE** : Archive depuis container `dva-geo`, déploiement sur `rca-geo` sur IN3 - URL publique : https://rapp.geosector.fr/api/ 3. **PRODUCTION** : Archive depuis `rca-geo` (IN3), déploiement sur `pra-geo` (51.159.7.190) - URL publique : https://app.geosector.fr/api/ Caractéristiques : - Sauvegarde automatique avec rotation (garde les 10 dernières) - Préservation des dossiers `logs/` et `uploads/` - Gestion des permissions : - Code API : `nginx:nginx` (755/644) - Logs et uploads : `nobody:nginx` (755/644) - Installation des dépendances Composer (pas de mise à jour) - Journalisation dans `~/.geo_deploy_history` ### Surveillance - Monitoring des processus PHP-FPM - Surveillance de la base de données - Monitoring des performances - Alertes sur erreurs critiques ## Changements récents ### Version 3.2.5 (29 Septembre 2025) #### 1. Système de gestion automatique des devices Tap to Pay **Nouveaux endpoints ajoutés :** - `GET /api/stripe/devices/certified-android` : Récupération de la liste complète des appareils certifiés - `POST /api/stripe/devices/check-tap-to-pay` : Vérification de compatibilité d'un appareil spécifique - Endpoints publics (pas d'authentification requise) pour vérification côté app **Script CRON de mise à jour automatique :** - **Script** : `/scripts/cron/update_stripe_devices.php` - **Fréquence** : Hebdomadaire (dimanche 3h) - **Fonction** : Maintient à jour la liste de 95+ appareils Android certifiés - **Base de données** : Table `stripe_android_certified_devices` avec 77 appareils actifs **Corrections des requirements iOS :** - Mise à jour : iOS 16.4+ minimum (au lieu de 15.4/16.0) - Raison : Support PIN complet obligatoire pour les paiements > 50€ **Documentation ajoutée :** - `docs/STRIPE-TAP-TO-PAY-REQUIREMENTS.md` : Requirements officiels complets - Liste exhaustive des appareils certifiés par fabricant - Configuration SDK pour toutes les plateformes #### 2. Configuration des tâches CRON sur les containers **Environnements configurés :** - **DVA-GEO (DEV)** : 3 CRONs actifs - **RCA-GEO (RECETTE)** : 3 CRONs actifs (ajoutés le 29/09) - **PRA-GEO (PROD)** : À configurer **Tâches automatisées :** 1. Queue d'emails : Toutes les 5 minutes 2. Nettoyage sécurité : Quotidien à 2h 3. Mise à jour devices Stripe : Hebdomadaire dimanche 3h ### Version 3.2.4 (Septembre 2025) #### 1. Implémentation complète de Stripe Connect V1 **Paiements Stripe intégrés pour les amicales :** - **Stripe Connect Express** : Onboarding simplifié pour les associations - **Tap to Pay** : Paiements NFC via l'application mobile Flutter - **Paiements Web** : Interface de paiement navigateur avec Stripe.js - **Webhooks** : Gestion automatique des événements Stripe **Nouvelles tables de base de données :** - `stripe_accounts` : Gestion des comptes Connect par amicale - `stripe_payment_history` : Historique des transactions Stripe - `stripe_refunds` : Gestion des remboursements - Ajout de `stripe_payment_id` dans `ope_pass` pour liaison bidirectionnelle **Nouveaux services :** - **StripeService** : Communication avec l'API Stripe, gestion des PaymentIntents - **StripeController** : Endpoints API pour création de comptes, paiements et webhooks **Flow de paiement optimisé (v2) :** 1. Passage créé/modifié EN PREMIER pour obtenir un ID réel 2. Création PaymentIntent avec `passage_id` réel (jamais 0) 3. Traitement Tap to Pay via SDK Stripe Terminal 4. Mise à jour automatique du passage avec `stripe_payment_id` **Endpoints ajoutés :** - `POST /api/stripe/accounts/create` : Création compte Connect - `POST /api/stripe/payments/create-intent` : Création PaymentIntent - `GET /api/stripe/payments/{id}` : Statut d'un paiement - `POST /api/stripe/webhooks` : Réception événements Stripe **Sécurité et validation :** - Validation stricte des montants (1€ à 999€) - Vérification correspondance passage/montant - Gestion des permissions par amicale - Logs complets des transactions **Configuration multi-environnements :** - DEV/RECETTE : Clés de test Stripe - PRODUCTION : Clés live avec webhooks sécurisés - Migration base de données via `migrate_stripe_payment_id.sql` **Documentation technique :** - `docs/STRIPE-TAP-TO-PAY-FLOW.md` : Flow complet de paiement - `docs/PLANNING-STRIPE-API.md` : Architecture et planification ### Version 3.0.7 (Août 2025) #### 1. Implémentation complète de la norme NIST SP 800-63B pour les mots de passe - **Nouveau service :** `PasswordSecurityService` pour la gestion sécurisée des mots de passe - **Vérification HIBP :** Intégration de l'API Have I Been Pwned avec k-anonymity - **Validation souple :** Suppression des obligations de composition (majuscules, chiffres, spéciaux) - **Support Unicode :** Acceptation de tous les caractères, incluant émojis et espaces - **Nouveaux endpoints :** `/api/password/check`, `/api/password/compromised`, `/api/password/generate` #### 2. Autorisation des emails multiples - **Suppression de l'unicité :** Un même email peut être utilisé pour plusieurs comptes - **Adaptation de `lostPassword` :** Mise à jour de tous les comptes partageant l'email - **Un seul mot de passe :** Tous les comptes avec le même email reçoivent le même nouveau mot de passe #### 3. Autorisation mot de passe = identifiant - **Choix client :** Permet d'avoir un mot de passe identique au nom d'utilisateur - **Pas de vérification contextuelle :** Aucune vérification nom/email dans le mot de passe ### Version 3.0.6 (Août 2025) #### 1. Correction des rôles administrateurs - **Avant :** Les administrateurs d'amicale devaient avoir `fk_role > 2` - **Après :** Les administrateurs d'amicale ont `fk_role > 1` (donc rôle 2 et plus) - **Impact :** Les champs `chk_stripe`, `chk_mdp_manuel`, `chk_username_manuel` sont maintenant modifiables par les admins d'amicale (rôle 2) #### 2. Envoi systématique des deux emails lors de la création d'utilisateur - **Avant :** Le 2ème email (mot de passe) n'était envoyé que si le mot de passe était généré automatiquement - **Après :** Les deux emails sont toujours envoyés lors de la création d'un membre - Email 1 : Identifiant (username) - Email 2 : Mot de passe (1 seconde après) - **Raison :** Le nouveau membre a toujours besoin des deux informations pour se connecter #### 3. Ajout des champs manquants dans la réponse du login - **Champs ajoutés dans la requête SQL :** - `fk_titre` - `date_naissance` - `date_embauche` - `encrypted_phone` - `encrypted_mobile` - **Impact :** Ces données sont maintenant correctement retournées dans l'objet `user` lors du login #### 4. Système de gestion des logos d'entité - **Nouvelle fonctionnalité :** Upload et gestion des logos pour les amicales - **Routes ajoutées :** - `POST /api/entites/{id}/logo` : Upload d'un nouveau logo - `GET /api/entites/{id}/logo` : Récupération du logo - **Caractéristiques :** - Réservé aux administrateurs d'amicale (fk_role == 2) - Un seul logo actif par entité - Redimensionnement automatique (250x250px max) - Format base64 dans les réponses JSON (compatible Flutter) - Logo inclus automatiquement dans la réponse du login #### 5. Amélioration de l'intégration Flutter - **Format d'envoi des images :** Base64 data URL pour compatibilité multiplateforme - **Structure de réponse enrichie :** Le logo est inclus dans l'objet `amicale` lors du login - **Optimisation :** Pas de requête HTTP supplémentaire nécessaire pour afficher le logo ### Version 3.0.8 (Janvier 2025) #### 1. Système de génération automatique de reçus fiscaux pour les dons - **Nouveau service :** `ReceiptService` pour la génération automatique de reçus PDF - **Déclencheurs automatiques :** - Création d'un passage avec `fk_type=1` (don) et email valide - Mise à jour d'un passage en don si `nom_recu` est vide/null - **Caractéristiques techniques :** - PDF ultra-légers (< 5KB) générés en format natif sans librairie externe - Support des caractères accentués avec conversion automatique - Stockage structuré : `/uploads/entites/{entite_id}/recus/{operation_id}/` - Enregistrement dans la table `medias` avec catégorie `recu` - **Queue d'envoi email :** - Envoi automatique par email avec pièce jointe PDF - Format MIME multipart pour compatibilité maximale - Gestion dans la table `email_queue` avec statut de suivi - **Nouvelle route API :** - `GET /api/passages/{id}/receipt` : Récupération du PDF d'un reçu - Retourne le PDF en base64 ou téléchargement direct selon Accept header - **Champs base de données utilisés :** - `nom_recu` : Nom du fichier PDF généré - `date_creat_recu` : Date de génération du reçu - `date_sent_recu` : Date d'envoi par email - `chk_email_sent` : Indicateur d'envoi réussi