<?php
namespace App\Controller;
use App\Config;
use App\Entity\Candidature;
use App\Entity\CompteRecrutement;
use App\Entity\Enum\FrenchRegion;
use App\Entity\Offer;
use App\Entity\OfferVisit;
use App\Entity\Profile;
use App\Entity\ProfileVisit;
use App\Entity\Report;
use App\Entity\Society;
use App\Entity\TempMail;
use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Form\ReportFormType;
use App\Form\ResetPasswordRequestFormType;
use App\Repository\CandidatureRepository;
use App\Repository\ExternalCompanyRepository;
use App\Repository\OfferFavoriteRepository;
use App\Repository\OfferRepository;
use App\Repository\ProfileRepository;
use App\Repository\ProfileVisitRepository;
use App\Repository\PublicationRepository;
use App\Repository\NewsRepository;
use App\Repository\SocietyRepository;
use App\Repository\UserRepository;
use App\Service\TempMailService;
use App\UseCase\Commande\CreateCommande;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Knp\Component\Pager\PaginatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Stripe\Stripe;
use Stripe\Subscription;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\BlogRepository;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class WebController extends AbstractController
{
/**
* @Route("/", name="homepage")
*/
public function index(BlogRepository $blogRepository, OfferRepository $offerRepository, ProfileRepository $profileRepository, NewsRepository $newsRepository): Response
{
$user = $this->getUser();
if (
$user && !in_array('ROLE_SOCIETY', $user->getRoles()) &&
!in_array('ROLE_ADMIN', $user->getRoles())
) {
// Si le user n'a pas de profils, ou s'il a des profils mais aucun n'est terminé
$profile = $user->getUniqueProfile();
if ($profile->getId() == null) {
return $this->redirectToRoute('complete_resume_profile');
}
if ($profile->getFinished() === 0 || $profile->getFinished() === false) {
return $this->redirectToRoute('complete_first_edit');
}
}
$offers = $offerRepository->getLatest();
$profiles = $profileRepository->getLatest(3);
$news = $newsRepository->getLatest();
$blogs = $blogRepository->findBy([], ['created_at' => 'DESC'], 3);
$form = $this->createForm(RegistrationFormType::class, new User());
$resetForm = $this->createForm(ResetPasswordRequestFormType::class);
return $this->render('page/home.html.twig', [
'offers' => $offers,
'profiles' => $profiles,
'regions' => FrenchRegion::REGION_DEPARTEMENT,
'registrationForm' => $form->createView(),
'resetForm' => $resetForm->createView(),
'faqs' => Config::FAQ,
'cityList' => Config::FEATURED_CITY,
'news' => $news,
'isComing' => (bool)$this->getParameter('is_coming'),
'isNotShow' => (bool)$this->getParameter('is_not_show'),
'blogs' => $blogs
]);
}
/**
* @Route("/societe/{slug}", name="society_profil", methods={"GET"})
*/
public function society(
string $slug,
Request $request,
OfferRepository $offerRepository,
PaginatorInterface $paginator,
ExternalCompanyRepository $externalCompanyRepository,
SocietyRepository $societyRepository
): Response {
/* @var User $user */
$user = $this->getUser();
// D'abord chercher dans la table Society locale
$society = $societyRepository->findOneBy(['slug' => $slug]);
$externalCompanyProfile = null;
$isExternalSociety = false;
if ($society) {
// Société locale trouvée
$offers = $offerRepository->findBy(['society' => $society], ['id' => 'desc']);
// Récupérer les informations externes si la société a un SIREN
if ($society->getSiren()) {
$externalCompanyProfile = $externalCompanyRepository->findCompanyWithProfileBySiren($society->getSiren());
}
$isOwner = $user && $user->isSociety() && ($society->getId() === $user->getSociety()->getId());
$reportedId = $society->getId();
} else {
// Société locale non trouvée, chercher dans la base externe par slug
$externalCompanyProfile = $externalCompanyRepository->findCompanyWithProfileBySlug($slug);
if (!$externalCompanyProfile) {
// Aucune société trouvée, retourner 404
throw $this->createNotFoundException('Société non trouvée');
}
// Créer une société virtuelle pour l'affichage
$society = new Society();
$society->setName($externalCompanyProfile['name'] ?? 'Centre de formation');
$society->setSiren($externalCompanyProfile['profile']['siren'] ?? $slug);
$society->setType('Centre de formation');
// Récupérer les offres externes (society = null et siren correspond)
$offers = $offerRepository->createQueryBuilder('o')
->where('o.society IS NULL')
->andWhere('o.siren = :siren')
->setParameter('siren', $externalCompanyProfile['profile']['siren'] ?? $slug)
->orderBy('o.id', 'DESC')
->getQuery()
->getResult();
$isExternalSociety = true;
$isOwner = false;
$reportedId = null;
}
$viewData = [
'society' => $society,
'offers' => $paginator->paginate($offers, $request->query->getInt('page', 1), 10),
'isOwner' => $isOwner,
'reportedId' => $reportedId,
'reportedTarget' => 'society',
'externalCompanyProfile' => $externalCompanyProfile,
'isExternalSociety' => $isExternalSociety,
];
$viewData['reportForm'] = $this->createForm(ReportFormType::class, new Report())->createView();
if (!$this->getUser()) {
$viewData['registrationForm'] = $this->createForm(RegistrationFormType::class, new User())->createView();
}
return $this->render('page/society.html.twig', $viewData);
}
/**
* @Route("/societe/formation/{slug}", name="society_formation_profile", methods={"GET"})
*/
public function societyFormation(
string $slug,
Request $request,
OfferRepository $offerRepository,
PaginatorInterface $paginator,
ExternalCompanyRepository $externalCompanyRepository,
SocietyRepository $societyRepository,
\App\Repository\FormationRepository $formationRepository
): Response {
// Ce endpoint est 100% sous-domaine (externe)
$society = null;
$externalCompanyProfile = null;
$isExternalSociety = true;
$isOwner = false;
$reportedId = null;
// Tenter de trouver le profil entreprise externe par slug ou SIREN
$externalCompanyProfile = $externalCompanyRepository->findCompanyWithProfileBySlug($slug);
if (!$externalCompanyProfile) {
$externalCompanyProfile = $externalCompanyRepository->findCompanyWithProfileBySiren($slug);
}
// Récupérer les formations par user slug externe
$perPage = 6;
$pageFormations = max(1, (int)$request->query->get('pf', 1));
$centreFormations = [];
$centreFormationsTotalPages = 0;
$activeUserIds = $formationRepository->getActiveUserIds();
$externalUserId = $formationRepository->getExternalUserIdBySlug($slug);
if ($externalUserId) {
$total = $formationRepository->countFormationsByUserId($externalUserId, $activeUserIds);
$centreFormations = $formationRepository->getFormationsByUserId(
$externalUserId,
$perPage,
($pageFormations - 1) * $perPage,
$activeUserIds
);
// Normaliser certains champs JSON en tableaux
foreach ($centreFormations as &$cf) {
foreach (['profilsAcceptes', 'lieuCours', 'lieuCoursVilles', 'certifications'] as $jsonField) {
if (isset($cf[$jsonField]) && is_string($cf[$jsonField])) {
$trimmed = trim($cf[$jsonField]);
if ($trimmed !== '' && ($trimmed[0] === '[' || $trimmed[0] === '{')) {
$decoded = json_decode($cf[$jsonField], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$cf[$jsonField] = $decoded;
}
}
}
}
}
unset($cf);
$centreFormationsTotalPages = (int) ceil($total / $perPage);
// Fallback pour header: si pas d'infos entreprise, déduire name/avatar depuis la première formation
if ((!$externalCompanyProfile || empty($externalCompanyProfile['name'])) && !empty($centreFormations)) {
$first = $centreFormations[0];
$derivedName = trim(((string)($first['user_nom'] ?? '')) . ' ' . ((string)($first['user_prenom'] ?? '')));
$derivedAvatar = null;
if (!empty($first['user_photo'])) {
$base = $_ENV['FORMATION_URL'] ?? '';
$derivedAvatar = rtrim($base, '/') . '/uploads/avatars/' . $first['user_photo'];
}
$externalCompanyProfile = [
'name' => $derivedName ?: 'Centre de formation',
'avatar' => $derivedAvatar,
'profile' => [
'siren' => $slug
]
];
}
}
$viewData = [
'society' => $society,
'externalCompanyProfile' => $externalCompanyProfile,
'isExternalSociety' => $isExternalSociety,
'isOwner' => $isOwner,
'reportedId' => $reportedId,
'reportedTarget' => 'society',
'centreFormations' => $centreFormations,
'centreFormationsCurrentPage' => $pageFormations,
'centreFormationsTotalPages' => $centreFormationsTotalPages,
];
$viewData['reportForm'] = $this->createForm(ReportFormType::class, new Report())->createView();
if (!$this->getUser()) {
$viewData['registrationForm'] = $this->createForm(RegistrationFormType::class, new User())->createView();
}
return $this->render('page/society_formation.html.twig', $viewData);
}
/**
* @Route("/job/{slug}", name="public_mission", methods={"GET","POST"})
*
* @param Offer $offer
* @param OfferRepository $offerRepository
* @param Request $request
* @param EntityManagerInterface $entityManager
* @param CandidatureRepository $candidatureRepository
* @param ProfileRepository $profileRepository
* @param OfferFavoriteRepository $offerFavoriteRepository
* @param MailerInterface $mailer
* @return Response
* @throws TransportExceptionInterface
*/
public function offer(
Offer $offer,
OfferRepository $offerRepository,
Request $request,
EntityManagerInterface $entityManager,
CandidatureRepository $candidatureRepository,
ProfileRepository $profileRepository,
OfferFavoriteRepository $offerFavoriteRepository,
MailerInterface $mailer,
TempMailService $tempMailService,
SessionInterface $session,
\App\Repository\ExternalCompanyRepository $externalCompanyRepository,
\App\Repository\FormationRepository $formationRepository,
\App\Service\LinkCheckerService $linkCheckerService
): Response {
// Vérifier si l'URL précédente est déjà présente dans la session
if (!$session->has('previous_url')) {
// Récupérer l'URL précédente (Referer)
$referer = $request->headers->get('referer');
// Sauvegarder l'URL dans la session si elle n'existe pas déjà
if ($referer) {
$session->set('previous_url', $referer);
}
}
$user = $this->getUser();
if ($user && !in_array('ROLE_ADMIN', $user->getRoles()) && $offer->getUser() != $user && ($offer->getInvalid() == 1 || $offer->getInvalid() == 2)) {
return $this->redirectToRoute('homepage');
}
if (!$user && ($offer->getInvalid() == 1 || $offer->getInvalid() == 2)) {
return $this->redirectToRoute('homepage');
}
$currentDate = new DateTime();
// Check if offer is expired or 404 manually before displaying it
if ($offer->getExpireAt() !== null && $offer->getExpireAt() < $currentDate) {
$this->addFlash('package_error', "Cette offre a expiré et n'est plus disponible.");
return $this->redirectToRoute('homepage');
}
if ($offer->getLinkExtern()) {
if ($linkCheckerService->is404($offer->getLinkExtern())) {
// Ne pas modifier la base ici : l'expiration est gérée par la
// commande cron app:offers:check-links pour éviter les faux positifs.
$this->addFlash('package_error', "L'offre n'est plus disponible.");
return $this->redirectToRoute('homepage');
}
}
/* @var User $user */
$user = $this->getUser();
$candidatures = [];
$isOwner = false;
if ($user) {
$currentOwnerID = null;
if ($offer->getSociety()) {
$currentOwnerID = $offer->getSociety()->getId();
}
$isOwner = $user->isSociety() && ($user->getSociety()->getId() == $currentOwnerID);
$offerVisit = new OfferVisit();
if (!$isOwner) {
$offerVisit
->setUser($user)
->setOffer($offer)
->setCreatedAt(new DateTime());
$entityManager->persist($offerVisit);
$entityManager->flush();
}
// REDIRECT IF PROFIL NOT COMPLETE
if ($user->isFreelance()) {
$currentProfil = $user->getUniqueProfile();
if (!$currentProfil->getId()) {
return $this->redirectToRoute('complete_resume_profile');
}
}
}
$alreadyPostulate = null;
$similarOffersExact = $offerRepository->findSimilarExact($offer->getId(), $offer->getContrats(), 2);
if (count($similarOffersExact) > 0) {
$similarOffers = $similarOffersExact;
} else {
$similarOffersPartiel = $offerRepository->findSimilarPartiel($offer->getId(), $offer->getContrats(), 2);
if (count($similarOffersPartiel) > 0) {
$similarOffers = $similarOffersExact;
} else {
$similarOffers = $offerRepository->findSimilar($offer->getId(), 2);
}
}
if ($user) {
$candidature = new Candidature();
$candidature->setOffer($offer);
$candidature->setPostulateAt(new DateTimeImmutable());
if ($user->isFreelance()) {
$candidature->setProfile($user->getUniqueProfile());
$alreadyPostulate = $candidatureRepository->findOneBy([
'profile' => $user->getUniqueProfile(),
'offer' => $offer
]);
} else {
$profiles = $user->getProfiles();
$allCandidatures = $candidatureRepository->findByOfferAndSociety($offer, $profiles);
foreach ($allCandidatures as $c) {
$candidatures[] = $c->getProfile()->getId();
}
}
}
if ($request->isMethod('POST')) {
if ($this->getUser()) {
if ($user->isSociety()) {
$interContrat = $profileRepository->find($request->get('intercontrat'));
$candidature->setProfile($interContrat);
}
$offer->incrementUnseenCandidature();
$candidature->setAnswers($request->get('answers'));
if ($candidature->getProfile()) {
$entityManager->persist($candidature);
$entityManager->flush();
// --- SEND MAIL SERVICE ----
// MAIL TO RECRUTEUR
$emailTo = null;
if ($offer->getUser()) {
$emailTo = $offer->getUser()->getEmail();
} elseif ($offer->getSociety()) {
$emailTo = $offer->getSociety()->getUser()->getEmail();
} elseif ($offer->getUserRecruteur()) {
$emailTo = $offer->getUserRecruteur()->getEmail();
} else {
// External offer without linked society: fetch contact email from external repository using SIREN
if (method_exists($offer, 'getSiren')) {
$siren = $offer->getSiren();
if ($siren) {
$ext = $externalCompanyRepository->findCompanyBySiren($siren);
if ($ext && !empty($ext['email'])) {
$emailTo = $ext['email'];
}
}
}
}
// Fallback: use contact_mail from the offer if available (for Formateur offers)
if (empty($emailTo) && $offer->getContactMail()) {
$emailTo = $offer->getContactMail();
}
// Only send email if we have a valid email address
if (!empty($emailTo)) {
$tempMail = new TempMail();
$tempMail->setAdress($emailTo);
$tempMail->setType_OfferCandidature();
$tempMail->setOffer($offer);
$tempMail->setCandidature($candidature);
$tempMailService->sendingMail($tempMail);
}
}
}
return $this->redirectToRoute('public_mission', ['slug' => $offer->getSlug()]);
}
/** @var DateTime $createdDate */
$createdDate = $offer->getCreatedAt();
$endDate = clone $offer->getExpireAt();
// $endDate->add(new \DateInterval('P6M'));
// if ($offer->getExpireAt()) {
// $endDate = $offer->getExpireAt();
// }
$offerFavorite = $offerFavoriteRepository->findOneBy(['user' => $user, 'offer' => $offer]);
// External company info for this offer if no linked society
$externalCompanies = [];
if ($offer->getSociety() === null && method_exists($offer, 'getSiren')) {
$siren = $offer->getSiren();
if ($siren) {
$info = $externalCompanyRepository->findCompanyBySiren($siren);
if ($info) {
$externalCompanies[$offer->getId()] = $info;
}
}
}
// Récupérer les formations pertinentes basées sur le titre de l'offre
$relevantFormations = [];
try {
$activeUserIds = $formationRepository->getActiveUserIds();
if (!empty($activeUserIds)) {
$relevantFormations = $formationRepository->findFormationsByOfferTitle(
$offer->getTitle(),
3,
$activeUserIds
);
}
} catch (\Throwable $e) {
error_log("Erreur lors de la récupération des formations pertinentes: " . $e->getMessage());
}
return $this->render('page/offer.html.twig', [
'offer' => $offer,
'similarOffers' => $similarOffers,
'isOwner' => $isOwner,
'alreadyPostulate' => $alreadyPostulate,
'isInFavorite' => !empty($offerFavorite),
'candidatures' => $candidatures,
'offerEndDate' => $endDate,
'currentDate' => $currentDate,
'externalCompanies' => $externalCompanies,
'relevantFormations' => $relevantFormations,
]);
}
/**
* @Route("/ajax/submit-candidature", name="ajax_submit_candidature", methods={"POST"})
*/
public function ajaxSubmitCandidature(
Request $request,
ProfileRepository $profileRepository,
OfferRepository $offerRepository,
EntityManagerInterface $entityManager,
CandidatureRepository $candidatureRepository
): JsonResponse {
$user = $this->getUser();
if ($user) {
$candidature = new Candidature();
$offer = $offerRepository->find($request->get('offer'));
$candidature->setOffer($offer);
$candidature->setPostulateAt(new DateTimeImmutable());
if ($user->isFreelance()) {
$candidature->setProfile($user->getUniqueProfile());
$alreadyPostulate = $candidatureRepository->findOneBy([
'profile' => $user->getUniqueProfile(),
'offer' => $offer
]);
} else {
$profiles = $user->getProfiles();
$allCandidatures = $candidatureRepository->findByOfferAndSociety($offer, $profiles);
foreach ($allCandidatures as $c) {
$candidatures[] = $c->getProfile()->getId();
}
}
return new JsonResponse(['status' => 'success']);
}
return new JsonResponse(['status' => 'error'], Response::HTTP_BAD_REQUEST);
}
/**
* @Route("/profil/{slug}", name="public_profile", methods={"GET"})
*/
public function profile(
Profile $profile,
EntityManagerInterface $entityManager,
ProfileRepository $profileRepository,
ProfileVisitRepository $profileVisitRepository,
CandidatureRepository $candidatureRepository,
Request $request
): Response {
/* @var User $user */
$user = $this->getUser();
$siren = $request->query->get('s');
// Si un SIREN est fourni, c'est une offre externe - vérifier l'accès via SIREN
if ($siren) {
// Vérifier que le profil a candidaté sur au moins une offre avec ce SIREN
$candidatures = $candidatureRepository->createQueryBuilder('c')
->join('c.offer', 'o')
->where('c.profile = :profile')
->andWhere('o.siren = :siren')
->andWhere('o.society IS NULL') // Offre externe (sans société liée)
->setParameter('profile', $profile)
->setParameter('siren', $siren)
->getQuery()
->getResult();
// Si aucune candidature trouvée avec ce SIREN, refuser l'accès
if (empty($candidatures)) {
throw $this->createNotFoundException('Accès non autorisé');
}
} else {
// Pas de SIREN fourni - c'est une offre de société locale, appliquer les restrictions d'origine
if (!$user) {
throw $this->createNotFoundException('Accès non autorisé');
}
// Vérifier si l'utilisateur est freelance et essaie de voir un profil qui n'est pas le sien
if ($user->isFreelance() && $profile->getId() !== $user->getUniqueProfile()->getId()) {
return $this->redirectToRoute('dashboard');
}
}
$isOwner = false;
if ($user) {
$isOwner = $profile->getUser()->getId() === $user->getId();
if ($user->getCompteSociety()) {
if ($user->getSociety()->getUser()->getId() === $profile->getUser()->getId()) {
$isOwner = true;
}
}
}
// if ($user->isSociety()) {
// $society = $user->getSociety();
//
// $candidatures = $candidatureRepository->findBySocietyAndProfile($society, $profile);
//
// $ProfilesVisits = $profileVisitRepository->findBySociety($society);
// $profileIds = [];
// foreach ($ProfilesVisits as $visit) {
// $profileIds[] = $visit['id'];
// }
//
// if (!in_array($profile->getId(), $profileIds)) {
// if (!$candidatures && !$society->canViewProfile()) {
// $this->addFlash('package_error', "Acheter un package pour voir le profil");
//
// return $this->redirectToRoute('dashboard');
// }
// }
//
//
// if ($profile->getUser()->isFreelance() ||
// ($profile->getUser()->isSociety() && $society->getId() != $profile->getUser()->getSociety()->getId())
// ) {
// $profileVisit = new ProfileVisit();
// $profileVisit->setSociety($society);
// $profileVisit->setProfile($profile);
// $profileVisit->setUser($user);
// $profileVisit->setViewAt(new DateTimeImmutable());
//
// $entityManager->persist($profileVisit);
//
// if (!$candidatures && !in_array($profile->getId(), $profileIds)) {
// $society->decreaseProfileView();
// }
//
// $entityManager->flush();
// }
// }
$experiences = $profile->getExperiences()->toArray();
// Tri par endAt descendant, puis startAt descendant
usort($experiences, function ($a, $b) {
$aEnd = $a->getEndAt();
$bEnd = $b->getEndAt();
// Si les deux sont null
if ($aEnd === null && $bEnd === null) {
return $b->getStartAt() <=> $a->getStartAt(); // plus récent d'abord
}
// Si a est en poste (null), il passe avant b
if ($aEnd === null) {
return -1;
}
// Si b est en poste (null), il passe avant a
if ($bEnd === null) {
return 1;
}
// Sinon on compare normalement
if ($aEnd == $bEnd) {
return $b->getStartAt() <=> $a->getStartAt();
}
return $bEnd <=> $aEnd;
});
// maj pour que le profil soit vu (seulement si l'utilisateur est connecté)
if ($user) {
$profileVisit = new ProfileVisit();
$profileVisit->setProfile($profile);
$profileVisit->setUser($user);
$profileVisit->setViewAt(new DateTimeImmutable());
if ($user->getSociety()) {
$profileVisit->setSociety($user->getSociety());
}
$entityManager->persist($profileVisit);
$entityManager->flush();
}
return $this->render('page/profile.html.twig', [
'profile' => $profile,
'experiencesSorted' => $experiences,
'reportForm' => $this->createForm(ReportFormType::class, new Report())->createView(),
'reportedId' => $profile->getId(),
'reportedTarget' => 'profile',
'isOwner' => $isOwner,
]);
}
/**
* @Route("/profil_freelancer/{slug}", name="public_profile_freelancer", methods={"GET"})
* @IsGranted("ROLE_FREELANCE")
*/
public function profile_by_freelancer(
Profile $profile,
EntityManagerInterface $entityManager,
ProfileRepository $profileRepository,
ProfileVisitRepository $profileVisitRepository,
CandidatureRepository $candidatureRepository,
OfferRepository $offerRepository
): Response {
/* @var User $user */
$user = $this->getUser();
$offers = $offerRepository->findBy(['user' => $user]);
$candidatures_offer = $candidatureRepository->findBy(['offer' => $offers]);
// Récupérer les IDs des profiles dans un tableau
$profileIds = array_map(function ($candidature) {
return $candidature->getProfile()->getId(); // Récupérer l'ID du profil
}, $candidatures_offer);
if ($user->isFreelance() && !in_array($profile->getId(), $profileIds)) {
return $this->redirectToRoute('dashboard');
}
return $this->render('page/profile.html.twig', [
'profile' => $profile,
'reportForm' => $this->createForm(ReportFormType::class, new Report())->createView(),
'reportedId' => $profile->getId(),
'reportedTarget' => 'profile',
'isOwner' => false
]);
}
/**
* @Route("/email-non-verifie", name="email_unverified", methods={"GET"})
*/
public function userUnverified(): Response
{
$hasVerifiedUser = false;
if ($this->getUser()) {
$hasVerifiedUser = $this->getUser()->isVerified();
}
if ($hasVerifiedUser) {
return $this->redirectToRoute('dashboard');
} else {
return $this->render('page/email_must_verified.html.twig');
}
}
/**
* @Route("/pourquoi-s-inscrire", name="why_subscribe", methods={"GET"})
*/
public function whySubscribe(): Response
{
return $this->render('page/why-subscribe.html.twig');
}
/**
* @Route("/package/checkout", name="payment_validation", methods={"GET"})
*/
public function paymentValidation(
Request $request,
CreateCommande $createCommande,
EntityManagerInterface $entityManager,
UserRepository $userRepository
): Response {
if ($request->query->get('stripe') == 'success') {
$society = $this->getUser()->getSociety();
if (!empty($society) && $request->get('package') == 'year') {
$retour_commande = $createCommande->execute($request->get('package'), $society, ($request->get('multiple') == 0));
if ($retour_commande->getIsPrecommand()) {
$user = $userRepository->createQueryBuilder('u')
->where('u.subscriptionId IS NOT NULL')
->andWhere('u.email = :email')
->setParameter('email', $this->getUser()->getUserIdentifier()) // Replace $someId with the actual id you want to search for
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
// dd($stripeSubscriptionId);
// Configurer l'API Stripe
Stripe::setApiKey($_ENV["STRIPE_SECRET"]);
if ($user !== null && $user !== '') {
$stripeSubscriptionId = $user->getSubscriptionId();
try {
// Annuler l'abonnement
$subscription = Subscription::retrieve($stripeSubscriptionId);
if (!empty($subscription)) {
$subscription->cancel();
// Mettez à jour votre base de données pour refléter l'annulation
$user->setSubscriptionId(null);
$entityManager->flush();
$this->addFlash('success', 'Subscription cancelled successfully.');
}
} catch (\Stripe\Exception\ApiErrorException $e) {
// Gérer les erreurs de l'API Stripe
$this->addFlash('error', 'Une erreur est survenue lors de l\'annulation.');
} catch (\Exception $e) {
// Gérer d'autres erreurs possibles
$this->addFlash('error', 'Une erreur est survenue.');
}
}
}
/* Set compte recrutement */
$nbMois = $request->query->get("nbMois");
if ($nbMois) {
$nbMois = intval($nbMois);
if ($nbMois > 0) {
$retour_commande->setNbMoisRecruit($nbMois);
$entityManager->persist($retour_commande);
for ($i = 1; $i <= $nbMois; $i++) {
$compteRecrutement = new CompteRecrutement();
$compteRecrutement->setSociete($society);
$compteRecrutement->setNbMois($nbMois);
$compteRecrutement->setCommande($retour_commande);
$compteRecrutement->setIsJeton(true);
$entityManager->persist($compteRecrutement);
$entityManager->flush();
}
}
}
}
// Handle success return for monthly subscription checkout
if (!empty($society) && $request->get('package') == 'month') {
// Optionally verify the Checkout Session if session_id was provided in the success URL
$sessionId = $request->query->get('session_id');
if ($sessionId) {
try {
Stripe::setApiKey($_ENV["STRIPE_SECRET"]);
$session = \Stripe\Checkout\Session::retrieve($sessionId);
} catch (\Exception $e) {
// Do not block user if verification fails
}
}
// Avoid duplicate creation if webhook already created it
$lastMonthCommande = $entityManager->getRepository(\App\Entity\Commande::class)
->findOneBy(['society' => $society, 'package' => 'month'], ['id' => 'DESC']);
$shouldCreate = true;
if ($lastMonthCommande && $lastMonthCommande->getCreatedAt()) {
$tenMinutesAgo = new DateTimeImmutable('-10 minutes');
if ($lastMonthCommande->getCreatedAt() >= $tenMinutesAgo) {
$shouldCreate = false;
}
}
if ($shouldCreate) {
$createCommande->execute('month', $society, ($request->get('multiple') == 0));
}
}
$this->addFlash('success_payement', 'Paiement validé.');
return $this->redirectToRoute('commande_list');
} else {
$this->addFlash('error', 'Une erreur s\'est produite lors de votre paiement.');
return $this->redirectToRoute('checkout_package', ['name' => $request->query->get('package')]);
}
}
/**
* @Route("/mail/send/reactivation", name="mail_s_sendReactivation")
*
*
* ENVOYE DE MAIL DE REACTIVATION
*/
public function sendReactivation(
EntityManagerInterface $entityManager,
SocietyRepository $societyRepository,
MailerInterface $mailer,
TempMailService $tempMailService
): Response {
$societies = $societyRepository->findExpiredPauseDate();
$i = 0;
foreach ($societies as $society) {
$society->setPauseDate(null);
$entityManager->persist($society);
$i++;
$entityManager->flush();
// SEND MAIL
$tempMail = new TempMail();
$tempMail->setType_SocietyReactivation();
$tempMail->setAdress($society->getUser()->getEmail());
$tempMail->setSociety($society);
$tempMailService->sendingMail($tempMail);
}
return new Response($i . ' sociétés réactivées');
}
/**
* @Route("/stripe/webhook", name="webhook_stripe")
*/
public function webhook(
Request $request,
CreateCommande $createCommande,
\App\UseCase\Package\Config $config,
HttpClientInterface $client,
SocietyRepository $societyRepository,
UserRepository $userRepository,
EntityManagerInterface $em
) {
// Debug log temporaire
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - Webhook called' . PHP_EOL, FILE_APPEND);
\Stripe\Stripe::setApiKey($_ENV["STRIPE_SECRET"]);
// This is your Stripe CLI webhook secret for testing your endpoint locally.
$endpoint_secret = $_ENV["STRIPE_WEBHOOK_SIGNATURE"];
// Debug: log le secret utilisé
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - Secret: ' . substr($endpoint_secret, 0, 10) . '...' . PHP_EOL, FILE_APPEND);
$payload = @file_get_contents('php://input');
$sig_header = $request->headers->get('stripe-signature');
// Debug: log les headers
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - Sig header: ' . ($sig_header ? 'present' : 'missing') . PHP_EOL, FILE_APPEND);
$event = null;
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
$endpoint_secret
);
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - Event constructed successfully: ' . $event->type . PHP_EOL, FILE_APPEND);
} catch (\UnexpectedValueException $e) {
// Invalid payload
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - UnexpectedValueException: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(400);
exit();
} catch (\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
file_put_contents('webhook_debug.log', date('Y-m-d H:i:s') . ' - SignatureVerificationException: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(400);
exit();
}
switch ($event->type) {
case 'checkout.session.completed':
$invoice = $event->data->object; // contains a \Stripe\Invoice
$metadata = $invoice->metadata;
$userId = $metadata->user_id;
$subscriptionId = $invoice->subscription;
if ($subscriptionId == null or $subscriptionId == '') {
return $this->json(['error' => 'subscription not found']);
}
$user = $userRepository->find($userId);
$society = $user->getSociety();
if (!empty($society)) {
$user->setSubscriptionId($subscriptionId);
$em->persist($user);
$em->flush();
$retour_commande = $createCommande->execute($metadata->package, $society, 0);
return new Response('Success', 200);
}
break;
case 'invoice.payment_succeeded':
$invoice = $event->data->object; // contains a \Stripe\Invoice
try {
$subscription = \Stripe\Subscription::retrieve($invoice->subscription);
if ($subscription->subscription == null or $subscription->subscription == '') {
return $this->json(['error' => 'subscription not found']);
}
// Vérifiez que les métadonnées existent et ne sont pas vides
$user = $userRepository->findOneBy(['subscriptionId' => $subscription->subscription]);
if ($user !== null && $user !== '') {
$society = $user->getSociety();
if (!empty($society)) {
$retour_commande = $createCommande->execute($society->getPackage(), $society, 0);
// Mettez à jour votre base de données ici selon vos besoins
return new Response('Success', 200);
}
} else {
// Gérer le cas où les métadonnées sont absentes ou invalides
return $this->json(['error' => 'User not found']);
}
} catch (\Stripe\Exception\ApiErrorException $e) {
// Handle Stripe API error
return $this->json(['error' => 'Stripe API error'], 404);
} catch (\Exception $e) {
// Handle other errors
return $this->json(['error' => 'An unexpected error occurred'], 404);
}
// // Récupérer le répertoire d'images depuis les paramètres
// $directory = $this->getParameter('img_directory');
//
// // Définir le nom du fichier
// $filename = $directory . '/test2.completed';
// file_put_contents($filename, $subscription);
break;
// Ajouter d'autres cas selon vos besoins
default:
return new Response('Unhandled event type', 400);
}
}
/**
* @Route("/{slug}{trailingSlash}", name="public_page", methods={"GET"}, requirements={"slug"="^(?!formation$|formation/).+?", "trailingSlash"="/?"})
*/
public function page(string $slug, PublicationRepository $publicationRepository, EntityManagerInterface $entityManager): Response
{
if ($slug == 'societe' || $slug == 'formation') {
return $this->render('page/error.html.twig');
}
$publication = $publicationRepository->findOneBy(['slug' => $slug]);
if (null === $publication) {
throw $this->createNotFoundException();
}
$publication->setView($publication->getView() + 1);
$entityManager->persist($publication);
$entityManager->flush();
return $this->render('page/page.html.twig', [
'publication' => $publication,
]);
}
}