# Documentation - Inscriptions Publiques ## Vue d'ensemble Le système d'inscription publique permet de créer des inscriptions **sans authentification**, via un feature flag activable/désactivable. ## Activation ### Configuration Ajouter dans `.env` : ```env REGISTRATION_PUBLIC_ENABLED=true REGISTRATION_PUBLIC_RATE_LIMIT=20,1 REGISTRATION_PUBLIC_REQUIRE_EMAIL_VERIFICATION=true REGISTRATION_VERIFICATION_TOKEN_EXPIRES_DAYS=7 ``` ### Comportement - **Quand activé** : La route `POST /api/public/events/{event}/registrations` accepte les inscriptions - **Quand désactivé** : La route retourne **404** (pas de surface d'attaque exposée) ## Endpoint ### POST /api/public/events/{event}/registrations Créer une inscription publique pour un événement. **Paramètres URL** : - `event` : ID ou slug de l'événement **Headers** : - `Content-Type: application/json` - `Accept: application/json` **Rate Limiting** : Configurable via `REGISTRATION_PUBLIC_RATE_LIMIT` (défaut: 20 req/min) --- ## Exemples de payload ### 1. Inscription SOMMET (Formule B) ```json { "pricing_plan_id": 2, "first_name": "Jean", "last_name": "Dupont", "email": "jean.dupont@example.com", "phone": "+22670123456", "country": "Burkina Faso", "city": "Ouagadougou", "organization": "Ministère XYZ", "position": "Directeur", "nationality": "Burkinabè", "is_haggai_leader": true, "payment_method_selected": "mobile_money" } ``` ### 2. Inscription SOMMET (Formule D - avec conjoint) ```json { "pricing_plan_id": 4, "first_name": "Marie", "last_name": "Martin", "email": "marie.martin@example.com", "phone": "+22670123457", "country": "Burkina Faso", "city": "Ouagadougou", "organization": "Église ABC", "position": "Pasteur", "nationality": "Burkinabè", "is_haggai_leader": false, "spouse_fullname": "Pierre Martin", "payment_method_selected": "national_office" } ``` ### 3. Inscription SÉMINAIRE (intention: participate) ```json { "pricing_plan_id": 5, "first_name": "Paul", "last_name": "Kouassi", "email": "paul.kouassi@example.com", "phone": "+22501234567", "country": "Côte d'Ivoire", "city": "Abidjan", "organization": "Ministère Haggai", "position": "Coordinateur", "nationality": "Ivoirien", "is_haggai_leader": true, "seminar_intent": "participate", "seminar_question_or_concern": "Je souhaite approfondir les stratégies de leadership.", "payment_method_selected": "paypal" } ``` ### 4. Inscription SÉMINAIRE (intention: recommend) ```json { "pricing_plan_id": 5, "first_name": "Sophie", "last_name": "Traoré", "email": "sophie.traore@example.com", "phone": "+22670123458", "country": "Burkina Faso", "city": "Bobo-Dioulasso", "organization": "Église DEF", "position": "Pasteur", "nationality": "Burkinabè", "is_haggai_leader": true, "seminar_intent": "recommend", "seminar_question_or_concern": "Je recommande un leader prometteur.", "recommended_full_name": "Amadou Diallo", "recommended_profession": "Pasteur", "recommended_residence_country": "Mali", "recommended_phone_whatsapp": "+22370123456", "recommended_email": "amadou.diallo@example.com", "payment_method_selected": "mobile_money" } ``` ### 5. Inscription SÉMINAIRE (intention: na) ```json { "pricing_plan_id": 5, "first_name": "Luc", "last_name": "Sangaré", "email": "luc.sangare@example.com", "phone": "+22670123459", "country": "Burkina Faso", "city": "Koudougou", "organization": "Ministère GHI", "position": "Évangéliste", "nationality": "Burkinabè", "is_haggai_leader": false, "seminar_intent": "na", "payment_method_selected": "national_office" } ``` --- ## Réponses ### Succès (201 Created) ```json { "message": "Inscription créée avec succès.", "data": { "registration_id": 123, "status": "submitted", "event": "Sommet Haggai Francophone 2026" } } ``` ### Erreur de validation (422 Unprocessable Entity) ```json { "message": "Erreur de validation.", "errors": { "email": ["L'adresse email est requise."], "spouse_fullname": ["Le nom complet du conjoint est requis pour la Formule D."] } } ``` ### Doublon (422 Unprocessable Entity) ```json { "message": "Erreur de validation.", "errors": { "email": ["Une inscription existe déjà pour cet événement avec cet email ou téléphone."] } } ``` ### Feature désactivé (404 Not Found) ```json { "message": "Public registration is not enabled." } ``` ### Rate limit (429 Too Many Requests) ```json { "message": "Too Many Attempts." } ``` --- ## Règles de validation ### Champs communs (tous événements) - `pricing_plan_id` : **requis**, doit exister pour l'événement - `first_name` : **requis**, string max 255 - `last_name` : **requis**, string max 255 - `email` : **requis**, email valide, max 255 - `phone` : **requis**, string max 255 - `country` : **requis**, string max 255 - `city` : optionnel, string max 255 - `organization` : optionnel, string max 255 - `position` : optionnel, string max 255 - `nationality` : optionnel, string max 255 - `is_haggai_leader` : optionnel, boolean - `payment_method_selected` : **requis**, `paypal` | `mobile_money` | `national_office` ### Champs spécifiques SOMMET - `ouaga_resident_confirmed` : optionnel, boolean (requis si Plan A) - `spouse_fullname` : optionnel, string max 255 (**requis si Plan D**) ### Champs spécifiques SÉMINAIRE - `seminar_intent` : **requis**, `participate` | `recommend` | `na` - `seminar_question_or_concern` : optionnel, string max 1000 **Si `seminar_intent = 'recommend'`** : - `recommended_full_name` : **requis**, string max 255 - `recommended_profession` : **requis**, string max 255 - `recommended_residence_country` : **requis**, string max 255 - `recommended_phone_whatsapp` : **requis**, string max 255 - `recommended_email` : optionnel, email valide, max 255 --- ## Déduplication Le système vérifie automatiquement les doublons : - Par **email** (priorité) - Par **téléphone** (si email non trouvé) Si une inscription existe déjà pour le même événement avec le même email/téléphone, une erreur 422 est retournée. --- ## Sécurité 1. **Feature Flag** : Route cachée (404) quand désactivée 2. **Rate Limiting** : Configurable via `REGISTRATION_PUBLIC_RATE_LIMIT` 3. **Validation stricte** : Tous les champs sont validés côté serveur 4. **Sanitization** : Email et téléphone sont normalisés (trim, lowercase pour email) 5. **Rôle 'guest'** : Les utilisateurs créés via inscription publique reçoivent le rôle `guest` (accès limité) 6. **Email non vérifié** : `email_verified_at = null` par défaut pour distinguer les utilisateurs publics 7. **Vérification email** : Recommandée pour éviter doublons et emails faux 8. **Pas d'exposition de données sensibles** : Réponse minimale (registration_id, status, event) 9. **Journalisation** : Tous les événements importants sont loggés (création, déduplication, vérification) --- ## Vérification email (recommandé) Si `REGISTRATION_PUBLIC_REQUIRE_EMAIL_VERIFICATION=true` : - Un token de vérification est généré (64 caractères, hashé en base) - Un email est envoyé avec le lien de vérification - L'inscription reste en statut `draft` jusqu'à vérification - Après vérification via `GET /api/public/registrations/verify/{token}` : - Statut → `submitted` - `email_verified_at` est défini sur l'utilisateur - Rôle passe de `guest` → `participant` **⚠️ IMPORTANT** : Il est **fortement recommandé** d'activer la vérification email pour éviter : - Des doublons - Des emails faux - Des numéros WhatsApp incorrects ### Route de vérification **GET /api/public/registrations/verify/{token}** Vérifie et confirme une inscription publique. **Réponse succès (200)** : ```json { "message": "Inscription vérifiée avec succès.", "data": { "registration_id": 123, "status": "submitted", "event": "Sommet Haggai Francophone 2026" } } ``` **Réponse erreur (422)** : ```json { "message": "Erreur de vérification.", "errors": { "token": ["Le token de vérification est invalide ou a expiré."] } } ``` --- ## Tests Exécuter les tests : ```bash php artisan test --filter PublicRegistrationTest ``` Les tests couvrent : - ✅ Route désactivée → 404 - ✅ Route activée → 201 avec payload valide - ✅ Validation des champs requis - ✅ Validation Plan D (spouse_fullname) - ✅ Validation Séminaire (seminar_intent) - ✅ Validation recommandation (si recommend) - ✅ Prévention doublons --- ## Compatibilité ✅ **Le mode privé (authentifié) reste inchangé** et fonctionne comme avant. Les deux modes partagent la même logique métier (`RegistrationService`) pour garantir la cohérence.