<?php
namespace App\Controller;
use App\Entity\Member;
use App\Entity\EmailChangeRequest;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Stripe\Stripe;
use Stripe\Checkout\Session;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
class AdhesionController extends AbstractController
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* @Route("/adhesion", name="app_adhesion")
*/
public function adhesion(): Response
{
return $this->render('adhesion/adhesion.html.twig');
}
/**
* @Route("/checkout/adhesion", name="app_checkout_adhesion", methods={"POST"})
*/
public function checkout(Request $request): Response
{
$plan = $request->request->get('plan');
$price = $request->request->get('price');
$firstname = $request->request->get('firstname');
$lastname = $request->request->get('lastname');
$email = $request->request->get('email');
$phone = $request->request->get('phone');
// Check if member already exists
$member = $this->em->getRepository(Member::class)->findOneBy(['email' => $email]);
if (!$member) {
$member = new Member();
$member->setEmail($email);
}
$member->setName($firstname . ' ' . $lastname);
$member->setPhone($phone);
$member->setMembershipType(ucfirst($plan));
$member->setJoinDate(new \DateTime());
$member->setIsActive(false); // Will be activated after successful payment
$this->em->persist($member);
$this->em->flush();
Stripe::setApiKey($_ENV['STRIPE_SECRET_KEY']);
$session = Session::create([
'payment_method_types' => ['card', 'sepa_debit'],
'line_items' => [
[
'price_data' => [
'currency' => 'eur',
'unit_amount' => $price * 100, // Amount in cents
'product_data' => [
'name' => 'Adhésion WA BENIN - Pack ' . ucfirst($plan),
'description' => 'Abonnement annuel',
],
// We can configure recurring if we want subscriptions:
// 'recurring' => ['interval' => 'year'],
],
'quantity' => 1,
]
],
'mode' => 'payment', // Or 'subscription' if using recurring
'success_url' => $this->generateUrl('app_adhesion_success', ['id' => $member->getId()], UrlGeneratorInterface::ABSOLUTE_URL),
'cancel_url' => $this->generateUrl('app_adhesion_cancel', [], UrlGeneratorInterface::ABSOLUTE_URL),
'metadata' => [
'type' => 'adhesion',
'member_id' => $member->getId()
]
]);
return $this->redirect($session->url, 303);
}
/**
* @Route("/adhesion/success/{id}", name="app_adhesion_success")
*/
public function success(int $id, MailerInterface $mailer): Response
{
$member = $this->em->getRepository(Member::class)->find($id);
if (!$member) {
throw $this->createNotFoundException('Membre non trouvé.');
}
// Activate member
$member->setIsActive(true);
// Generate OTP if not already generated
if (!$member->getOtp()) {
$otp = strtoupper(substr(bin2hex(random_bytes(4)), 0, 8)); // 8 alphanumeric chars
$member->setOtp($otp);
$this->em->flush();
// Send Email
try {
$email = (new TemplatedEmail())
->from(new Address('wabenin@azilink.com', 'WA BENIN'))
->to($member->getEmail())
->subject('Bienvenue ! Vos accès à l\'Espace Membre WA BENIN')
->htmlTemplate('emails/adhesion_otp.html.twig')
->context([
'member' => $member,
'otp' => $otp
]);
$mailer->send($email);
} catch (\Exception $e) {
// Ignore mailer errors on local testing
}
} else {
$otp = $member->getOtp();
}
return $this->render('adhesion/adhesion_success.html.twig', [
'member' => $member,
'otp' => $otp
]);
}
/**
* @Route("/adhesion/cancel", name="app_adhesion_cancel")
*/
public function cancel(): Response
{
return $this->render('adhesion/adhesion_cancel.html.twig');
}
/**
* @Route("/adhesion/resend-otp/{id}", name="app_adhesion_resend", methods={"POST"})
*/
public function resendOtp(int $id, MailerInterface $mailer): Response
{
$member = $this->em->getRepository(Member::class)->find($id);
if (!$member) throw $this->createNotFoundException('Membre non trouvé.');
$now = new \DateTime();
$lastSent = $member->getOtpLastSentAt();
// Prevent more than 2 resends in 24h
if ($lastSent) {
$diff = $now->diff($lastSent);
if ($diff->days == 0 && $member->getOtpSentCount() >= 2) {
// Flash message would be better, but returning to success page
return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'error' => 'limit_reached']);
}
} else {
$member->setOtpSentCount(0);
}
// Generate new OTP
$otp = strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
$member->setOtp($otp);
$member->setOtpSentCount($member->getOtpSentCount() + 1);
$member->setOtpLastSentAt($now);
$this->em->flush();
try {
$email = (new TemplatedEmail())
->from(new Address('wabenin@azilink.com', 'WA BENIN'))
->to($member->getEmail())
->subject('Votre nouveau code d\'accès WA BENIN')
->htmlTemplate('emails/adhesion_otp.html.twig')
->context([
'member' => $member,
'otp' => $otp
]);
$mailer->send($email);
} catch (\Exception $e) {}
return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'success' => 'resent']);
}
/**
* @Route("/adhesion/claim/{id}", name="app_adhesion_claim", methods={"POST"})
*/
public function emailClaim(int $id, Request $request): Response
{
$member = $this->em->getRepository(Member::class)->find($id);
if (!$member) throw $this->createNotFoundException('Membre non trouvé.');
$claim = new EmailChangeRequest();
$claim->setMember($member);
$claim->setName($request->request->get('name'));
$claim->setOldEmail($request->request->get('old_email'));
$claim->setNewEmail($request->request->get('new_email'));
$claim->setTransactionNumber($request->request->get('transaction_id', ''));
$this->em->persist($claim);
$this->em->flush();
return $this->redirectToRoute('app_adhesion_success', ['id' => $id, 'success' => 'claim_submitted']);
}
}