src/Controller/AccueilController.php line 517

Open in your IDE?
  1. <?php
  2. // src/Controller/AccueilController
  3. /*
  4.  * 
  5.  * Date de modification:16/05/2025
  6.  * Description:Controller LayoutAccueil 5sur5 SĂ©jour
  7.  *
  8.  */
  9. namespace App\Controller;
  10. use App\Entity\Fonctions;
  11. use App\Entity\User;
  12. use App\Entity\Ref;
  13. use App\Entity\Commande;
  14. use App\Entity\Typeproduit;
  15. use App\Service\AttachementService;
  16. use App\Service\BlogService;
  17. use App\Service\EmailsCmdService;
  18. use App\Service\SejourService;
  19. use App\Service\SiteService;
  20. use App\Service\TypeProduiteService;
  21. use App\Service\UserService;
  22. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  23. use Symfony\Component\Routing\Annotation\Route;
  24. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  25. use Symfony\Component\HttpFoundation\Response;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\JsonResponse;
  28. use Doctrine\ORM\EntityManagerInterface;
  29. use Symfony\Component\Mailer\MailerInterface;
  30. use Symfony\Bridge\Twig\Mime\TemplatedEmail;
  31. use Symfony\Component\Mime\Address;
  32. use Psr\Log\LoggerInterface;
  33. class AccueilController extends AbstractController
  34. {
  35.     private $typeProduiteService;
  36.     private $siteService;
  37.     private $blogService;
  38.     private $UserService;
  39.     private $sejourService;
  40.     private $attachementService;
  41.     private $mailer;
  42.     private $logger;
  43.     private $emailsCmdService;
  44.     public function __construct(TypeProduiteService $typeProduiteServiceSiteService $siteServiceBlogService $blogServiceUserService $UserServiceSejourService $sejourServiceAttachementService $attachementServiceMailerInterface $mailerLoggerInterface $loggerEmailsCmdService $emailsCmdService)
  45.     {
  46.         $this->typeProduiteService $typeProduiteService;
  47.         $this->siteService $siteService;
  48.         $this->blogService $blogService;
  49.         $this->UserService $UserService;
  50.         $this->sejourService $sejourService;
  51.         $this->attachementService $attachementService;
  52.         $this->mailer $mailer;
  53.         $this->logger $logger;
  54.         $this->emailsCmdService $emailsCmdService;
  55.     }
  56.     /**
  57.      * @Route("/Accueil5sur5", name="layoutAccueil")
  58.      * */
  59.     public function layoutAccueil()
  60.     {
  61.         return $this->render('Accueil/layoutAccueil.html.twig');
  62.     }
  63.     /**
  64.      * @Route("/Accueil5sur5/header_layoutAccueil", name="header_layoutAccueil")
  65.      */
  66.     public function header(Request $request): Response
  67.     {
  68.         $produit $this->typeProduiteService;
  69.         $liste $produit->produitlistType();
  70.         // dd($liste);
  71.         $session $request->getSession();
  72.         $Products $session->get("Panier");
  73.         if ($Products == Null) {
  74.             $Products = [];
  75.         }
  76.         return $this->render('Accueil/header.html.twig', [
  77.             'produit' => $liste,
  78.             'Products' => $Products,
  79.         ]);
  80.     }
  81.     /**
  82.      * @Route("/Accueil5sur5/header_Parent", name="header_Parent")
  83.      */
  84.     public function header_Parent(Request $request): Response
  85.     {
  86.         $produit $this->typeProduiteService;
  87.         $liste $produit->produitlistType();
  88.         // dd($liste);
  89.         $session $request->getSession();
  90.         $Products $session->get("Panier");
  91.         if ($Products == Null) {
  92.             $Products = [];
  93.         }
  94.         return $this->render('Accueil/headerParents.html.twig', [
  95.             'produit' => $liste,
  96.             'Products' => $Products,
  97.         ]);
  98.     }
  99.     /**
  100.      * @Route("/Accueil5sur5/headerLogin", name="headerLogin")
  101.      */
  102.     public function headerLogin(Request $request): Response
  103.     {
  104.         $produit $this->typeProduiteService;
  105.         $liste $produit->produitlistType();
  106.         // dd($liste);
  107.         $session $request->getSession();
  108.         $Products $session->get("Panier");
  109.         if ($Products == Null) {
  110.             $Products = [];
  111.         }
  112.         return $this->render('Accueil/headerLogin.html.twig', [
  113.             'produit' => $liste,
  114.             'Products' => $Products,
  115.         ]);
  116.     }
  117.     /**
  118.      * @Route("/", name="page_Accueil")
  119.      */
  120.     public function PageAccueil()
  121.     {
  122.         $produit $this->typeProduiteService;
  123.         $liste $produit->produitlistType();
  124.         $siteservice $this->siteService;
  125.         $site $siteservice->getActiveSite();
  126.         $list $this->blogService->allblog();
  127.         return $this->render('Accueil/PageAccueil.html.twig', [
  128.             "site" => $site
  129.             'list' => $list
  130.             'produit' => $liste,
  131.             'recaptcha_site_key' => $this->getParameter('recaptcha.site_key')
  132.         ]);
  133.     }
  134.     /**
  135.      * @Route("/Parent/", name="Parent")
  136.      */
  137.     public function Acceuil(Request $request)
  138.     {
  139.         $produit $this->typeProduiteService;
  140.         $liste $produit->produitlistType();
  141.         $siteservice $this->siteService;
  142.         $site $siteservice->getActiveSite();
  143.         $list $this->blogService->allblog();
  144.         $session $request->getSession();
  145.         $Products $session->get("Panier");
  146.         if ($Products == Null) {
  147.             $Products = [];
  148.         }
  149.         return $this->render('Accueil/PageAccueil.html.twig', [
  150.             "site" => $site
  151.             'list' => $list
  152.             'produit' => $liste
  153.             'Products' => $Products,
  154.             'recaptcha_site_key' => $this->getParameter('recaptcha.site_key')
  155.         ]);
  156.     }
  157.     /**
  158.      * @Route("/Accueil5sur5/footer_layoutAccueil", name="footer_layoutAccueil")
  159.      */
  160.     public function footer()
  161.     {
  162.         return $this->render('Accueil/footer.html.twig');
  163.     }
  164.     /**
  165.      * @Route("/Accompagnateur/register", name="accomp_register",methods={"POST","GET"})
  166.      */
  167.     public function register(Request $request\App\Service\RecaptchaService $recaptchaService)
  168.     {
  169.         try {
  170.             error_log("🔍 DEBUG accomp_register - DĂ©but du traitement pour: " $request->get('email'));
  171.             
  172.             // ===== VALIDATION RECAPTCHA V3 =====
  173.             $recaptchaToken $request->get('recaptcha_token''');
  174.             $recaptchaResult $recaptchaService->verify($recaptchaToken'creation_sejour'$request->getClientIp());
  175.             
  176.             if (!$recaptchaResult['success']) {
  177.                 $this->logger->warning('đŸ€– Ă‰chec validation reCAPTCHA', [
  178.                 'ip' => $request->getClientIp(),
  179.                     'error' => $recaptchaResult['error'],
  180.                     'score' => $recaptchaResult['score'] ?? null,
  181.                 'email' => $request->get('email')
  182.             ]);
  183.                 return new JsonResponse([
  184.                     'success' => false,
  185.                     'error' => 'Validation de sĂ©curitĂ© Ă©chouĂ©e. Veuillez rĂ©essayer.'
  186.                 ], 429);
  187.             }
  188.             error_log("✅ reCAPTCHA validĂ© avec score: " . ($recaptchaResult['score'] ?? 'N/A'));
  189.         // Forcer type_user=EF si la requĂȘte vient de la page Ă©coles publiques
  190.         $referer $request->headers->get('referer');
  191.         if ($referer && strpos($referer'/ecoles-publiques/creer') !== false) {
  192.             $request->request->set('type_user''EF');
  193.             error_log("đŸ« CrĂ©ation depuis page Ă©coles publiques â†’ Force type_user=EF");
  194.         }
  195.         ini_set("max_execution_time", -1);
  196.         ini_set('memory_limit''-1');
  197.         $em $this->getDoctrine()->getManager();
  198.         $UserService $this->UserService;
  199.         $SejourService $this->sejourService;
  200.         $AttachementService $this->attachementService;
  201.         $nom $request->get("nom_acc");
  202.         $prenom $request->get('prenom_acc');
  203.         $etablisment $request->get("etablisment");
  204.         $fonction $request->get("fonction");
  205.         $EntityFonction $em->getRepository(Fonctions::class)->find($fonction);
  206.         if ($EntityFonction) {
  207.             $nameFonction $EntityFonction->getName();
  208.         } else {
  209.             $nameFonction "";
  210.         }
  211.         $adressetablisment $request->get("adressetablisment");
  212.         // prix et reverse
  213.         $prixcnxparent 2.90;
  214.         $prixcnxpartenaire 0;
  215.         $reversecnxpart 0;
  216.         $reverseventepart 0;
  217.         $phoneacc $request->get("phone_acc");
  218.         $mail $request->get("email");
  219.         $password $request->get("password");
  220.         $role "ROLE_ACC";
  221.         $themSejour $request->get('theme_sejour');
  222.         $adressSejour $request->get("adress_sejour");
  223.         $codePostal $request->get("codePostal");
  224.         $dateDebut $request->get("date_debut_sejour");
  225.         $FinSejour $request->get("date_fin_sejour");
  226.         $AgeDugroupe $request->get("age_dugroupe_sejour");
  227.         $NbEnfant $request->get("NbEnfant");
  228.         $pays $request->get("pays");
  229.         $ville $request->get("ville");
  230.         $CODEpOSTALetablisment $request->get("CODEpOSTALetablisment");
  231.         $villeetablisment $request->get("villeetablisment");
  232.         // RĂ©cupĂ©rer le type de sĂ©jour depuis le formulaire (EP, EF, PF)
  233.         $type_user $request->get("type_user");
  234.         if (!$type_user) {
  235.             $type_user "PP"// Par dĂ©faut: Ă‰cole Publique (parents paient)
  236.         }
  237.         error_log("🔍 DEBUG register - type_user received: " $type_user);
  238.         error_log("🔍 DEBUG register - payment_method_structure: " . ($request->get('payment_method_structure') ?? 'NULL'));
  239.         // Mapper le type_user vers le type de sĂ©jour et connpay
  240.         // Logique du code sĂ©jour : [Type][Pay][Timestamp]
  241.         // Type: E=École, P=Partenaire, C=CSE
  242.         // Pay: P=Payant (parents), F=Free (gratuit/structure)
  243.         switch ($type_user) {
  244.             case 'EF':
  245.                 // Ă‰cole Française (page dĂ©diĂ©e) = Ă‰cole + Gratuit
  246.                 $type "ECOLES/AUTRES";
  247.                 $connpay 0// Gratuit (0 = Free)
  248.                 break;
  249.             case 'PF':
  250.                 // Partenaire FinancĂ© (structure prend en charge) = Partenaire + Structure finance
  251.                 $type "PARTENAIRES/VOYAGISTES";
  252.                 $connpay 0// Structure finance (0 = Free)
  253.                 break;
  254.             case 'PP':
  255.             default:
  256.                 // Ă‰cole Publique (parents paient par dĂ©faut) = Ă‰cole + Parents paient
  257.                 $type "ECOLES/AUTRES";
  258.                 $connpay 1// Parents paient (1 = Payant)
  259.                 break;
  260.         }
  261.         error_log("🔍 Type reçu du formulaire: type_user=$type_user â†’ Type sĂ©jour=$type, connpay=$connpay");
  262.         $bytes random_bytes(5);
  263.         $passAcommpa bin2hex($bytes);
  264.         if ($NbEnfant) {
  265.             $NbEnfant $NbEnfant;
  266.         } else {
  267.             $NbEnfant 0;
  268.         }
  269.         $dateSJoue = new \Datetime();
  270.         $Milliseconde $dateSJoue->format('u');
  271.         $annes $dateSJoue->format('y');
  272.         $joours $dateSJoue->format('d');
  273.         $joours $dateSJoue->format('t');
  274.         $emailaccf "Acompa" $annes $joours $Milliseconde "@fictif.com";
  275.         //CREATION ACCOMPAGNATEUR
  276.         $accomp $UserService->creationNewAcommpa($nom$prenom$etablisment$nameFonction$adressetablisment$phoneacc$emailaccf$role$passAcommpa$mail);
  277.         $accomp->setIdFonction($EntityFonction);
  278.         $em->persist($accomp);
  279.         $em->flush();
  280.         //CREATION PARTENAIRE
  281.         $part $em->getRepository(User::class)->VerifierAddresseEmailPartenaire($mail'ROLE_PARTENAIRE');
  282.         if (is_null($part)) {
  283.             $part $UserService->creationNewUser($nom$prenom$etablisment$nameFonction$adressetablisment$phoneacc$mail$passAcommpa"ROLE_PARTENAIRE");
  284.             $part->setIdFonction($EntityFonction);
  285.             $em->persist($part);
  286.             $em->flush();
  287.         }
  288.         //CREATION ETABLISSEMENT + PARTENAIRE
  289.         $Etablisment $UserService->creationNewEtablisment($part$nom$prenom$etablisment$fonction$adressetablisment$phoneacc$mail"ROLE_PARTENAIRE"$password$prixcnxparent$prixcnxpartenaire$reversecnxpart$reverseventepart);
  290.         if ($CODEpOSTALetablisment == "") {
  291.             $CODEpOSTALetablisment null;
  292.         }
  293.         $Etablisment->setCodepostaleatb($CODEpOSTALetablisment);
  294.         $Etablisment->setVille($villeetablisment);
  295.         $em->persist($Etablisment);
  296.         $em->flush();
  297.         //CREATION SEJOUR ACCOM + PARTENAIRE + ETABILSSEMENT
  298.         // Ne pas surcharger $connpay via la fonction (ex: VIP)
  299.         // Le code sĂ©jour doit reflĂ©ter exclusivement le choix utilisateur (type_user)
  300.         $sejour $SejourService->CreationNouveauSejourParAccompagnateur($themSejour$adressSejour$dateDebut$FinSejour$AgeDugroupe$type$NbEnfant$connpay$pays$ville$prixcnxparent$prixcnxpartenaire$reversecnxpart$reverseventepart);
  301.         $sejour->setCodePostal(intval($codePostal));
  302.         $SejourService->affecterAccompaniateur($sejour$accomp);
  303.         $SejourService->affecterPartenaire($sejour$part);
  304.         $SejourService->affecteretablisment($sejour$Etablisment);
  305.         $em->persist($sejour);
  306.         $em->flush();
  307.         // Gestion du paiement par la structure
  308.         $paymentMethodStructure $request->get('payment_method_structure');
  309.         $billingEmail $request->get('billing_email');
  310.         $billingContact $request->get('billing_contact');
  311.         // Si c'est une structure qui paie (PF) et qu'un mode de paiement est spĂ©cifiĂ©
  312.         if ($type_user === 'PF' && $paymentMethodStructure) {
  313.             // RĂ©cupĂ©rer le statut "en attente de paiement"
  314.             $statutAttentePaiement $em->getRepository(\App\Entity\Ref::class)->findOneBy(['libiller' => 'attente_paiement']);
  315.             if (!$statutAttentePaiement) {
  316.                 // CrĂ©er le statut s'il n'existe pas
  317.                 $statutAttentePaiement = new \App\Entity\Ref();
  318.                 $statutAttentePaiement->setLibiller('attente_paiement');
  319.                 // Pas de typeref nĂ©cessaire ici, on reste cohĂ©rent avec les autres statuts "attente_paiement"
  320.                 $em->persist($statutAttentePaiement);
  321.                 $em->flush();
  322.             }
  323.             // Mettre le sĂ©jour en attente de paiement
  324.             $sejour->setStatut($statutAttentePaiement);
  325.             $em->persist($sejour);
  326.             $em->flush();
  327.             // CrĂ©er une commande pour le sĂ©jour
  328.             $commande = new Commande();
  329.             $commande->setIdUser($part);
  330.             $commande->setIdSejour($sejour);
  331.             // RĂ©cupĂ©rer le statut "en attente" pour la commande
  332.             $statutCommandeEnAttente $em->getRepository(\App\Entity\Ref::class)->find(1); // 1 = en attente
  333.             if ($statutCommandeEnAttente) {
  334.                 $commande->setStatut($statutCommandeEnAttente);
  335.             }
  336.             $commande->setMontantht(39);
  337.             $commande->setMontantrth(46.80); // 39€ HT + 20% TVA
  338.             $commande->setMontanenv(46.80);
  339.             $commande->setTva(20);
  340.             $commande->setDateCreateCommande(new \DateTime());
  341.             $commande->setNumComande(time());
  342.             $commande->setPeriode('sejour_simple');
  343.             // Normaliser le mode de paiement
  344.             $paymentTypeNormalized = ($paymentMethodStructure === 'carte') ? 'up2pay' 'rib';
  345.             $commande->setPaymentType($paymentTypeNormalized);
  346.             $em->persist($commande);
  347.             $em->flush();
  348.             error_log("💰 Commande créée pour sĂ©jour #{$sejour->getId()}, mode de paiement: {$paymentTypeNormalized}");
  349.             // Stocker les infos de facturation en session pour l'envoi RIB si nĂ©cessaire
  350.             $session $request->getSession();
  351.             $session->set('sejour_payment_info', [
  352.                 'sejour_id' => $sejour->getId(),
  353.                 'commande_id' => $commande->getId(),
  354.                 'payment_method' => $paymentTypeNormalized,
  355.                 'billing_email' => $billingEmail,
  356.                 'billing_contact' => $billingContact
  357.             ]);
  358.             // Ne pas envoyer les codes immĂ©diatement si virement (sera envoyĂ© avec le RIB)
  359.             // Si carte : les codes seront envoyĂ©s aprĂšs paiement
  360.             if ($paymentTypeNormalized === 'rib') {
  361.                 // Les codes seront envoyĂ©s via la route send-rib-codes
  362.                 error_log("📧 Mode virement : codes sĂ©jour seront envoyĂ©s avec le RIB");
  363.                 $UserService->EnvoyerEmailAcommpatActivationNewMail($sejour$accomp);
  364.             } else {
  365.                 // Pour carte (Up2Pay) : les codes seront envoyĂ©s aprĂšs paiement rĂ©ussi
  366.                 error_log("💳 Mode carte Up2Pay : codes sĂ©jour seront envoyĂ©s aprĂšs paiement");
  367.                 $UserService->EnvoyerEmailAcommpatActivationNewMail($sejour$accomp);
  368.             }
  369.             //AFFECTAION LOGO TO USER PARTENAIRE ADN ACCOMPAGNATEUR
  370.             if ($request->request->has('path')) {
  371.                 $ty "logo sĂ©jour";
  372.                 $AttachementService->creationLogoSejour($accomp$request->get('path'), $ty);
  373.                 $AttachementService->creationLogoSejour($part$request->get('path'), $ty);
  374.             }
  375.             $TypeSejour 8;
  376.             $session $request->getSession();
  377.             $session->set('Sejour'$sejour->getId());
  378.             // Return Up2Pay payment response for carte
  379.             if ($paymentMethodStructure === 'carte') {
  380.                 return new JsonResponse([
  381.                     'success' => true,
  382.                     'paymentType' => 'up2pay',
  383.                     'message' => 'Redirection vers le paiement sĂ©curisĂ© Up2Pay...',
  384.                     'idSejour' => $sejour->getId(),
  385.                     'commandeId' => $commande->getId()
  386.                 ]);
  387.             }
  388.             // For RIB payment, return success (no payment gateway redirect needed)
  389.             return new JsonResponse(["idSejour" => $sejour->getId()]);
  390.         } else {
  391.             // Si parents paient ou pas de mode de paiement : envoyer les codes normalement
  392.             $UserService->EnvoyerEmailAcommpatActivationNewMail($sejour$accomp);
  393.         }
  394.         //AFFECTAION LOGO TO USER PARTENAIRE ADN ACCOMPAGNATEUR
  395.         if ($request->request->has('path')) {
  396.             $ty "logo sĂ©jour";
  397.             $AttachementService->creationLogoSejour($accomp$request->get('path'), $ty);
  398.             $AttachementService->creationLogoSejour($part$request->get('path'), $ty);
  399.         }
  400.         $TypeSejour 8;
  401.         $session $request->getSession();
  402.         $session->set('Sejour'$sejour->getId());
  403.         //$this->redirectToRoute('app_back_Acommpa');
  404.         return new JsonResponse(["idSejour" => $sejour->getId()]);
  405.         
  406.         } catch (\Exception $e) {
  407.             error_log("❌ Erreur dans accomp_register: " $e->getMessage());
  408.             error_log("Stack trace: " $e->getTraceAsString());
  409.             
  410.             return new JsonResponse([
  411.                 'success' => false,
  412.                 'error' => 'Une erreur est survenue lors de la crĂ©ation du sĂ©jour: ' $e->getMessage()
  413.             ], 500);
  414.         }
  415.     }
  416.     //inscription du parent 
  417.     /**
  418.      * @Route("/Parent/register", name="parent_register",methods={"POST","GET"})
  419.      */
  420.     public function registerparent(Request $request)
  421.     {
  422.         $UserService $this->UserService;
  423.         //
  424.         $nom $request->get("nomparent");
  425.         $prenom $request->get('prenomparent');
  426.         $mailparent $request->get("mailprent");
  427.         $numtel $request->get("numtel");
  428.         $passwordparent $request->get("passwordparent");
  429.         $confirmpassword $request->get("confirmpassword");
  430.         $notifsms $request->get("sms");
  431.         $notifmail $request->get('mailnotif');
  432.         if ($confirmpassword != $passwordparent) {
  433.             return new JsonResponse('erorpasswordconfirm');
  434.         }
  435.         $role "ROLE_PARENT";
  436.         $verification $UserService->verifmailold($mailparent);
  437.         //var_dump($verification);
  438.         if ($verification == NULL) {
  439.             $accomp $UserService->creationNewParent($nom$prenom$mailparent$numtel$role$passwordparent$notifsms$notifmail);
  440.             return new JsonResponse('done');
  441.         } else {
  442.             return new JsonResponse('eror');
  443.         }
  444.     }
  445.     /**
  446.      * @Route("/Accueil5sur5/5sur5", name="sur5")
  447.      */
  448.     public function sur5()
  449.     {
  450.         $produit $this->typeProduiteService;
  451.         $liste $produit->produitlistType();
  452.         //dd($liste);
  453.         return $this->render('Accueil/5sur5.html.twig', [
  454.             'produit' => $liste,
  455.         ]);
  456.     }
  457.     /**
  458.      * @Route("/Parent/Accueil5sur5/5sur5", name="5sur5Parent")
  459.      */
  460.     public function sur5Parent()
  461.     {
  462.         $produit $this->typeProduiteService;
  463.         $liste $produit->produitlistType();
  464.         //dd($liste);
  465.         return $this->render('Accueil/5sur5.html.twig', [
  466.             'produit' => $liste,
  467.         ]);
  468.     }
  469.     /**
  470.      * @Route("/Accueil5sur5/album/{id}", name="album")
  471.      */
  472.     public function produitlist($id)
  473.     {
  474.         $produit $this->typeProduiteService;
  475.         $liste $produit->produitlistType();
  476.         $produit $id;
  477.         //dd($liste);
  478.         return $this->render('Accueil/album.html.twig', [
  479.             'produit' => $liste,
  480.             'showArt' => $produit,
  481.         ]);
  482.     }
  483.     /**
  484.      * @Route("/Parent/Accueil5sur5/album/{id}", name="albumParent")
  485.      */
  486.     public function produitlistParent($id)
  487.     {
  488.         $produit $this->typeProduiteService;
  489.         $liste $produit->produitlistType();
  490.         // dd($liste);
  491.         $produit $id;
  492.         return $this->render('Accueil/album.html.twig', [
  493.             'produit' => $liste,
  494.             'showArt' => $produit,
  495.         ]);
  496.     }
  497.     /**
  498.      * @Route("/Accueil5sur5/Boutique_Souvenirs", name="boutique5sur5_Souvenir")
  499.      */
  500.     public function boutique5sur5()
  501.     {
  502.         $produit $this->typeProduiteService;
  503.         $liste $produit->produitlistTypeConditionnement();
  504.         return $this->render('Accueil/boutique_5sur5.html.twig', [
  505.             'produit' => $liste,
  506.         ]);
  507.     }
  508.     /**
  509.      * @Route("/Accueil5sur5/AlaUne", name="AlaUne")
  510.      */
  511.     public function AlaUne()
  512.     {
  513.         $blogsbolt $this->blogService;
  514.         $list $blogsbolt->allblog();
  515.         //dd($list);
  516.         return $this->render('Accueil/AlaUne.html.twig', ['list' => $list]);
  517.     }
  518.     /**
  519.      * @Route("/Parent/Accueil5sur5/AlaUne", name="AlaUneParent")
  520.      */
  521.     public function AlaUneParent()
  522.     {
  523.         $blogsbolt $this->blogService;
  524.         $list $blogsbolt->allblog();
  525.         //dd($list);
  526.         return $this->render('Accueil/AlaUneParent.html.twig', ['list' => $list]);
  527.     }
  528.     /**
  529.      * @Route("/Accueil5sur5/DetailsAlaUne/{id}", name="DetailsAlaUne")
  530.      */
  531.     public function DetailsAlaUne($id)
  532.     {
  533.         $blo $this->blogService;
  534.         $blog $blo->myblog($id);
  535.         return $this->render('Accueil/DetailsAlaUne.html.twig', ['blog' => $blog]);
  536.     }
  537.     /**
  538.      * @Route("/ServiceClient_5sur5", name="ServiceClient_5sur5")
  539.      * */
  540.     public function ServiceClient_5sur5()
  541.     {
  542.         $produit $this->typeProduiteService;
  543.         $liste $produit->produitlistType();
  544.         // dd($liste);
  545.         return $this->render('Accueil/ServiceClient_5sur5.html.twig', [
  546.             'produit' => $liste,
  547.         ]);
  548.     }
  549.     /**
  550.      * @Route("/Besoindaide_5sur5_accueil", name="Besoindaide_5sur5_accueil")
  551.      */
  552.     public function Besoindaide_5sur5_accueil(): Response
  553.     {
  554.         $produit $this->typeProduiteService;
  555.         $liste $produit->produitlistType();
  556.         // dd($liste);
  557.         return $this->render('Accueil/besoindaide_5sur5_accueil.html.twig', [
  558.             'produit' => $liste,
  559.         ]);
  560.     }
  561.     /**
  562.      * @Route("/help/microphone", name="help_microphone")
  563.      */
  564.     public function helpMicrophone(): Response
  565.     {
  566.         return $this->render('Accueil/help_microphone.html.twig', []);
  567.     }
  568.     /**
  569.      * @Route("/Mentionlegale_5sur5", name="Mentionlegale_5sur5")
  570.      */
  571.     public function Mentionlegale()
  572.     {
  573.         return $this->render('Accueil/mentionlegal.html.twig', []);
  574.     }
  575.     /**
  576.      * @Route("/Conditons_generales_5sur5", name="Conditons_generales_5sur5")
  577.      */
  578.     public function Conditons_generales()
  579.     {
  580.         return $this->render('Accueil/ConditionsGenerales.html.twig', []);
  581.     }
  582.     /**
  583.      * @Route("/Politique_Confidentialite_5sur5", name="Politique_Confidentialite_5sur5")
  584.      */
  585.     public function PolitiqueConfidentialite()
  586.     {
  587.         return $this->render('Accueil/PolitiqueConfidentialite.html.twig', []);
  588.     }
  589.     /**
  590.      * @Route("/unsubscribe-user", name="unsubscribe_user")
  591.      */
  592.     public function unsubscribeUser(Request $requestEntityManagerInterface $em): Response
  593.     {
  594.         $email $request->query->get('email');
  595.         if (!$email) {
  596.             return $this->render('unsubscribe/error.html.twig', [
  597.                 'message' => 'Email manquant'
  598.             ]);
  599.         }
  600.         $user $em->getRepository(User::class)->findOneBy(['email' => $email]);
  601.         if (!$user) {
  602.             return $this->render('unsubscribe/error.html.twig', [
  603.                 'message' => 'Utilisateur non trouvĂ©'
  604.             ]);
  605.         }
  606.         $user->setShowpubprod('unsuscribe');
  607.         $em->flush();
  608.         return $this->render('unsubscribe/success.html.twig', [
  609.             'email' => $email
  610.         ]);
  611.     }
  612.     /**
  613.      * @Route("/contact", name="app_contact", methods={"POST"})
  614.      */
  615.     public function processContactForm(Request $request): Response
  616.     {
  617.         try {
  618.             // RĂ©cupĂ©rer les donnĂ©es du formulaire (support des anciens et nouveaux noms)
  619.             $name $request->request->get('name') ?: $request->request->get('fullname');
  620.             $organization $request->request->get('organization') ?: $request->request->get('org');
  621.             $email $request->request->get('email');
  622.             $telephone $request->request->get('telephone') ?: $request->request->get('phone');
  623.             $sejoursCount $request->request->get('sejours_count') ?: $request->request->get('sejours');
  624.             $message $request->request->get('message''');
  625.             $topic $request->request->get('topic''contact');
  626.             $objet $request->request->get('objet''contact');
  627.             $pack $request->request->get('pack''');
  628.             $consent $request->request->get('consent');
  629.             // Support ancien format avec 'subject'
  630.             $subject $request->request->get('subject');
  631.             // Validation des donnĂ©es
  632.             if (empty($name) || empty($email) || empty($organization) || empty($consent)) {
  633.                 return new JsonResponse([
  634.                     'success' => false,
  635.                     'message' => 'Tous les champs obligatoires doivent ĂȘtre remplis (nom, email, Ă©tablissement, consentement).'
  636.                 ], 400);
  637.             }
  638.             if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  639.                 return new JsonResponse([
  640.                     'success' => false,
  641.                     'message' => 'L\'adresse email n\'est pas valide.'
  642.                 ], 400);
  643.             }
  644.             // PrĂ©parer le sujet selon le type de demande
  645.             if (empty($subject)) {
  646.                 $currentDate = new \DateTime();
  647.                 if ($objet === 'expert') {
  648.                     $subject 'Demande expert_' $currentDate->format('d/m/Y');
  649.                 } elseif ($objet === 'demo' || $topic === 'demo') {
  650.                     $subject 'Demande demo_' $currentDate->format('d/m/Y');
  651.                 } else {
  652.                     $subject 'Nouveau message de contact - 5sur5sĂ©jour';
  653.                 }
  654.             }
  655.             // CrĂ©er l'email
  656.             $emailContent = (new TemplatedEmail())
  657.                 ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  658.                 ->to(new Address('ramzi.benlarbi@gmail.com''Ramzi Benlarbi'))
  659.                 ->addCc(new Address('yousra.tlich@gmail.com''Yousra Tlich'))
  660.                 ->addCc(new Address('partenariat@5sur5sejour.com''Partenariat 5sur5sĂ©jour'))
  661.                 ->subject($subject)
  662.                 ->htmlTemplate('emails/contact_form.html.twig')
  663.                 ->context([
  664.                     'name' => $name,
  665.                     'organization' => $organization,
  666.                     'email' => $email,
  667.                     'telephone' => $telephone,
  668.                     'sejours_count' => $sejoursCount,
  669.                     'subject' => $subject,
  670.                     'message' => $message,
  671.                     'topic' => $topic,
  672.                     'objet' => $objet,
  673.                     'pack' => $pack,
  674.                     'date' => new \DateTime()
  675.                 ]);
  676.             // Envoyer l'email
  677.             $this->mailer->send($emailContent);
  678.             // Logger le succĂšs
  679.             $this->logger->info('Email de contact envoyĂ© avec succĂšs', [
  680.                 'from' => $email,
  681.                 'name' => $name,
  682.                 'subject' => $subject
  683.             ]);
  684.             return new JsonResponse([
  685.                 'success' => true,
  686.                 'message' => 'Votre message a Ă©tĂ© envoyĂ© avec succĂšs ! Nous vous rĂ©pondrons dans les plus brefs dĂ©lais.'
  687.             ]);
  688.         } catch (\Exception $e) {
  689.             // Logger l'erreur
  690.             $this->logger->error('Erreur lors de l\'envoi de l\'email de contact', [
  691.                 'error' => $e->getMessage(),
  692.                 'trace' => $e->getTraceAsString()
  693.             ]);
  694.             return new JsonResponse([
  695.                 'success' => false,
  696.                 'message' => 'Une erreur est survenue lors de l\'envoi du message. Veuillez rĂ©essayer plus tard.'
  697.             ], 500);
  698.         }
  699.     }
  700.     /**
  701.      * @Route("/contact/grand-volume", name="app_contact_grand_volume", methods={"POST"})
  702.      */
  703.     public function processGrandVolumeForm(Request $request\App\Service\RecaptchaService $recaptchaService): JsonResponse
  704.     {
  705.         try {
  706.             // RĂ©cupĂ©rer les donnĂ©es du formulaire
  707.             $nomStructure $request->request->get('nom_structure');
  708.             $typeStructure $request->request->get('type_structure');
  709.             $nombreSejours $request->request->get('nombre_sejours');
  710.             $contactEmail $request->request->get('contact_email');
  711.             $contactTelephone $request->request->get('contact_telephone');
  712.             $message $request->request->get('message''');
  713.             $acceptRgpd $request->request->get('accept_rgpd');
  714.             // ===== VALIDATION RECAPTCHA V3 =====
  715.             $recaptchaToken $request->request->get('recaptcha_token''');
  716.             $recaptchaResult $recaptchaService->verify($recaptchaToken'grand_volume'$request->getClientIp());
  717.             
  718.             if (!$recaptchaResult['success']) {
  719.                 $this->logger->warning('đŸ€– Ă‰chec validation reCAPTCHA sur grand volume', [
  720.                     'ip' => $request->getClientIp(),
  721.                     'error' => $recaptchaResult['error'],
  722.                     'email' => $contactEmail
  723.                 ]);
  724.                 return new JsonResponse([
  725.                     'success' => false,
  726.                     'message' => 'Validation de sĂ©curitĂ© Ă©chouĂ©e. Veuillez rĂ©essayer.'
  727.                 ], 429);
  728.             }
  729.             // Validation des donnĂ©es
  730.             if (
  731.                 empty($nomStructure) || empty($typeStructure) || empty($nombreSejours) ||
  732.                 empty($contactEmail) || empty($contactTelephone) || empty($acceptRgpd)
  733.             ) {
  734.                 return new JsonResponse([
  735.                     'success' => false,
  736.                     'message' => 'Tous les champs obligatoires doivent ĂȘtre remplis.'
  737.                 ], 400);
  738.             }
  739.             if (!filter_var($contactEmailFILTER_VALIDATE_EMAIL)) {
  740.                 return new JsonResponse([
  741.                     'success' => false,
  742.                     'message' => 'L\'adresse email n\'est pas valide.'
  743.                 ], 400);
  744.             }
  745.             if (intval($nombreSejours) < 26) {
  746.                 return new JsonResponse([
  747.                     'success' => false,
  748.                     'message' => 'Le nombre de sĂ©jours doit ĂȘtre d\'au moins 26 par an.'
  749.                 ], 400);
  750.             }
  751.             // PrĂ©parer le sujet
  752.             $subject 'Demande de devis Grand Volume - ' $nomStructure ' (' $nombreSejours ' sĂ©jours/an)';
  753.             // CrĂ©er l'email pour l'Ă©quipe
  754.             try {
  755.                 $emailContent = (new TemplatedEmail())
  756.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  757.                     ->to('partenariat@5sur5sejour.com')
  758.                     ->addTo('yousra.tlich@gmail.com')
  759.                     ->addTo('ramzi.benlarbi@gmail.com')
  760.                     ->subject($subject)
  761.                     ->htmlTemplate('emails/grand_volume_request.html.twig')
  762.                     ->context([
  763.                         'nomStructure' => $nomStructure,
  764.                         'typeStructure' => $typeStructure,
  765.                         'nombreSejours' => $nombreSejours,
  766.                         'contactEmail' => $contactEmail,
  767.                         'contactTelephone' => $contactTelephone,
  768.                         'message' => $message
  769.                     ]);
  770.                 $this->mailer->send($emailContent);
  771.             } catch (\Exception $emailException) {
  772.                 $this->logger->error('Erreur lors de l\'envoi de l\'email Ă  l\'Ă©quipe', [
  773.                     'error' => $emailException->getMessage()
  774.                 ]);
  775.                 throw $emailException;
  776.             }
  777.             // Email de confirmation au client
  778.             try {
  779.                 $clientEmail = (new TemplatedEmail())
  780.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  781.                     ->to($contactEmail)
  782.                     ->subject('Votre demande de devis Grand Volume a Ă©tĂ© reçue')
  783.                     ->htmlTemplate('emails/grand_volume_confirmation.html.twig')
  784.                     ->context([
  785.                         'nomStructure' => $nomStructure
  786.                     ]);
  787.                 $this->mailer->send($clientEmail);
  788.             } catch (\Exception $emailException) {
  789.                 // On log l'erreur mais on continue car l'email principal a Ă©tĂ© envoyĂ©
  790.                 $this->logger->warning('Erreur lors de l\'envoi de l\'email de confirmation au client', [
  791.                     'error' => $emailException->getMessage()
  792.                 ]);
  793.             }
  794.             return new JsonResponse([
  795.                 'success' => true,
  796.                 'message' => 'Votre demande de devis a Ă©tĂ© envoyĂ©e avec succĂšs ! Notre Ă©quipe vous contactera dans les plus brefs dĂ©lais pour vous proposer une offre personnalisĂ©e.'
  797.             ]);
  798.         } catch (\Exception $e) {
  799.             $this->logger->error('Erreur lors de l\'envoi du formulaire grand volume', [
  800.                 'error' => $e->getMessage(),
  801.                 'file' => $e->getFile(),
  802.                 'line' => $e->getLine(),
  803.                 'trace' => $e->getTraceAsString()
  804.             ]);
  805.             // En mode dev, retourner le message d'erreur dĂ©taillĂ©
  806.             if ($this->getParameter('kernel.environment') === 'dev') {
  807.                 return new JsonResponse([
  808.                     'success' => false,
  809.                     'message' => 'Erreur : ' $e->getMessage() . ' (ligne ' $e->getLine() . ')'
  810.                 ], 500);
  811.             }
  812.             return new JsonResponse([
  813.                 'success' => false,
  814.                 'message' => 'Une erreur est survenue lors de l\'envoi de votre demande. Veuillez rĂ©essayer plus tard.'
  815.             ], 500);
  816.         }
  817.     }
  818.     /**
  819.      * @Route("/contact/boite-vocale-devis", name="app_contact_boite_vocale_devis", methods={"POST"})
  820.      */
  821.     public function processBoiteVocaleDevisForm(Request $request): JsonResponse
  822.     {
  823.         try {
  824.             // RĂ©cupĂ©rer les donnĂ©es du formulaire
  825.             $nomStructure $request->request->get('nom_structure');
  826.             $typeStructure $request->request->get('type_structure');
  827.             $nombreParticipants $request->request->get('nombre_participants');
  828.             $dateDebut $request->request->get('date_debut');
  829.             $dateFin $request->request->get('date_fin');
  830.             $themeSejour $request->request->get('theme_sejour');
  831.             $contactEmail $request->request->get('contact_email');
  832.             $contactTelephone $request->request->get('contact_telephone');
  833.             $message $request->request->get('message''');
  834.             $acceptRgpd $request->request->get('accept_rgpd');
  835.             // Validation des donnĂ©es
  836.             if (
  837.                 empty($nomStructure) || empty($typeStructure) || empty($nombreParticipants) ||
  838.                 empty($dateDebut) || empty($dateFin) || empty($themeSejour) ||
  839.                 empty($contactEmail) || empty($contactTelephone) || empty($acceptRgpd)
  840.             ) {
  841.                 return new JsonResponse([
  842.                     'success' => false,
  843.                     'message' => 'Tous les champs obligatoires doivent ĂȘtre remplis.'
  844.                 ], 400);
  845.             }
  846.             if (!filter_var($contactEmailFILTER_VALIDATE_EMAIL)) {
  847.                 return new JsonResponse([
  848.                     'success' => false,
  849.                     'message' => 'L\'adresse email n\'est pas valide.'
  850.                 ], 400);
  851.             }
  852.             if (intval($nombreParticipants) < 71) {
  853.                 return new JsonResponse([
  854.                     'success' => false,
  855.                     'message' => 'Le nombre de participants doit ĂȘtre d\'au moins 71 pour un grand sĂ©jour.'
  856.                 ], 400);
  857.             }
  858.             // Validation des dates
  859.             $dateDebutObj = new \DateTime($dateDebut);
  860.             $dateFinObj = new \DateTime($dateFin);
  861.             if ($dateFinObj $dateDebutObj) {
  862.                 return new JsonResponse([
  863.                     'success' => false,
  864.                     'message' => 'La date de fin doit ĂȘtre postĂ©rieure Ă  la date de dĂ©but.'
  865.                 ], 400);
  866.             }
  867.             // PrĂ©parer le sujet
  868.             $subject 'Demande de devis BoĂźte Vocale - ' $nomStructure ' (' $nombreParticipants ' participants)';
  869.             // CrĂ©er l'email pour l'Ă©quipe
  870.             try {
  871.                 $emailContent = (new TemplatedEmail())
  872.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  873.                     ->to('partenariat@5sur5sejour.com')
  874.                     ->addTo('yousra.tlich@gmail.com')
  875.                     ->addTo('ramzi.benlarbi@gmail.com')
  876.                     ->subject($subject)
  877.                     ->htmlTemplate('emails/boite_vocale/devis_request.html.twig')
  878.                     ->context([
  879.                         'nomStructure' => $nomStructure,
  880.                         'typeStructure' => $typeStructure,
  881.                         'nombreParticipants' => $nombreParticipants,
  882.                         'dateDebut' => $dateDebut,
  883.                         'dateFin' => $dateFin,
  884.                         'themeSejour' => $themeSejour,
  885.                         'contactEmail' => $contactEmail,
  886.                         'contactTelephone' => $contactTelephone,
  887.                         'message' => $message
  888.                     ]);
  889.                 $this->mailer->send($emailContent);
  890.             } catch (\Exception $emailException) {
  891.                 $this->logger->error('Erreur lors de l\'envoi de l\'email Ă  l\'Ă©quipe (boĂźte vocale)', [
  892.                     'error' => $emailException->getMessage()
  893.                 ]);
  894.                 throw $emailException;
  895.             }
  896.             // Email de confirmation au client
  897.             try {
  898.                 $clientEmail = (new TemplatedEmail())
  899.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  900.                     ->to($contactEmail)
  901.                     ->subject('Votre demande de devis BoĂźte Vocale a Ă©tĂ© reçue')
  902.                     ->htmlTemplate('emails/boite_vocale/devis_confirmation.html.twig')
  903.                     ->context([
  904.                         'nomStructure' => $nomStructure
  905.                     ]);
  906.                 $this->mailer->send($clientEmail);
  907.             } catch (\Exception $emailException) {
  908.                 // On log l'erreur mais on continue car l'email principal a Ă©tĂ© envoyĂ©
  909.                 $this->logger->warning('Erreur lors de l\'envoi de l\'email de confirmation au client (boĂźte vocale)', [
  910.                     'error' => $emailException->getMessage()
  911.                 ]);
  912.             }
  913.             return new JsonResponse([
  914.                 'success' => true,
  915.                 'message' => 'Votre demande de devis a Ă©tĂ© envoyĂ©e avec succĂšs ! Notre Ă©quipe vous contactera dans les plus brefs dĂ©lais pour vous proposer une offre personnalisĂ©e.'
  916.             ]);
  917.         } catch (\Exception $e) {
  918.             $this->logger->error('Erreur lors de l\'envoi du formulaire devis boĂźte vocale', [
  919.                 'error' => $e->getMessage(),
  920.                 'file' => $e->getFile(),
  921.                 'line' => $e->getLine(),
  922.                 'trace' => $e->getTraceAsString()
  923.             ]);
  924.             // En mode dev, retourner le message d'erreur dĂ©taillĂ©
  925.             if ($this->getParameter('kernel.environment') === 'dev') {
  926.                 return new JsonResponse([
  927.                     'success' => false,
  928.                     'message' => 'Erreur : ' $e->getMessage() . ' (ligne ' $e->getLine() . ')'
  929.                 ], 500);
  930.             }
  931.             return new JsonResponse([
  932.                 'success' => false,
  933.                 'message' => 'Une erreur est survenue lors de l\'envoi de votre demande. Veuillez rĂ©essayer plus tard.'
  934.             ], 500);
  935.         }
  936.     }
  937.     /**
  938.      * @Route("/contact/demo", name="app_contact_demo", methods={"POST"})
  939.      */
  940.     public function processDemoForm(Request $request): Response
  941.     {
  942.         try {
  943.             // RĂ©cupĂ©rer les donnĂ©es du formulaire de dĂ©mo
  944.             $fullname $request->request->get('fullname');
  945.             $org $request->request->get('org');
  946.             $email $request->request->get('email');
  947.             $phone $request->request->get('phone');
  948.             $message $request->request->get('message');
  949.             $topic $request->request->get('topic');
  950.             $pack $request->request->get('pack');
  951.             $consent $request->request->get('consent');
  952.             // Validation des donnĂ©es
  953.             if (empty($fullname) || empty($org) || empty($email) || empty($consent)) {
  954.                 return new JsonResponse([
  955.                     'success' => false,
  956.                     'message' => 'Tous les champs obligatoires doivent ĂȘtre remplis et le consentement acceptĂ©.'
  957.                 ], 400);
  958.             }
  959.             if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  960.                 return new JsonResponse([
  961.                     'success' => false,
  962.                     'message' => 'L\'adresse email n\'est pas valide.'
  963.                 ], 400);
  964.             }
  965.             // PrĂ©parer le sujet avec la date
  966.             $currentDate = new \DateTime();
  967.             $subject 'Demande demo_' $currentDate->format('d/m/Y');
  968.             // CrĂ©er l'email pour la demande de dĂ©mo
  969.             $emailContent = (new TemplatedEmail())
  970.                 ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  971.                 ->to(new Address('ramzi.benlarbi@gmail.com''Ramzi Benlarbi'))
  972.                 ->addCc(new Address('yousra.tlich@gmail.com''Yousra Tlich'))
  973.                 ->addCc(new Address('partenariat@5sur5sejour.com''Partenariat 5sur5sĂ©jour'))
  974.                 ->subject($subject)
  975.                 ->htmlTemplate('emails/demo_request.html.twig')
  976.                 ->context([
  977.                     'fullname' => $fullname,
  978.                     'org' => $org,
  979.                     'email' => $email,
  980.                     'phone' => $phone,
  981.                     'message' => $message,
  982.                     'topic' => $topic,
  983.                     'pack' => $pack,
  984.                     'date' => $currentDate
  985.                 ]);
  986.             // Envoyer l'email
  987.             $this->mailer->send($emailContent);
  988.             // Logger le succĂšs
  989.             $this->logger->info('Email de demande de dĂ©mo envoyĂ© avec succĂšs', [
  990.                 'from' => $email,
  991.                 'fullname' => $fullname,
  992.                 'org' => $org,
  993.                 'topic' => $topic
  994.             ]);
  995.             return new JsonResponse([
  996.                 'success' => true,
  997.                 'message' => 'Votre demande de dĂ©mo a Ă©tĂ© envoyĂ©e avec succĂšs ! Nous vous recontacterons dans les 24 heures.'
  998.             ]);
  999.         } catch (\Exception $e) {
  1000.             // Logger l'erreur
  1001.             $this->logger->error('Erreur lors de l\'envoi de l\'email de demande de dĂ©mo', [
  1002.                 'error' => $e->getMessage(),
  1003.                 'trace' => $e->getTraceAsString()
  1004.             ]);
  1005.             return new JsonResponse([
  1006.                 'success' => false,
  1007.                 'message' => 'Une erreur est survenue lors de l\'envoi de la demande. Veuillez rĂ©essayer plus tard.'
  1008.             ], 500);
  1009.         }
  1010.     }
  1011.     /**
  1012.      * @Route("/Accueil5sur5/Pack_Access", name="pack_access")
  1013.      * @Route("/Accueil5sur5/Pack_Decouverte", name="pack_decouverte")
  1014.      */
  1015.     public function packAccess()
  1016.     {
  1017.         return $this->render('Accueil/pack_access.html.twig');
  1018.     }
  1019.     /**
  1020.      * @Route("/Accueil5sur5/Pack_Annuel_Partenaires", name="pack_annuel_partenaires")
  1021.      * 
  1022.      */
  1023.     public function packAnnuelPartenaires()
  1024.     {
  1025.         return $this->render('Accueil/pack_annuel_partenaires.html.twig');
  1026.     }
  1027.     /**
  1028.      * @Route("/Accueil5sur5/Boite_Vocale", name="boite_vocale")
  1029.      */
  1030.     public function boiteVocale()
  1031.     {
  1032.         return $this->render('Accueil/boite_vocale.html.twig');
  1033.     }
  1034.     /**
  1035.      * @Route("/Accueil5sur5/Commande_Groupee", name="commande_groupee_accueil")
  1036.      */
  1037.     public function commandeGroupeeAccueil()
  1038.     {
  1039.         return $this->render('Accueil/CommandeGroupee.html.twig');
  1040.     }
  1041.     /**
  1042.      * @Route("/checkout/boite-vocale/{pack}", name="checkout_boite_vocale", defaults={"pack"="35"})
  1043.      */
  1044.     public function checkoutBoiteVocale(string $pack)
  1045.     {
  1046.         // Configuration des packs
  1047.         $packConfigs = [
  1048.             '35' => ['name' => 'Pack 35 enfants''price' => 40'max' => 35],
  1049.             '70' => ['name' => 'Pack 70 enfants''price' => 70'max' => 70],
  1050.             'plus' => ['name' => 'Grand sĂ©jour''price' => 0'max' => null]
  1051.         ];
  1052.         $packConfig $packConfigs[$pack] ?? $packConfigs['35'];
  1053.         return $this->render('Accueil/checkout_boite_vocale.html.twig', [
  1054.             'pack_type' => $pack,
  1055.             'pack_config' => $packConfig
  1056.         ]);
  1057.     }
  1058.     /**
  1059.      * @Route("/checkout/process-boite-vocale", name="checkout_process_boite_vocale", methods={"POST"})
  1060.      */
  1061.     public function processCheckoutBoiteVocale(Request $requestEntityManagerInterface $em\App\Service\RecaptchaService $recaptchaService)
  1062.     {
  1063.         try {
  1064.             // RĂ©cupĂ©rer les donnĂ©es du formulaire
  1065.             $data = [
  1066.                 'company_name' => $request->request->get('company_name'),
  1067.                 'company_type' => $request->request->get('company_type'),
  1068.                 'phone' => $request->request->get('phone'),
  1069.                 'address' => $request->request->get('address'),
  1070.                 'postal_code' => $request->request->get('postal_code'),
  1071.                 'city' => $request->request->get('city'),
  1072.                 'first_name' => $request->request->get('first_name'),
  1073.                 'last_name' => $request->request->get('last_name'),
  1074.                 'email' => $request->request->get('email'),
  1075.                 'role' => $request->request->get('role'),
  1076.                 'date_debut' => $request->request->get('date_debut'),
  1077.                 'date_fin' => $request->request->get('date_fin'),
  1078.                 'nombre_participants' => $request->request->get('nombre_participants'),
  1079.                 'theme_sejour' => $request->request->get('theme_sejour'),
  1080.                 'adresse_sejour' => $request->request->get('adresse_sejour'),
  1081.                 'pack_type' => $request->request->get('pack_type''35'),
  1082.                 'payment_method' => $request->request->get('payment_method''online')
  1083.             ];
  1084.             // ===== VALIDATION RECAPTCHA V3 =====
  1085.             $recaptchaToken $request->request->get('recaptcha_token''');
  1086.             $recaptchaResult $recaptchaService->verify($recaptchaToken'checkout_boite_vocale'$request->getClientIp());
  1087.             
  1088.             if (!$recaptchaResult['success']) {
  1089.                 $this->logger->warning('đŸ€– Ă‰chec validation reCAPTCHA sur checkout boite vocale', [
  1090.                     'ip' => $request->getClientIp(),
  1091.                     'error' => $recaptchaResult['error'],
  1092.                     'email' => $data['email']
  1093.                 ]);
  1094.                 return new JsonResponse([
  1095.                     'success' => false,
  1096.                     'error' => 'Validation de sĂ©curitĂ© Ă©chouĂ©e. Veuillez rĂ©essayer.'
  1097.                 ], 429);
  1098.             }
  1099.             // Validation des champs obligatoires
  1100.             $requiredFields = ['company_name''phone''address''postal_code''city''first_name''last_name''email''date_debut''date_fin''nombre_participants''theme_sejour''adresse_sejour'];
  1101.             foreach ($requiredFields as $field) {
  1102.                 if (empty($data[$field])) {
  1103.                     return new JsonResponse([
  1104.                         'success' => false,
  1105.                         'error' => "Le champ {$field} est obligatoire"
  1106.                     ], 400);
  1107.                 }
  1108.             }
  1109.             // DĂ©terminer les prix selon le pack
  1110.             $packPrices = [
  1111.                 '35' => ['ht' => 40'ttc' => 48],
  1112.                 '70' => ['ht' => 70'ttc' => 84],
  1113.                 'plus' => ['ht' => 0'ttc' => 0// Sur devis
  1114.             ];
  1115.             $prices $packPrices[$data['pack_type']] ?? $packPrices['35'];
  1116.             // CrĂ©er ou rĂ©cupĂ©rer l'utilisateur
  1117.             $userRepository $em->getRepository(User::class);
  1118.             $user $userRepository->findOneBy(['email' => $data['email']]);
  1119.             if (!$user) {
  1120.                 $user = new User();
  1121.                 $user->setEmail($data['email']);
  1122.                 $user->setNom($data['last_name']);
  1123.                 $user->setPrenom($data['first_name']);
  1124.                 $user->setNummobile($data['phone']);
  1125.                 $user->setNometablisment($data['company_name']);
  1126.                 $mdpTemp substr(md5(uniqid()), 08);
  1127.                 $user->setPassword(password_hash($mdpTempPASSWORD_BCRYPT));
  1128.                 $user->setRoles(json_encode(['ROLE_PARTENAIRE']));
  1129.                 // Fetch Ref entity for status
  1130.                 $statutActif $em->getRepository(Ref::class)->find(1);
  1131.                 if (!$statutActif) {
  1132.                     $statutActif = new Ref();
  1133.                     $statutActif->setLibiller('Actif');
  1134.                     $em->persist($statutActif);
  1135.                     $em->flush();
  1136.                 }
  1137.                 $user->setStatut($statutActif);
  1138.                 $em->persist($user);
  1139.                 $em->flush();
  1140.             }
  1141.             // RĂ©cupĂ©rer le statut "en attente"
  1142.             $statutEnAttente $em->getRepository(Ref::class)->find(1);
  1143.             if (!$statutEnAttente) {
  1144.                 $statutEnAttente = new Ref();
  1145.                 $statutEnAttente->setLibiller('attente_paiement');
  1146.                 $em->persist($statutEnAttente);
  1147.                 $em->flush();
  1148.             }
  1149.             // CrĂ©er la commande
  1150.             $commande = new Commande();
  1151.             $commande->setIdUser($user);
  1152.             $commande->setStatut($statutEnAttente);
  1153.             $commande->setMontantht($prices['ht']);
  1154.             $commande->setMontantrth($prices['ttc']);
  1155.             $commande->setMontanenv($prices['ttc']);
  1156.             $commande->setTva(20);
  1157.             $commande->setDateCreateCommande(new \DateTime());
  1158.             $commande->setPeriode('boite_vocale'); // Identifier comme commande boĂźte vocale
  1159.             $commande->setNumComande(time());
  1160.             // Normaliser la mĂ©thode de paiement
  1161.             $methodNormalized strtolower($data['payment_method']);
  1162.             $aliases = [
  1163.                 'online' => 'stripe',
  1164.                 'cb' => 'stripe',
  1165.                 'payplug' => 'stripe',
  1166.                 'card' => 'stripe',
  1167.                 'bank' => 'rib',
  1168.                 'transfer' => 'rib',
  1169.                 'virement' => 'rib',
  1170.                 'bank_transfer' => 'rib'
  1171.             ];
  1172.             if (isset($aliases[$methodNormalized])) {
  1173.                 $methodNormalized $aliases[$methodNormalized];
  1174.             }
  1175.             $commande->setPaymentType($methodNormalized);
  1176.             // Stocker les infos du sĂ©jour dans un champ JSON ou commentaire (Ă  adapter selon votre structure)
  1177.             // Pour l'instant, on stocke dans le numĂ©ro de commande comme rĂ©fĂ©rence
  1178.             $commande->setNumComande((int)time());
  1179.             $em->persist($commande);
  1180.             $em->flush();
  1181.             // Stocker les donnĂ©es en session pour la page de confirmation
  1182.             $session $request->getSession();
  1183.             $session->set('checkout_data', [
  1184.                 'commande_id' => $commande->getId(),
  1185.                 'type' => 'boite_vocale',
  1186.                 'pack_type' => $data['pack_type'],
  1187.                 'montant_ht' => $prices['ht'],
  1188.                 'montant_ttc' => $prices['ttc'],
  1189.                 'payment_method' => $methodNormalized,
  1190.                 'user' => $user,
  1191.                 'data' => $data
  1192.             ]);
  1193.             // Traiter selon la mĂ©thode de paiement
  1194.             if ($methodNormalized === 'stripe') {
  1195.                 // Return command ID for Up2Pay form submission
  1196.                 return new JsonResponse([
  1197.                     'success' => true,
  1198.                     'paymentType' => 'up2pay',
  1199.                     'message' => 'Redirection vers le paiement sĂ©curisĂ© Up2Pay...',
  1200.                     'commandeId' => $commande->getId()
  1201.                 ]);
  1202.             } else {
  1203.                 // RIB - Envoyer l'email avec les coordonnĂ©es bancaires
  1204.                 try {
  1205.                     $email = (new TemplatedEmail())
  1206.                         ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  1207.                         ->to($user->getEmail())
  1208.                         ->cc('yousra.tlich@gmail.com''ramzi.benlarbi@gmail.com')
  1209.                         ->subject('Virement bancaire - BoĂźte Vocale - Commande N°' $commande->getId())
  1210.                         ->htmlTemplate('emails/boite_vocale/rib_commande.html.twig')
  1211.                         ->context([
  1212.                             'user' => $user,
  1213.                             'commande' => $commande,
  1214.                             'data' => $data,
  1215.                             'pack_type' => $data['pack_type'],
  1216.                             'pack_label' => $data['pack_type'] === '35' 'Pack 35 enfants' : ($data['pack_type'] === '70' 'Pack 70 enfants' 'Grand sĂ©jour'),
  1217.                             'montant_ht' => $prices['ht'],
  1218.                             'montant_ttc' => $prices['ttc']
  1219.                         ]);
  1220.                     $this->mailer->send($email);
  1221.                 } catch (\Exception $e) {
  1222.                     error_log("⚠ Erreur envoi email RIB: " $e->getMessage());
  1223.                 }
  1224.                 // Retourner succĂšs avec message pour popup
  1225.                 return new JsonResponse([
  1226.                     'success' => true,
  1227.                     'paymentType' => 'rib',
  1228.                     'message' => 'Votre demande a bien Ă©tĂ© envoyĂ©e, nous reviendrons vers vous dans les plus brefs dĂ©lais.',
  1229.                     'redirectUrl' => $this->generateUrl('checkout_success', ['method' => 'rib'])
  1230.                 ]);
  1231.             }
  1232.         } catch (\Exception $e) {
  1233.             error_log("❌ Erreur checkout boĂźte vocale: " $e->getMessage());
  1234.             error_log($e->getTraceAsString());
  1235.             return new JsonResponse([
  1236.                 'success' => false,
  1237.                 'error' => 'Une erreur est survenue lors du traitement de votre commande. Veuillez rĂ©essayer.'
  1238.             ], 500);
  1239.         }
  1240.     }
  1241.     /**
  1242.      * @Route("/checkout/pack/{pack}", name="checkout_pack", defaults={"pack"="serenite"})
  1243.      */
  1244.     public function checkoutPack(string $pack)
  1245.     {
  1246.         // Configuration des packs
  1247.         $packsConfig = [
  1248.             'decouverte' => [
  1249.                 'id' => 'decouverte',
  1250.                 'name' => 'Pack DĂ©couverte',
  1251.                 'subtitle' => 'Abonnement annuel',
  1252.                 'price' => 290,
  1253.                 'price_ht' => '290 â‚Ź HT',
  1254.                 'onboarding' => 'Inclus',
  1255.                 'vocal_price' => '+5 â‚Ź / sĂ©jour',
  1256.                 'features' => [
  1257.                     'SĂ©jours illimitĂ©s toute l\'annĂ©e',
  1258.                     'Multi-sites pour une Ă©quipe',
  1259.                     'Reporting mensuel & facturation',
  1260.                     'Support prioritaire individuel'
  1261.                 ],
  1262.                 'info' => 'Plusieurs sĂ©jours par an ? Tarif dĂ©gressif automatique !'
  1263.             ],
  1264.             'serenite' => [
  1265.                 'id' => 'serenite',
  1266.                 'name' => 'Pack Annuel â€“ 25 sĂ©jours',
  1267.                 'subtitle' => 'Le plus utilisĂ©',
  1268.                 'description' => 'Le pack idĂ©al pour les structures qui organisent plusieurs sĂ©jours par an. Jusqu\'Ă  25 sĂ©jours inclus dans votre abonnement annuel.',
  1269.                 'price' => 490,
  1270.                 'price_ht' => '490 â‚Ź HT',
  1271.                 'price_suffix' => '/ an / structure',
  1272.                 'onboarding' => 'DĂ©ploiement par l\'Ă©quipe 5sur5',
  1273.                 'vocal_price' => ''// RetirĂ© - plus de voix tĂ©lĂ©phonique
  1274.                 'benefit' => '+90% de connexions parents',
  1275.                 'benefit_subtitle' => 'Pas d\'action de votre part, on s\'occupe de tout mettre en place',
  1276.                 'features' => [
  1277.                     'Support prioritaire',
  1278.                     'Jusqu\'Ă  25 sĂ©jours inclus',
  1279.                     'DĂ©ploiement par l\'Ă©quipe 5sur5',
  1280.                     'Accompagnateurs illimitĂ©s',
  1281.                     'Support premium'
  1282.                 ],
  1283.                 'info' => 'Budget annuel fixe, 0 dĂ©passement. Un prix unique qui couvre tous vos sĂ©jours et tous les parents connectĂ©s. Aucun dĂ©passement, donnĂ©es sĂ©curisĂ©es RGPD en France.',
  1284.                 'additional_info' => 'Que vous organisiez 2 ou 20 sĂ©jours par an, tout est couvert sans frais supplĂ©mentaires. Notre plateforme gĂšre automatiquement les familles, les albums souvenirs et la communication en temps rĂ©el.'
  1285.             ],
  1286.             'pro_illimite' => [
  1287.                 'id' => 'pro_illimite',
  1288.                 'name' => 'Pack Pro IllimitĂ©',
  1289.                 'subtitle' => 'Abonnement annuel',
  1290.                 'price' => 790,
  1291.                 'price_ht' => '790 â‚Ź HT',
  1292.                 'onboarding' => 'Inclus + Formation avancĂ©e',
  1293.                 'vocal_price' => 'Inclus',
  1294.                 'features' => [
  1295.                     'SĂ©jours illimitĂ©s toute l\'annĂ©e',
  1296.                     'Multi-sites pour une Ă©quipe',
  1297.                     'Reporting mensuel & facturation',
  1298.                     'Support prioritaire individuel',
  1299.                     'Voix tĂ©lĂ©phonique illimitĂ©e',
  1300.                     'Formation avancĂ©e de l\'Ă©quipe'
  1301.                 ],
  1302.                 'info' => 'Le meilleur rapport qualitĂ©/prix pour les structures avec plusieurs sĂ©jours !'
  1303.             ]
  1304.         ];
  1305.         // VĂ©rifier que le pack existe
  1306.         if (!isset($packsConfig[$pack])) {
  1307.             throw $this->createNotFoundException('Pack non trouvĂ©');
  1308.         }
  1309.         return $this->render('Accueil/checkout_pack.html.twig', [
  1310.             'pack' => $pack,
  1311.             'packConfig' => $packsConfig[$pack]
  1312.         ]);
  1313.     }
  1314.     /**
  1315.      * @Route("/checkout/process", name="checkout_process_pack", methods={"POST"})
  1316.      */
  1317.     public function checkoutProcessPack(Request $requestMailerInterface $mailer\App\Service\RecaptchaService $recaptchaService)
  1318.     {
  1319.         try {
  1320.             error_log('🔍 CHECKOUT START - Processing form submission');
  1321.             // RĂ©cupĂ©ration des donnĂ©es du formulaire
  1322.             $data = [
  1323.                 'pack_id' => $request->request->get('pack_id'),
  1324.                 'payment_method' => $request->request->get('payment_method'),
  1325.                 'company_name' => $request->request->get('company_name'),
  1326.                 'company_type' => $request->request->get('company_type'),
  1327.                 'siren' => $request->request->get('siren'),
  1328.                 'phone' => $request->request->get('phone'),
  1329.                 'address' => $request->request->get('address'),
  1330.                 'postal_code' => $request->request->get('postal_code'),
  1331.                 'city' => $request->request->get('city'),
  1332.                 'first_name' => $request->request->get('first_name'),
  1333.                 'last_name' => $request->request->get('last_name'),
  1334.                 'email' => $request->request->get('email'),
  1335.                 'role' => $request->request->get('role'),
  1336.                 'need_onboarding' => $request->request->get('need_onboarding') ? true false,
  1337.             ];
  1338.             error_log('📋 Data received: ' json_encode($data));
  1339.             // ===== VALIDATION RECAPTCHA V3 =====
  1340.             $recaptchaToken $request->request->get('recaptcha_token''');
  1341.             $recaptchaResult $recaptchaService->verify($recaptchaToken'checkout_pack'$request->getClientIp());
  1342.             
  1343.             if (!$recaptchaResult['success']) {
  1344.                 $this->logger->warning('đŸ€– Ă‰chec validation reCAPTCHA sur checkout pack', [
  1345.                     'ip' => $request->getClientIp(),
  1346.                     'error' => $recaptchaResult['error'],
  1347.                     'email' => $data['email']
  1348.                 ]);
  1349.                 $this->addFlash('error''Validation de sĂ©curitĂ© Ă©chouĂ©e. Veuillez rĂ©essayer.');
  1350.                 return $this->redirectToRoute('checkout_pack', ['pack' => $data['pack_id'] ?? 'serenite']);
  1351.             }
  1352.             // Validation
  1353.             $requiredFields = [
  1354.                 'pack_id',
  1355.                 'payment_method',
  1356.                 'company_name',
  1357.                 'company_type',
  1358.                 'phone',
  1359.                 'address',
  1360.                 'postal_code',
  1361.                 'city',
  1362.                 'first_name',
  1363.                 'last_name',
  1364.                 'email',
  1365.                 'role'
  1366.             ];
  1367.             foreach ($requiredFields as $field) {
  1368.                 if (empty($data[$field])) {
  1369.                     error_log('❌ Missing required field: ' $field);
  1370.                     $this->addFlash('error''Tous les champs obligatoires doivent ĂȘtre remplis.');
  1371.                     return $this->redirectToRoute('checkout_pack', ['pack' => $data['pack_id'] ?? 'serenite']);
  1372.                 }
  1373.             }
  1374.             error_log('✅ Validation passed');
  1375.             // ==================== CRÉER LA COMMANDE EN BASE DE DONNÉES ====================
  1376.             $em $this->getDoctrine()->getManager();
  1377.             $commande null;
  1378.             // CrĂ©er la commande pour TOUS les modes de paiement (online, transfer, quote)
  1379.             try {
  1380.                 error_log('📩 CrĂ©ation commande pack pour: ' $data['pack_id'] . ' (method: ' $data['payment_method'] . ')');
  1381.                 // CrĂ©er ou rĂ©cupĂ©rer l'utilisateur
  1382.                 $userRepository $em->getRepository(User::class);
  1383.                 $user $userRepository->findOneBy(['email' => $data['email']]);
  1384.                 if (!$user) {
  1385.                     $user = new User();
  1386.                     $user->setEmail($data['email']);
  1387.                     $user->setNom($data['last_name']);
  1388.                     $user->setPrenom($data['first_name']);
  1389.                     $user->setNummobile($data['phone'] ?? '');
  1390.                     $user->setNometablisment($data['company_name'] ?? '');
  1391.                     // Combine address fields into single address field
  1392.                     $fullAddress trim(
  1393.                         ($data['address'] ?? '') . ' ' .
  1394.                         ($data['postal_code'] ?? '') . ' ' .
  1395.                         ($data['city'] ?? '')
  1396.                     );
  1397.                     $user->setAdresse($fullAddress);
  1398.                     $mdpTemp substr(md5(uniqid()), 08);
  1399.                     $user->setPassword(password_hash($mdpTempPASSWORD_BCRYPT));
  1400.                     $user->setRoles(json_encode(['ROLE_PARTENAIRE']));
  1401.                     // Fetch Ref entity for status
  1402.                     $statutActif $em->getRepository(Ref::class)->find(1);
  1403.                     if (!$statutActif) {
  1404.                         $statutActif = new Ref();
  1405.                         $statutActif->setLibiller('Actif');
  1406.                         $em->persist($statutActif);
  1407.                         $em->flush();
  1408.                     }
  1409.                     $user->setStatut($statutActif);
  1410.                     $em->persist($user);
  1411.                     $em->flush();
  1412.                     error_log('✅ Utilisateur créé: ' $user->getEmail());
  1413.                 } else {
  1414.                     error_log('✅ Utilisateur existant: ' $user->getEmail());
  1415.                 }
  1416.                 // DĂ©terminer les montants
  1417.                 $packPrices = [
  1418.                     'serenite' => ['ht' => 490'ttc' => 588],
  1419.                     'decouverte' => ['ht' => 290'ttc' => 348],
  1420.                 ];
  1421.                 $prices $packPrices[$data['pack_id']] ?? $packPrices['serenite'];
  1422.                 // RĂ©cupĂ©rer le statut "en attente"
  1423.                 $statutEnAttente $em->getRepository(Ref::class)->find(1);
  1424.                 if (!$statutEnAttente) {
  1425.                     $statutEnAttente = new Ref();
  1426.                     $statutEnAttente->setLibiller('En attente');
  1427.                     $em->persist($statutEnAttente);
  1428.                     $em->flush();
  1429.                 }
  1430.                 // CrĂ©er la commande
  1431.                 $commande = new Commande();
  1432.                 $commande->setIdUser($user);
  1433.                 $commande->setStatut($statutEnAttente);
  1434.                 $commande->setMontantht($prices['ht']);
  1435.                 $commande->setMontantrth($prices['ttc']);
  1436.                 $commande->setMontanenv($prices['ttc']);
  1437.                 $commande->setTva(20);
  1438.                 $commande->setDateCreateCommande(new \DateTime());
  1439.                 $commande->setDateExpidition((new \DateTime())->modify('+12 months'));
  1440.                 $commande->setPeriode('pack_' $data['pack_id']);
  1441.                 $commande->setPaymentType($data['payment_method']);
  1442.                 $commande->setNumComande(time() . rand(100999));
  1443.                 $em->persist($commande);
  1444.                 $em->flush();
  1445.                 error_log('✅ Commande créée: ID=' $commande->getId() . ' N°' $commande->getNumComande());
  1446.             } catch (\Exception $e) {
  1447.                 error_log('❌ Erreur crĂ©ation commande: ' $e->getMessage());
  1448.                 return new JsonResponse([
  1449.                     'success' => false,
  1450.                     'error' => 'Erreur lors de la crĂ©ation de la commande: ' $e->getMessage()
  1451.                 ], 500);
  1452.             }
  1453.             // Stocker en session AVANT d'envoyer les emails
  1454.             $data['commande_id'] = $commande $commande->getId() : null;
  1455.             $data['commande_numero'] = $commande $commande->getNumComande() : null;
  1456.             $request->getSession()->set('checkout_data'$data);
  1457.             error_log('đŸ’Ÿ Data stored in session');
  1458.             // Envoyer les emails de maniĂšre asynchrone (ne pas bloquer la requĂȘte)
  1459.             try {
  1460.                 error_log('📧 Attempting to send email to team...');
  1461.                 // Envoyer email Ă  l'Ă©quipe 5sur5
  1462.                 $emailToTeam = (new TemplatedEmail())
  1463.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  1464.                     ->to('contact@5sur5sejour.com')
  1465.                     ->cc('yousra.tlich@gmail.com''ramzi.benlarbi@gmail.com')
  1466.                     ->subject('Nouvelle souscription Pack ' strtoupper($data['pack_id']) . ' - ' $data['company_name'])
  1467.                     ->htmlTemplate('emails/checkout_notification_team.html.twig')
  1468.                     ->context([
  1469.                         'data' => $data,
  1470.                         'date' => new \DateTime()
  1471.                     ]);
  1472.                 $mailer->send($emailToTeam);
  1473.                 error_log('✅ Email to team sent');
  1474.                 // Envoyer email de confirmation au client
  1475.                 $emailToClient = (new TemplatedEmail())
  1476.                     ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  1477.                     ->to($data['email'])
  1478.                     ->subject('Confirmation de votre souscription - 5sur5 SĂ©jour')
  1479.                     ->htmlTemplate('emails/checkout_confirmation_client.html.twig')
  1480.                     ->context([
  1481.                         'data' => $data,
  1482.                         'date' => new \DateTime()
  1483.                     ]);
  1484.                 $mailer->send($emailToClient);
  1485.                 error_log('✅ Email to client sent');
  1486.             } catch (\Exception $emailError) {
  1487.                 // Log l'erreur mais ne bloque pas le processus
  1488.                 error_log('⚠ Email error (non-blocking): ' $emailError->getMessage());
  1489.             }
  1490.             // Stocker la commande en session pour PayPlug
  1491.             $request->getSession()->set('commandeActual'$commande->getId());
  1492.             error_log('đŸ’Ÿ Commande stockĂ©e en session: ' $commande->getId());
  1493.             // Redirection selon le mode de paiement
  1494.             error_log('🔄 Redirecting based on method: ' $data['payment_method']);
  1495.             switch ($data['payment_method']) {
  1496.                 case 'online':
  1497.                     // Return command ID for Up2Pay form submission
  1498.                     error_log('💳 PrĂ©paration paiement Up2Pay pour commande #' $commande->getId());
  1499.                     // Return JSON response for AJAX call
  1500.                     return new JsonResponse([
  1501.                         'success' => true,
  1502.                         'paymentType' => 'up2pay',
  1503.                         'message' => 'Redirection vers le paiement sĂ©curisĂ© Up2Pay...',
  1504.                         'commandeId' => $commande->getId()
  1505.                     ]);
  1506.                 case 'transfer':
  1507.                     $this->addFlash('info''Demande de virement enregistrĂ©e. Vous recevrez les coordonnĂ©es bancaires par email.');
  1508.                     return $this->redirectToRoute('checkout_success', ['method' => 'transfer']);
  1509.                 case 'quote':
  1510.                     $this->addFlash('info''Demande de devis enregistrĂ©e. Vous recevrez le devis par email.');
  1511.                     return $this->redirectToRoute('checkout_success', ['method' => 'quote']);
  1512.                 default:
  1513.                     error_log('❌ Invalid payment method: ' $data['payment_method']);
  1514.                     throw new \Exception('Mode de paiement invalide');
  1515.             }
  1516.         } catch (\Exception $e) {
  1517.             error_log('❌ CHECKOUT ERROR: ' $e->getMessage());
  1518.             error_log('Stack trace: ' $e->getTraceAsString());
  1519.             $this->addFlash('error''Une erreur est survenue. Veuillez rĂ©essayer. DĂ©tail: ' $e->getMessage());
  1520.             return $this->redirectToRoute('checkout_pack', ['pack' => $request->request->get('pack_id''serenite')]);
  1521.         }
  1522.     }
  1523.     /**
  1524.      * @Route("/checkout/success/{method}", name="checkout_success")
  1525.      */
  1526.     public function checkoutSuccess(Request $requeststring $method)
  1527.     {
  1528.         $data $request->getSession()->get('checkout_data');
  1529.         if (!$data) {
  1530.             return $this->redirectToRoute('page_Accueil');
  1531.         }
  1532.         return $this->render('Accueil/checkout_success.html.twig', [
  1533.             'data' => $data,
  1534.             'method' => $method
  1535.         ]);
  1536.     }
  1537.     /**
  1538.      * @Route("/Accueil5sur5/Pack_Pro_Illimite", name="pack_pro_illimite")
  1539.      */
  1540.     public function packProIllimite()
  1541.     {
  1542.         return $this->render('Accueil/pack_pro_illimite.html.twig');
  1543.     }
  1544.     /**
  1545.      * @Route("/bank-transfer-request", name="bank_transfer_request", methods={"POST"})
  1546.      */
  1547.     public function bankTransferRequest(Request $requestEntityManagerInterface $em\App\Service\RecaptchaService $recaptchaService): JsonResponse
  1548.     {
  1549.         try {
  1550.             $this->logger->info('🔍 DĂ©but de la demande de RIB');
  1551.             
  1552.             // ===== VALIDATION RECAPTCHA V3 =====
  1553.             $recaptchaToken $request->request->get('recaptcha_token''');
  1554.             $recaptchaResult $recaptchaService->verify($recaptchaToken'rib_request'$request->getClientIp());
  1555.             
  1556.             if (!$recaptchaResult['success']) {
  1557.                 $this->logger->warning('đŸ€– Ă‰chec validation reCAPTCHA sur demande RIB', [
  1558.                     'ip' => $request->getClientIp(),
  1559.                     'error' => $recaptchaResult['error'],
  1560.                     'score' => $recaptchaResult['score'] ?? null
  1561.                 ]);
  1562.                 
  1563.                 return new JsonResponse([
  1564.                     'success' => false,
  1565.                     'message' => 'Validation de sĂ©curitĂ© Ă©chouĂ©e. Veuillez rĂ©essayer.'
  1566.                 ], 429);
  1567.             }
  1568.             // Accepter les deux formats de paramĂštres
  1569.             $fullname $request->request->get('full_name') ?: $request->request->get('rib_fullname');
  1570.             $emailUser $request->request->get('billing_email') ?: $request->request->get('rib_email');
  1571.             $phone $request->request->get('phone') ?: $request->request->get('rib_phone');
  1572.             $org $request->request->get('company') ?: $request->request->get('rib_org');
  1573.             $message $request->request->get('message') ?: $request->request->get('rib_message');
  1574.             // ParamĂštres spĂ©cifiques au pack
  1575.             $packId $request->request->get('pack_id');
  1576.             $participants $request->request->get('participants');
  1577.             $totalAmount $request->request->get('total_amount');
  1578.             $startDate $request->request->get('start_date');
  1579.             $endDate $request->request->get('end_date');
  1580.             $theme $request->request->get('theme');
  1581.             $accompagnateur $request->request->get('accompagnateur');
  1582.             $this->logger->info('📝 DonnĂ©es reçues', [
  1583.                 'fullname' => $fullname,
  1584.                 'email' => $emailUser,
  1585.                 'pack_id' => $packId,
  1586.                 'participants' => $participants,
  1587.                 'total_amount' => $totalAmount,
  1588.                 'theme' => $theme,
  1589.                 'start_date' => $startDate,
  1590.                 'end_date' => $endDate
  1591.             ]);
  1592.             // Validation basique
  1593.             if (empty($fullname) || empty($emailUser)) {
  1594.                 return new JsonResponse([
  1595.                     'success' => false,
  1596.                     'message' => 'Le nom et l\'email sont obligatoires.'
  1597.                 ], 400);
  1598.             }
  1599.             // GĂ©nĂ©rer un numĂ©ro de demande unique
  1600.             $requestNumber 'RIB-' date('Ymd') . '-' strtoupper(substr(md5(uniqid()), 06));
  1601.             $this->logger->info('🔱 NumĂ©ro de demande gĂ©nĂ©rĂ©: ' $requestNumber);
  1602.             // CrĂ©er ou rĂ©cupĂ©rer l'utilisateur accompagnateur
  1603.             try {
  1604.                 $this->logger->info('đŸ‘€ Recherche utilisateur: ' $emailUser);
  1605.                 $userRepository $em->getRepository(User::class);
  1606.                 $user $userRepository->findOneBy(['email' => $emailUser]);
  1607.                 if (!$user) {
  1608.                     $this->logger->info('➕ CrĂ©ation nouvel utilisateur');
  1609.                     $user = new User();
  1610.                     $user->setEmail($emailUser);
  1611.                     $user->setNom($fullname);
  1612.                     $user->setPrenom('');
  1613.                     $user->setNummobile($phone ?? '');
  1614.                     $user->setNometablisment($org ?? '');
  1615.                     // GĂ©nĂ©rer un mot de passe temporaire
  1616.                     $mdpTemp substr(md5(uniqid()), 08);
  1617.                     $user->setPassword(password_hash($mdpTempPASSWORD_BCRYPT));
  1618.                     $user->setRoles(json_encode(['ROLE_ACCOMPAGNATEUR']));
  1619.                     $user->setStatut(1);
  1620.                     $em->persist($user);
  1621.                     $em->flush();
  1622.                     $this->logger->info('✅ Utilisateur créé: ID=' $user->getId());
  1623.                 } else {
  1624.                     $this->logger->info('✅ Utilisateur existant trouvĂ©: ID=' $user->getId());
  1625.                 }
  1626.             } catch (\Exception $userError) {
  1627.                 $this->logger->error('❌ Erreur crĂ©ation utilisateur', [
  1628.                     'error' => $userError->getMessage(),
  1629.                     'trace' => $userError->getTraceAsString()
  1630.                 ]);
  1631.                 throw $userError;
  1632.             }
  1633.             // DĂ©terminer si c'est un pack annuel (serenite/decouverte) ou un sĂ©jour simple
  1634.             $isPack in_array($packId, ['serenite''decouverte''Pack SĂ©rĂ©nitĂ©''Pack DĂ©couverte']);
  1635.             $this->logger->info('🔍 DĂ©tection type de demande', [
  1636.                 'pack_id' => $packId,
  1637.                 'isPack' => $isPack 'OUI' 'NON',
  1638.                 'test_array' => ['serenite''decouverte''Pack SĂ©rĂ©nitĂ©''Pack DĂ©couverte']
  1639.             ]);
  1640.             $sejour null;
  1641.             $commande null;
  1642.             if ($isPack) {
  1643.                 // ==================== CRÉATION COMMANDE PACK ====================
  1644.                 try {
  1645.                     $this->logger->info('📩 CrĂ©ation commande pack: ' $packId);
  1646.                     // Mapping pack â†’ labeletype
  1647.                     $packNames = [
  1648.                         'decouverte' => 'Pack DĂ©couverte',
  1649.                         'serenite' => 'Pack SĂ©rĂ©nitĂ©',
  1650.                         'Pack DĂ©couverte' => 'Pack DĂ©couverte',
  1651.                         'Pack SĂ©rĂ©nitĂ©' => 'Pack SĂ©rĂ©nitĂ©'
  1652.                     ];
  1653.                     $packLabel $packNames[$packId] ?? 'Pack SĂ©rĂ©nitĂ©';
  1654.                     // RĂ©cupĂ©rer le produit en BDD
  1655.                     $typeProduit $em->getRepository(Typeproduit::class)->findOneBy([
  1656.                         'labeletype' => $packLabel
  1657.                     ]);
  1658.                     // RĂ©cupĂ©rer le statut "en attente"
  1659.                     $statutEnAttente $em->getRepository(Ref::class)->find(1);
  1660.                     if (!$statutEnAttente) {
  1661.                         $statutEnAttente = new Ref();
  1662.                         $statutEnAttente->setLibiller('En attente');
  1663.                         $em->persist($statutEnAttente);
  1664.                         $em->flush();
  1665.                     }
  1666.                     // DĂ©terminer les montants du pack
  1667.                     $defaultsByPack = [
  1668.                         'Pack SĂ©rĂ©nitĂ©' => ['ht' => 490'ttc' => 588],
  1669.                         'Pack DĂ©couverte' => ['ht' => 290'ttc' => 348],
  1670.                     ];
  1671.                     $montantHt $typeProduit $typeProduit->getMontantHt() : null;
  1672.                     $montantTtc $typeProduit $typeProduit->getMontantTTC() : null;
  1673.                     if (empty($montantHt) || empty($montantTtc)) {
  1674.                         $fallback $defaultsByPack[$packLabel] ?? $defaultsByPack['Pack SĂ©rĂ©nitĂ©'];
  1675.                         $montantHt $montantHt ?: $fallback['ht'];
  1676.                         $montantTtc $montantTtc ?: $fallback['ttc'];
  1677.                     }
  1678.                     // CrĂ©er la commande avec statut "en attente"
  1679.                     $commande = new Commande();
  1680.                     $commande->setIdUser($user);
  1681.                     $commande->setStatut($statutEnAttente);
  1682.                     $commande->setMontantht($montantHt);
  1683.                     $commande->setMontantrth($montantTtc);
  1684.                     $commande->setMontanenv($montantTtc);
  1685.                     $commande->setTva(20);
  1686.                     // Dates d'abonnement: dĂ©but maintenant, fin dans 12 mois
  1687.                     $startDateTime = new \DateTime();
  1688.                     $endDateTime = (clone $startDateTime)->modify('+12 months');
  1689.                     $commande->setDateCreateCommande($startDateTime);
  1690.                     $commande->setDateExpidition($endDateTime);
  1691.                     $commande->setPeriode('annuel');
  1692.                     $commande->setPaymentType('rib');
  1693.                     $commande->setNumComande(time());
  1694.                     $this->logger->info('đŸ’Ÿ Avant persist/flush commande');
  1695.                     $em->persist($commande);
  1696.                     $this->logger->info('đŸ’Ÿ AprĂšs persist, avant flush');
  1697.                     $em->flush();
  1698.                     $this->logger->info('đŸ’Ÿ AprĂšs flush');
  1699.                     $commandeId $commande->getId();
  1700.                     $this->logger->info('✅ Commande pack créée: ID=' $commandeId ' Montant=' $montantTtc '€');
  1701.                     // VĂ©rifier que la commande existe vraiment en BDD
  1702.                     $commandeVerif $em->getRepository(Commande::class)->find($commandeId);
  1703.                     if ($commandeVerif) {
  1704.                         $this->logger->info('✅ VÉRIFICATION: Commande #' $commandeId ' existe bien en BDD');
  1705.                     } else {
  1706.                         $this->logger->error('❌ VÉRIFICATION: Commande #' $commandeId ' N\'EXISTE PAS en BDD !');
  1707.                     }
  1708.                 } catch (\Exception $commandeError) {
  1709.                     $this->logger->error('❌ Erreur crĂ©ation commande pack', [
  1710.                         'error' => $commandeError->getMessage(),
  1711.                         'trace' => $commandeError->getTraceAsString()
  1712.                     ]);
  1713.                     // NE PAS continuer si la crĂ©ation Ă©choue
  1714.                     return new JsonResponse([
  1715.                         'success' => false,
  1716.                         'message' => 'Erreur lors de la crĂ©ation de la commande: ' $commandeError->getMessage()
  1717.                     ], 500);
  1718.                 }
  1719.             } else {
  1720.                 // ==================== CRÉATION SÉJOUR SIMPLE PF ====================
  1721.                 try {
  1722.                     $this->logger->info('đŸ•ïž CrĂ©ation du sĂ©jour simple PF');
  1723.                     $dateDebut $startDate ?: date('Y-m-d');
  1724.                     $dateFin $endDate ?: date('Y-m-d'strtotime($dateDebut ' +7 days'));
  1725.                     $themeTexte $theme ?: 'SĂ©jour';
  1726.                     $nbParticipants $participants ?: 10;
  1727.                     $sejour $this->sejourService->CreationNouveauSejour(
  1728.                         $themeTexte,              // themSejour
  1729.                         '',                       // adressSejour
  1730.                         '',                       // codePostal
  1731.                         $dateDebut,              // dateDebut
  1732.                         $dateFin,                // FinSejour
  1733.                         '6-10 ans',              // AgeDugroupe
  1734.                         'PF',                    // type (Partenaire FinancĂ©)
  1735.                         $user->getId(),          // userid
  1736.                         $nbParticipants,         // NbEnfant
  1737.                         0,                       // connpay (0 = non payĂ©)
  1738.                         'France',                // pays
  1739.                         '',                      // ville
  1740.                         null,                    // prixcnxparent
  1741.                         null,                    // prixcnxpartenaire
  1742.                         null,                    // reversecnxpart
  1743.                         null                     // reverseventepart
  1744.                     );
  1745.                     // RĂ©cupĂ©rer ou crĂ©er le statut "attente_paiement"
  1746.                     $statutAttentePaiement $em->getRepository(Ref::class)->findOneBy(['libiller' => 'attente_paiement']);
  1747.                     if (!$statutAttentePaiement) {
  1748.                         $this->logger->warning('⚠ Statut "attente_paiement" non trouvĂ©');
  1749.                     } else {
  1750.                         $sejour->setStatut($statutAttentePaiement);
  1751.                         $em->flush();
  1752.                         $this->logger->info('✅ Statut dĂ©fini: ' $statutAttentePaiement->getLibiller());
  1753.                     }
  1754.                     $this->logger->info('✅ SĂ©jour créé: Code=' $sejour->getCodeSejour() . ' ID=' $sejour->getId());
  1755.                 } catch (\Exception $sejourError) {
  1756.                     $this->logger->error('❌ Erreur crĂ©ation sĂ©jour', [
  1757.                         'error' => $sejourError->getMessage(),
  1758.                         'trace' => $sejourError->getTraceAsString()
  1759.                     ]);
  1760.                     throw $sejourError;
  1761.                 }
  1762.             }
  1763.             // Envoyer email Ă  l'Ă©quipe avec les informations complĂštes
  1764.             $emailContext = [
  1765.                 'fullname' => $fullname,
  1766.                 'user_email' => $emailUser,
  1767.                 'phone' => $phone,
  1768.                 'org' => $org,
  1769.                 'message' => $message,
  1770.                 'pack_id' => $packId,
  1771.                 'participants' => $participants,
  1772.                 'total_amount' => $totalAmount,
  1773.                 'start_date' => $startDate,
  1774.                 'end_date' => $endDate,
  1775.                 'theme' => $theme,
  1776.                 'accompagnateur' => $accompagnateur,
  1777.                 'request_number' => $requestNumber,
  1778.             ];
  1779.             // Ajouter les infos spĂ©cifiques selon le type
  1780.             if ($isPack && $commande) {
  1781.                 $emailContext['commande_id'] = $commande->getId();
  1782.                 $emailContext['commande_numero'] = $commande->getNumComande();
  1783.                 $emailContext['montant_ht'] = $commande->getMontantht();
  1784.                 $emailContext['montant_ttc'] = $commande->getMontantrth();
  1785.                 $emailContext['is_pack'] = true;
  1786.             } else if ($sejour) {
  1787.                 $emailContext['code_sejour'] = $sejour->getCodeSejour();
  1788.                 $emailContext['sejour_id'] = $sejour->getId();
  1789.                 $emailContext['is_pack'] = false;
  1790.             }
  1791.             $subjectPrefix $isPack '📩 Demande RIB Pack Annuel' '🏩 Demande RIB Paiement SĂ©jour Simple';
  1792.             // Ajouter le numĂ©ro de commande au sujet si c'est un pack
  1793.             $subjectSuffix $requestNumber;
  1794.             if ($isPack && $commande) {
  1795.                 $subjectSuffix .= ' - Commande #' $commande->getNumComande();
  1796.             }
  1797.             $teamEmail = (new TemplatedEmail())
  1798.                 ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  1799.                 ->to('partenariat@5sur5sejour.com')
  1800.                 ->cc('ramzi.benlarbi@gmail.com''yousra.tlich@gmail.com')
  1801.                 ->subject($subjectPrefix ' - ' $org ' - ' $subjectSuffix)
  1802.                 ->htmlTemplate('emails/bank_transfer_request_team.html.twig')
  1803.                 ->context($emailContext);
  1804.             $this->mailer->send($teamEmail);
  1805.             // Email de confirmation au client avec RIB
  1806.             $ribDetails = [
  1807.                 'iban' => 'FR76 1010 7001 5300 0123 4567 890',
  1808.                 'bic' => 'BREDFRPPXXX',
  1809.                 'beneficiaire' => '5SUR5 SEJOUR',
  1810.                 'reference' => $requestNumber
  1811.             ];
  1812.             // PrĂ©parer le contexte email client
  1813.             $clientEmailContext = [
  1814.                 'fullname' => $fullname,
  1815.                 'request_number' => $requestNumber,
  1816.                 'rib' => $ribDetails,
  1817.                 'pack_id' => $packId,
  1818.                 'participants' => $participants,
  1819.                 'total_amount' => $totalAmount,
  1820.                 'start_date' => $startDate,
  1821.                 'end_date' => $endDate,
  1822.                 'theme' => $theme,
  1823.                 'accompagnateur' => $accompagnateur,
  1824.                 'org' => $org,
  1825.             ];
  1826.             // Ajouter les infos spĂ©cifiques
  1827.             if ($isPack && $commande) {
  1828.                 $clientEmailContext['commande_id'] = $commande->getId();
  1829.                 $clientEmailContext['commande_numero'] = $commande->getNumComande();
  1830.                 $clientEmailContext['montant_ht'] = $commande->getMontantht();
  1831.                 $clientEmailContext['montant_ttc'] = $commande->getMontantrth();
  1832.                 $clientEmailContext['is_pack'] = true;
  1833.             } else if ($sejour) {
  1834.                 $clientEmailContext['code_sejour'] = $sejour->getCodeSejour();
  1835.                 $clientEmailContext['sejour_id'] = $sejour->getId();
  1836.                 $clientEmailContext['is_pack'] = false;
  1837.             }
  1838.             $clientEmail = (new TemplatedEmail())
  1839.                 ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  1840.                 ->to($emailUser)
  1841.                 ->subject('RIB et Facture Proforma - ' $requestNumber ' - 5sur5 SĂ©jour')
  1842.                 ->htmlTemplate('emails/bank_transfer_request_client.html.twig')
  1843.                 ->context($clientEmailContext);
  1844.             $this->mailer->send($clientEmail);
  1845.             return new JsonResponse([
  1846.                 'success' => true,
  1847.                 'message' => 'Votre demande a Ă©tĂ© envoyĂ©e avec succĂšs ! Vous allez recevoir le RIB et la facture proforma par email.',
  1848.                 'requestNumber' => $requestNumber
  1849.             ]);
  1850.         } catch (\Exception $e) {
  1851.             $this->logger->error('Erreur lors de l\'envoi de la demande de RIB', [
  1852.                 'error' => $e->getMessage(),
  1853.                 'trace' => $e->getTraceAsString()
  1854.             ]);
  1855.             return new JsonResponse([
  1856.                 'success' => false,
  1857.                 'message' => 'Erreur: ' $e->getMessage(),
  1858.                 'error' => $e->getMessage(),
  1859.                 'trace' => $e->getTraceAsString()
  1860.             ], 500);
  1861.         }
  1862.     }
  1863.     /**
  1864.      * @Route("/pack-request-submit", name="pack_request_submit", methods={"POST"})
  1865.      */
  1866.     public function packRequestSubmit(Request $request): JsonResponse
  1867.     {
  1868.         try {
  1869.             $fullname $request->request->get('pack_fullname');
  1870.             $email $request->request->get('pack_email');
  1871.             $phone $request->request->get('pack_phone');
  1872.             $org $request->request->get('pack_org');
  1873.             $packType $request->request->get('pack_type');
  1874.             $message $request->request->get('pack_message');
  1875.             // Validation basique
  1876.             if (empty($fullname) || empty($email) || empty($packType)) {
  1877.                 return new JsonResponse([
  1878.                     'success' => false,
  1879.                     'message' => 'Le nom, l\'email et le type de pack sont obligatoires.'
  1880.                 ], 400);
  1881.             }
  1882.             // Envoyer email Ă  l'Ă©quipe
  1883.             $email = (new TemplatedEmail())
  1884.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  1885.                 ->to('contact@5sur5sejour.fr')
  1886.                 ->subject('Demande de Pack Partenaire - ' $fullname)
  1887.                 ->htmlTemplate('emails/pack_request_team.html.twig')
  1888.                 ->context([
  1889.                     'fullname' => $fullname,
  1890.                     'email' => $email,
  1891.                     'phone' => $phone,
  1892.                     'org' => $org,
  1893.                     'packType' => $packType,
  1894.                     'message' => $message
  1895.                 ]);
  1896.             $this->mailer->send($email);
  1897.             // Email de confirmation au client
  1898.             $clientEmail = (new TemplatedEmail())
  1899.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  1900.                 ->to($email)
  1901.                 ->subject('Demande de Pack Partenaire reçue - 5sur5 SĂ©jour')
  1902.                 ->htmlTemplate('emails/pack_request_partner.html.twig')
  1903.                 ->context([
  1904.                     'fullname' => $fullname,
  1905.                     'packType' => $packType
  1906.                 ]);
  1907.             $this->mailer->send($clientEmail);
  1908.             return new JsonResponse([
  1909.                 'success' => true,
  1910.                 'message' => 'Votre demande de pack partenaire a Ă©tĂ© envoyĂ©e avec succĂšs ! Nous vous recontacterons dans les 24 heures.'
  1911.             ]);
  1912.         } catch (\Exception $e) {
  1913.             $this->logger->error('Erreur lors de l\'envoi de la demande de pack', [
  1914.                 'error' => $e->getMessage(),
  1915.                 'trace' => $e->getTraceAsString()
  1916.             ]);
  1917.             return new JsonResponse([
  1918.                 'success' => false,
  1919.                 'message' => 'Une erreur est survenue lors de l\'envoi de la demande. Veuillez rĂ©essayer plus tard.'
  1920.             ], 500);
  1921.         }
  1922.     }
  1923.     /**
  1924.      * @Route("/checkout-create-session", name="checkout_create_session", methods={"POST"})
  1925.      */
  1926.     public function checkoutCreateSession(Request $request): JsonResponse
  1927.     {
  1928.         try {
  1929.             $company $request->request->get('company');
  1930.             $contactName $request->request->get('contact_name');
  1931.             $email $request->request->get('email');
  1932.             $phone $request->request->get('phone');
  1933.             $packType $request->request->get('pack_type');
  1934.             $address $request->request->get('address');
  1935.             $city $request->request->get('city');
  1936.             $postalCode $request->request->get('postal_code');
  1937.             $siret $request->request->get('siret');
  1938.             // Validation basique
  1939.             if (empty($company) || empty($contactName) || empty($email) || empty($packType)) {
  1940.                 return new JsonResponse([
  1941.                     'success' => false,
  1942.                     'message' => 'Les champs obligatoires doivent ĂȘtre remplis.'
  1943.                 ], 400);
  1944.             }
  1945.             // Envoyer email Ă  l'Ă©quipe
  1946.             $email = (new TemplatedEmail())
  1947.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  1948.                 ->to('contact@5sur5sejour.fr')
  1949.                 ->subject('Nouvelle souscription en ligne - ' $company)
  1950.                 ->htmlTemplate('emails/checkout_request_team.html.twig')
  1951.                 ->context([
  1952.                     'company' => $company,
  1953.                     'contactName' => $contactName,
  1954.                     'email' => $email,
  1955.                     'phone' => $phone,
  1956.                     'packType' => $packType,
  1957.                     'address' => $address,
  1958.                     'city' => $city,
  1959.                     'postalCode' => $postalCode,
  1960.                     'siret' => $siret
  1961.                 ]);
  1962.             $this->mailer->send($email);
  1963.             // Email de confirmation au client
  1964.             $clientEmail = (new TemplatedEmail())
  1965.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  1966.                 ->to($email)
  1967.                 ->subject('Souscription reçue - 5sur5 SĂ©jour')
  1968.                 ->htmlTemplate('emails/checkout_request_client.html.twig')
  1969.                 ->context([
  1970.                     'company' => $company,
  1971.                     'contactName' => $contactName,
  1972.                     'packType' => $packType
  1973.                 ]);
  1974.             $this->mailer->send($clientEmail);
  1975.             return new JsonResponse([
  1976.                 'success' => true,
  1977.                 'message' => 'Votre souscription a Ă©tĂ© enregistrĂ©e avec succĂšs ! Nous vous recontacterons dans les 24 heures pour finaliser l\'activation.'
  1978.             ]);
  1979.         } catch (\Exception $e) {
  1980.             $this->logger->error('Erreur lors de l\'enregistrement de la souscription', [
  1981.                 'error' => $e->getMessage(),
  1982.                 'trace' => $e->getTraceAsString()
  1983.             ]);
  1984.             return new JsonResponse([
  1985.                 'success' => false,
  1986.                 'message' => 'Une erreur est survenue lors de l\'enregistrement. Veuillez rĂ©essayer plus tard.'
  1987.             ], 500);
  1988.         }
  1989.     }
  1990.     /**
  1991.      * @Route("/quote-request", name="quote_request", methods={"POST"})
  1992.      */
  1993.     public function quoteRequest(Request $request): JsonResponse
  1994.     {
  1995.         try {
  1996.             $orgType $request->request->get('org_type');
  1997.             $orgName $request->request->get('org_name');
  1998.             $contactName $request->request->get('contact_name');
  1999.             $email $request->request->get('email');
  2000.             $phone $request->request->get('phone');
  2001.             $packType $request->request->get('pack_type');
  2002.             $address $request->request->get('address');
  2003.             $city $request->request->get('city');
  2004.             $postalCode $request->request->get('postal_code');
  2005.             $message $request->request->get('message');
  2006.             // Validation basique
  2007.             if (empty($orgType) || empty($orgName) || empty($contactName) || empty($email) || empty($packType)) {
  2008.                 return new JsonResponse([
  2009.                     'success' => false,
  2010.                     'message' => 'Les champs obligatoires doivent ĂȘtre remplis.'
  2011.                 ], 400);
  2012.             }
  2013.             // Envoyer email Ă  l'Ă©quipe
  2014.             $email = (new TemplatedEmail())
  2015.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  2016.                 ->to('contact@5sur5sejour.fr')
  2017.                 ->subject('Demande de devis - ' $orgName)
  2018.                 ->htmlTemplate('emails/quote_request_team.html.twig')
  2019.                 ->context([
  2020.                     'orgType' => $orgType,
  2021.                     'orgName' => $orgName,
  2022.                     'contactName' => $contactName,
  2023.                     'email' => $email,
  2024.                     'phone' => $phone,
  2025.                     'packType' => $packType,
  2026.                     'address' => $address,
  2027.                     'city' => $city,
  2028.                     'postalCode' => $postalCode,
  2029.                     'message' => $message
  2030.                 ]);
  2031.             $this->mailer->send($email);
  2032.             // Email de confirmation au client
  2033.             $clientEmail = (new TemplatedEmail())
  2034.                 ->from(new Address('noreply@5sur5sejour.fr''5sur5 SĂ©jour'))
  2035.                 ->to($email)
  2036.                 ->subject('Demande de devis reçue - 5sur5 SĂ©jour')
  2037.                 ->htmlTemplate('emails/quote_request_client.html.twig')
  2038.                 ->context([
  2039.                     'orgName' => $orgName,
  2040.                     'contactName' => $contactName,
  2041.                     'packType' => $packType
  2042.                 ]);
  2043.             $this->mailer->send($clientEmail);
  2044.             return new JsonResponse([
  2045.                 'success' => true,
  2046.                 'message' => 'Votre demande de devis a Ă©tĂ© envoyĂ©e avec succĂšs ! Nous vous recontacterons dans les 24 heures.'
  2047.             ]);
  2048.         } catch (\Exception $e) {
  2049.             $this->logger->error('Erreur lors de l\'envoi de la demande de devis', [
  2050.                 'error' => $e->getMessage(),
  2051.                 'trace' => $e->getTraceAsString()
  2052.             ]);
  2053.             return new JsonResponse([
  2054.                 'success' => false,
  2055.                 'message' => 'Une erreur est survenue lors de l\'envoi de la demande. Veuillez rĂ©essayer plus tard.'
  2056.             ], 500);
  2057.         }
  2058.     }
  2059.     /**
  2060.      * @Route("/creation-simple/send-rib-codes", name="app_creation_simple_send_rib_codes", methods={"POST","GET"})
  2061.      */
  2062.     public function sendRibCodes(Request $requestEntityManagerInterface $em): JsonResponse
  2063.     {
  2064.         if ($request->isMethod('GET')) {
  2065.             return new JsonResponse([
  2066.                 'success' => false,
  2067.                 'message' => 'Cette URL est rĂ©servĂ©e aux requĂȘtes sĂ©curisĂ©es. Merci d\'utiliser le formulaire de crĂ©ation de sĂ©jour.'
  2068.             ], 405);
  2069.         }
  2070.         try {
  2071.             $data json_decode($request->getContent(), true);
  2072.             $sejourId $data['sejour_id'] ?? null;
  2073.             $billingEmail $data['billing_email'] ?? null;
  2074.             $billingContact $data['billing_contact'] ?? null;
  2075.             // Note: Anti-spam dĂ©sactivĂ© ici car la validation reCAPTCHA est faite sur la requĂȘte initiale
  2076.             error_log("📧 sendRibCodes appelĂ© pour sĂ©jour #{$sejourId}, email: {$billingEmail}");
  2077.             if (!$sejourId || !$billingEmail) {
  2078.                 return new JsonResponse([
  2079.                     'success' => false,
  2080.                     'message' => 'DonnĂ©es manquantes'
  2081.                 ], 400);
  2082.             }
  2083.             $sejour $em->getRepository(\App\Entity\Sejour::class)->find($sejourId);
  2084.             if (!$sejour) {
  2085.                 return new JsonResponse([
  2086.                     'success' => false,
  2087.                     'message' => 'SĂ©jour non trouvĂ©'
  2088.                 ], 404);
  2089.             }
  2090.             $commande $em->getRepository(Commande::class)->findOneBy(['idSejour' => $sejour]);
  2091.             if (!$commande) {
  2092.                 return new JsonResponse([
  2093.                     'success' => false,
  2094.                     'message' => 'Commande non trouvĂ©e'
  2095.                 ], 404);
  2096.             }
  2097.             $user $sejour->getIdPartenaire();
  2098.             if (!$user) {
  2099.                 return new JsonResponse([
  2100.                     'success' => false,
  2101.                     'message' => 'Utilisateur non trouvĂ©'
  2102.                 ], 404);
  2103.             }
  2104.             // Envoyer email avec RIB + codes sĂ©jour
  2105.             $email = (new TemplatedEmail())
  2106.                 ->from(new Address('info@5sur5sejour.com''5sur5 SĂ©jour'))
  2107.                 ->to($billingEmail)
  2108.                 ->subject('RIB et codes sĂ©jour - ' $sejour->getCodeSejour())
  2109.                 ->htmlTemplate('emails/sejour_simple/rib_codes.html.twig')
  2110.                 ->context([
  2111.                     'sejour' => $sejour,
  2112.                     'commande' => $commande,
  2113.                     'user' => $user,
  2114.                     'billingContact' => $billingContact
  2115.                 ]);
  2116.             $this->mailer->send($email);
  2117.             // Envoyer aussi les codes Ă  l'accompagnateur
  2118.             $accompagnateur $sejour->getIdAcommp();
  2119.             if ($accompagnateur && $accompagnateur->getReponseemail()) {
  2120.                 $this->UserService->EnvoyerEmailAcommpatActivationNewMail($sejour$accompagnateur);
  2121.             }
  2122.             return new JsonResponse([
  2123.                 'success' => true,
  2124.                 'message' => 'RIB et codes sĂ©jour envoyĂ©s avec succĂšs'
  2125.             ]);
  2126.         } catch (\Exception $e) {
  2127.             $this->logger->error('Erreur lors de l\'envoi RIB + codes', [
  2128.                 'error' => $e->getMessage(),
  2129.                 'trace' => $e->getTraceAsString()
  2130.             ]);
  2131.             return new JsonResponse([
  2132.                 'success' => false,
  2133.                 'message' => 'Une erreur est survenue lors de l\'envoi'
  2134.             ], 500);
  2135.         }
  2136.     }
  2137.     /**
  2138.      * @Route("/creation-simple/checkout", name="app_creation_simple_checkout", methods={"GET"})
  2139.      */
  2140.     public function checkoutSejourSimple(Request $requestEntityManagerInterface $em): Response
  2141.     {
  2142.         $sejourId $request->query->get('sejour_id');
  2143.         if (!$sejourId) {
  2144.             return $this->redirectToRoute('app_creation_simple_creer');
  2145.         }
  2146.         $sejour $em->getRepository(\App\Entity\Sejour::class)->find($sejourId);
  2147.         if (!$sejour) {
  2148.             return $this->redirectToRoute('app_creation_simple_creer');
  2149.         }
  2150.         $commande $em->getRepository(Commande::class)->findOneBy(['idSejour' => $sejour]);
  2151.         if (!$commande) {
  2152.             return $this->redirectToRoute('app_creation_simple_creer');
  2153.         }
  2154.         // Stocker la commande en session pour Payplug
  2155.         $session $request->getSession();
  2156.         $session->set('commandeActual'$commande->getId());
  2157.         // Rediriger vers Payplug (route dĂ©diĂ©e aux sĂ©jours simples, sans authentification requise)
  2158.         return $this->redirectToRoute('sejour_simple_payplug_chargement_form');
  2159.     }
  2160. }