src/Controller/AdhesionController.php line 31

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Member;
  4. use App\Entity\EmailChangeRequest;
  5. use Doctrine\ORM\EntityManagerInterface;
  6. use Symfony\Component\Mime\Address;
  7. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpFoundation\Response;
  10. use Symfony\Component\Routing\Annotation\Route;
  11. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  12. use Stripe\Stripe;
  13. use Stripe\Checkout\Session;
  14. use Symfony\Component\Mailer\MailerInterface;
  15. use Symfony\Bridge\Twig\Mime\TemplatedEmail;
  16. class AdhesionController extends AbstractController
  17. {
  18. private $em;
  19. public function __construct(EntityManagerInterface $em)
  20. {
  21. $this->em = $em;
  22. }
  23. /**
  24. * @Route("/adhesion", name="app_adhesion")
  25. */
  26. public function adhesion(): Response
  27. {
  28. return $this->render('adhesion/adhesion.html.twig');
  29. }
  30. /**
  31. * @Route("/checkout/adhesion", name="app_checkout_adhesion", methods={"POST"})
  32. */
  33. public function checkout(Request $request): Response
  34. {
  35. $plan = $request->request->get('plan');
  36. $price = $request->request->get('price');
  37. $firstname = $request->request->get('firstname');
  38. $lastname = $request->request->get('lastname');
  39. $email = $request->request->get('email');
  40. $phone = $request->request->get('phone');
  41. // Check if member already exists
  42. $member = $this->em->getRepository(Member::class)->findOneBy(['email' => $email]);
  43. if (!$member) {
  44. $member = new Member();
  45. $member->setEmail($email);
  46. }
  47. $member->setName($firstname . ' ' . $lastname);
  48. $member->setPhone($phone);
  49. $member->setMembershipType(ucfirst($plan));
  50. $member->setJoinDate(new \DateTime());
  51. $member->setIsActive(false); // Will be activated after successful payment
  52. $this->em->persist($member);
  53. $this->em->flush();
  54. Stripe::setApiKey($_ENV['STRIPE_SECRET_KEY']);
  55. $session = Session::create([
  56. 'payment_method_types' => ['card', 'sepa_debit'],
  57. 'line_items' => [
  58. [
  59. 'price_data' => [
  60. 'currency' => 'eur',
  61. 'unit_amount' => $price * 100, // Amount in cents
  62. 'product_data' => [
  63. 'name' => 'Adhésion WA BENIN - Pack ' . ucfirst($plan),
  64. 'description' => 'Abonnement annuel',
  65. ],
  66. // We can configure recurring if we want subscriptions:
  67. // 'recurring' => ['interval' => 'year'],
  68. ],
  69. 'quantity' => 1,
  70. ]
  71. ],
  72. 'mode' => 'payment', // Or 'subscription' if using recurring
  73. 'success_url' => $this->generateUrl('app_adhesion_success', ['id' => $member->getId()], UrlGeneratorInterface::ABSOLUTE_URL),
  74. 'cancel_url' => $this->generateUrl('app_adhesion_cancel', [], UrlGeneratorInterface::ABSOLUTE_URL),
  75. 'metadata' => [
  76. 'type' => 'adhesion',
  77. 'member_id' => $member->getId()
  78. ]
  79. ]);
  80. return $this->redirect($session->url, 303);
  81. }
  82. /**
  83. * @Route("/adhesion/success/{id}", name="app_adhesion_success")
  84. */
  85. public function success(int $id, MailerInterface $mailer): Response
  86. {
  87. $member = $this->em->getRepository(Member::class)->find($id);
  88. if (!$member) {
  89. throw $this->createNotFoundException('Membre non trouvé.');
  90. }
  91. // Activate member
  92. $member->setIsActive(true);
  93. // Generate OTP if not already generated
  94. if (!$member->getOtp()) {
  95. $otp = strtoupper(substr(bin2hex(random_bytes(4)), 0, 8)); // 8 alphanumeric chars
  96. $member->setOtp($otp);
  97. $this->em->flush();
  98. // Send Email
  99. try {
  100. $email = (new TemplatedEmail())
  101. ->from(new Address('wabenin@azilink.com', 'WA BENIN'))
  102. ->to($member->getEmail())
  103. ->subject('Bienvenue ! Vos accès à l\'Espace Membre WA BENIN')
  104. ->htmlTemplate('emails/adhesion_otp.html.twig')
  105. ->context([
  106. 'member' => $member,
  107. 'otp' => $otp
  108. ]);
  109. $mailer->send($email);
  110. } catch (\Exception $e) {
  111. // Ignore mailer errors on local testing
  112. }
  113. } else {
  114. $otp = $member->getOtp();
  115. }
  116. return $this->render('adhesion/adhesion_success.html.twig', [
  117. 'member' => $member,
  118. 'otp' => $otp
  119. ]);
  120. }
  121. /**
  122. * @Route("/adhesion/cancel", name="app_adhesion_cancel")
  123. */
  124. public function cancel(): Response
  125. {
  126. return $this->render('adhesion/adhesion_cancel.html.twig');
  127. }
  128. /**
  129. * @Route("/adhesion/resend-otp/{id}", name="app_adhesion_resend", methods={"POST"})
  130. */
  131. public function resendOtp(int $id, MailerInterface $mailer): Response
  132. {
  133. $member = $this->em->getRepository(Member::class)->find($id);
  134. if (!$member) throw $this->createNotFoundException('Membre non trouvé.');
  135. $now = new \DateTime();
  136. $lastSent = $member->getOtpLastSentAt();
  137. // Prevent more than 2 resends in 24h
  138. if ($lastSent) {
  139. $diff = $now->diff($lastSent);
  140. if ($diff->days == 0 && $member->getOtpSentCount() >= 2) {
  141. // Flash message would be better, but returning to success page
  142. return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'error' => 'limit_reached']);
  143. }
  144. } else {
  145. $member->setOtpSentCount(0);
  146. }
  147. // Generate new OTP
  148. $otp = strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
  149. $member->setOtp($otp);
  150. $member->setOtpSentCount($member->getOtpSentCount() + 1);
  151. $member->setOtpLastSentAt($now);
  152. $this->em->flush();
  153. try {
  154. $email = (new TemplatedEmail())
  155. ->from(new Address('wabenin@azilink.com', 'WA BENIN'))
  156. ->to($member->getEmail())
  157. ->subject('Votre nouveau code d\'accès WA BENIN')
  158. ->htmlTemplate('emails/adhesion_otp.html.twig')
  159. ->context([
  160. 'member' => $member,
  161. 'otp' => $otp
  162. ]);
  163. $mailer->send($email);
  164. } catch (\Exception $e) {}
  165. return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'success' => 'resent']);
  166. }
  167. /**
  168. * @Route("/adhesion/claim/{id}", name="app_adhesion_claim", methods={"POST"})
  169. */
  170. public function emailClaim(int $id, Request $request): Response
  171. {
  172. $member = $this->em->getRepository(Member::class)->find($id);
  173. if (!$member) throw $this->createNotFoundException('Membre non trouvé.');
  174. $claim = new EmailChangeRequest();
  175. $claim->setMember($member);
  176. $claim->setName($request->request->get('name'));
  177. $claim->setOldEmail($request->request->get('old_email'));
  178. $claim->setNewEmail($request->request->get('new_email'));
  179. $claim->setTransactionNumber($request->request->get('transaction_id', ''));
  180. $this->em->persist($claim);
  181. $this->em->flush();
  182. return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'success' => 'claim_submitted']);
  183. }
  184. }