src/Controller/CompanySpace/StripeSubscriptionController.php line 166

Open in your IDE?
  1. <?php
  2. namespace App\Controller\CompanySpace;
  3. use App\Entity\Abonnement;
  4. use App\Entity\Company;
  5. use App\Entity\Paiement;
  6. use App\Repository\AbonnementRepository;
  7. use App\Repository\CompanyRepository;
  8. use App\Repository\PaiementRepository;
  9. use App\Repository\TypeAbonnementRepository;
  10. use App\Services\StripeSubscriptionService;
  11. use Doctrine\ORM\EntityManagerInterface;
  12. use Exception;
  13. use Psr\Log\LoggerInterface;
  14. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  15. use Symfony\Component\HttpFoundation\JsonResponse;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpFoundation\Response;
  18. use Symfony\Component\Routing\Annotation\Route;
  19. class StripeSubscriptionController extends AbstractController
  20. {
  21.     private $stripeSubscriptionService;
  22.     public function __construct(StripeSubscriptionService $stripeSubscriptionService)
  23.     {
  24.         $this->stripeSubscriptionService $stripeSubscriptionService;
  25.     }
  26.     /**
  27.      * @Route("/subscription/stripe/main", name="stripe_subscription_main")
  28.      */
  29.     public function index(CompanyRepository $companyRepository): Response
  30.     {
  31.         return $this->render('company_space/stripe_subscription/index.html.twig', [
  32.             'company' => $companyRepository->Find($this->getUser())
  33.         ]);
  34.     }
  35.     /**
  36.      * @Route("/subscription/stripe/type", name="stripe_subscription_type")
  37.      */
  38.     public function subscriptionType(TypeAbonnementRepository $typeAbonnementRepositoryAbonnementRepository $abonnementRepository): Response
  39.     {
  40.         $typeAbonnements $typeAbonnementRepository->findBy([], ['prix' => 'ASC'] );
  41.         $activeSub $abonnementRepository->findOneBy(['company' => $this->getUser(), 'isActive' => true]);
  42.         return $this->render('company_space/stripe_subscription/type.html.twig', [
  43.             'typeAbonnements' => $typeAbonnements,
  44.             'activeSub' => $activeSub,
  45.             'company' => $this->getUser()
  46.         ]);
  47.     }
  48.     /**
  49.      * @Route("/subscription/stripe/return-url", name="stripe_subscription_return_url")
  50.      */
  51.     public function stripeSubscriptionReturnUrl(
  52.         Request $request,
  53.         LoggerInterface $logger,
  54.         AbonnementRepository $abonnementRepository,
  55.         EntityManagerInterface $entityManager,
  56.         TypeAbonnementRepository $typeAbonnementRepository,
  57.         CompanyRepository $companyRepository
  58.     ): Response 
  59.     {
  60.         $checkoutSessionId $request->get('checkout_session');
  61.         $checkoutSession $this->stripeSubscriptionService->getCheckoutSession($checkoutSessionId);
  62.         $subscriptionStripe $this->stripeSubscriptionService->getSubscription($checkoutSession->subscription);
  63.         $subscriptionInvoice $this->stripeSubscriptionService->getInvoice($checkoutSession->invoice);
  64.         if ($checkoutSession->payment_status !== 'paid' || $subscriptionInvoice->status !== 'paid') {
  65.             $this->addFlash('danger''Le paiement n\'a pas été effectué. Veuillez réessayer.');
  66.         }
  67.         $session $checkoutSession;
  68.         $subscriptionId $session->subscription;
  69.         if ($subscriptionId === null) {
  70.             $this->addFlash('danger''Aucune souscription trouvée. Veuillez réessayer.');
  71.             $logger->info('Webhook Stripe no subscription');
  72.             exit();
  73.         }
  74.         // Get subscription
  75.         $typeAbonnementId $subscriptionStripe->plan->id;
  76.         // Get user
  77.         // TODO: pré-remplir le champ email dans le formulaire de souscription
  78.         $customerEmail $session->customer_details->email;
  79.         /** @var Company $user */
  80.         $user $this->getUser() != null $this->getUser() : $companyRepository->findOneBy(['email' => $customerEmail]);
  81.         if (!$user) {
  82.             throw new \Exception('Utilisateur non trouvé');
  83.             // $logger->info('Webhook Stripe user not found');
  84.             //Ajouter un message flash afin d'informer l'utilisateur que l'adresse e-mail ne correspond pas à un compte existant
  85.             http_response_code(404);
  86.             exit();
  87.         }
  88.         // Disable old subscription
  89.         $activeSub $abonnementRepository->findOneBy(['company' => $user'isActive' => true]);
  90.         if ($activeSub) {
  91.             try {
  92.                 $this->stripeSubscriptionService->cancelSubscription($activeSub->getStripeSubscriptionId());
  93.             } catch (\Throwable $th) {
  94.                 $logger->info('Abonnement n°:' $activeSub->getId() . ' non trouvé pour l\'utilisateur avec l\'e-mail: ' $user->getEmail());
  95.             }
  96.             $activeSub->setIsActive(false);
  97.             $entityManager->persist($activeSub);
  98.         }
  99.         // Get plan
  100.         $typeAbonnement $typeAbonnementRepository->findOneBy(['stripeID' => $typeAbonnementId]);
  101.         if (!$typeAbonnement) {
  102.             $logger->info('Webhook Stripe plan not found');
  103.             throw new Exception('Plan not found for Stripe ID: ' $typeAbonnementId);
  104.         }
  105.         $subscription = new Abonnement();
  106.         $subscription->setTypeAbonnement($typeAbonnement);
  107.         $subscription->setStripeSubscriptionId($subscriptionStripe->id);
  108.         $logger->info('Stripe subscription start: ' $subscriptionStripe->current_period_start);
  109.         $logger->info('Stripe subscription end: ' $subscriptionStripe->current_period_end);
  110.         $start = new \DateTimeImmutable();
  111.         $subscription->setCreatedAt($start);
  112.         $endPrototype = clone $start;
  113.         $end $endPrototype->modify('+1 month');
  114.         $subscription->setNextUpdate($end);
  115.         $subscription->setCompany($user);
  116.         $subscription->setIsActive(true);
  117.         $user->setStripeId($session->customer);
  118.         $entityManager->persist($subscription);
  119.         $entityManager->flush();
  120.         $invoice = new Paiement();
  121.         $invoice->setPayinId($subscriptionInvoice->id);
  122.         $invoice->setStripeInvoiceID($subscriptionInvoice->id);
  123.         $invoice->setAbonnement($subscription);
  124.         $invoice->setStripeNumber($subscriptionInvoice->number);
  125.         $invoice->setStripeAmountPaid($subscriptionInvoice->amount_paid);
  126.         // Hosted invoice url is now generated by formator
  127.         $invoice->setHostedInvoiceUrl($subscriptionInvoice->hosted_invoice_url);
  128.         $invoice->setPaid(true);
  129.         $entityManager->persist($invoice);
  130.         $entityManager->flush();
  131.         $this->addFlash('success''Votre abonnement a été créé avec succès !');
  132.         return $this->redirectToRoute('stripe_subscription_details', ['id' => $user->getId()]);
  133.     }
  134.     /**
  135.      * @Route("/subscription/stripe/webhook", name="stripe_subscription_webhook")
  136.      */
  137.     public function webhook(
  138.         LoggerInterface $logger,
  139.         AbonnementRepository $abonnementRepository,
  140.         EntityManagerInterface $entityManager,
  141.         PaiementRepository $paiementRepository
  142.     ): Response {
  143.         \Stripe\Stripe::setApiKey($this->getParameter('stripe_sk'));
  144.         $event null;
  145.         // Check request
  146.         $endpoint_secret $this->getParameter('stripe_webhook_secret');
  147.         $payload = @file_get_contents('php://input');
  148.         $sig_header $_SERVER['HTTP_STRIPE_SIGNATURE'];
  149.         try {
  150.             $event \Stripe\Webhook::constructEvent(
  151.                 $payload,
  152.                 $sig_header,
  153.                 $endpoint_secret
  154.             );
  155.         } catch (\UnexpectedValueException $e) {
  156.             // Invalid payload
  157.             $logger->info('Webhook Stripe Invalid payload');
  158.             http_response_code(400);
  159.             exit();
  160.         } catch (\Stripe\Exception\SignatureVerificationException $e) {
  161.             // Invalid signature
  162.             $logger->info('Webhook Stripe Invalid signature');
  163.             http_response_code(403);
  164.             exit();
  165.         }
  166.         // Handle the event
  167.         switch ($event->type) {
  168.             case 'checkout.session.completed':
  169.                 $logger->info('Webhook Stripe connect checkout.session.completed');
  170.                 break;
  171.             case 'invoice.paid':
  172.                 $logger->info('Invoice paid');
  173.                 $subscriptionId null;
  174.                 if ($event->data->object->parent !== null) {
  175.                     $subscriptionId $event->data->object->parent->subscription_details->subscription;
  176.                 }
  177.                 if ($subscriptionId === null) {
  178.                     $logger->info('No subscription');
  179.                     return new JsonResponse([
  180.                         'status' => 'error',
  181.                         'message' => 'No Stripe subscription found'
  182.                     ], 200);
  183.                 }
  184.                 $subscription null;
  185.                 for ($i 0$i <= && $subscription === null$i++) {
  186.                     $subscription $abonnementRepository->findOneBy(['stripeSubscriptionId' => $subscriptionId]);
  187.                     if ($subscription) {
  188.                         break;
  189.                     }
  190.                     sleep(5);
  191.                 }
  192.                 if (!$subscription) {
  193.                     $logger->info('Subscription not found');
  194.                     return new Response('Non-existent subscription'404);
  195.                 }
  196.                 $paiementExisting $paiementRepository->findOneBy(['stripeInvoiceID' => $event->data->object->id]);
  197.                 
  198.                 if ($paiementExisting) {
  199.                     $logger->info('Paiement already exists for invoice: ' $event->data->object->id);
  200.                     return new Response('Paiement already exists'200);
  201.                 }
  202.                 $invoice = new Paiement();
  203.                 $invoice->setPayinId($event->data->object->id);
  204.                 $invoice->setStripeInvoiceID($event->data->object->id);
  205.                 $invoice->setAbonnement($subscription);
  206.                 $invoice->setStripeNumber($event->data->object->number);
  207.                 $invoice->setStripeAmountPaid($event->data->object->amount_paid);
  208.                 // Hosted invoice url is now generated by formator
  209.                 $invoice->setHostedInvoiceUrl($event->data->object->hosted_invoice_url);
  210.                 $invoice->setPaid(true); // Set paid to true as the invoice is paid
  211.                 $current_subscription_next_update $subscription->getNextUpdate();
  212.                 // Update next update date
  213.                 $next_update $current_subscription_next_update->modify('+1 month');
  214.                 $subscription->setNextUpdate($next_update);
  215.                 if ($subscription->getIsActive() === false) {
  216.                     $subscription->setIsActive(true);
  217.                 }
  218.                 $entityManager->persist($subscription);
  219.                 $entityManager->persist($invoice);
  220.                 $entityManager->flush();
  221.                 break;
  222.             case 'invoice.payment_succeeded':
  223.                 $logger->info('Invoice paid');
  224.                 $subscriptionId null;
  225.                 if ($event->data->object->parent !== null) {
  226.                     $subscriptionId $event->data->object->parent->subscription_details->subscription;
  227.                 }
  228.                 if ($subscriptionId === null) {
  229.                     $logger->info('No subscription');
  230.                     return new JsonResponse([
  231.                         'status' => 'error',
  232.                         'message' => 'No Stripe subscription found'
  233.                     ], 200);
  234.                 }
  235.                 $subscription null;
  236.                 for ($i 0$i <= && $subscription === null$i++) {
  237.                     $subscription $abonnementRepository->findOneBy(['stripeSubscriptionId' => $subscriptionId]);
  238.                     if ($subscription) {
  239.                         break;
  240.                     }
  241.                     sleep(5);
  242.                 }
  243.                 if (!$subscription) {
  244.                     $logger->info('Subscription not found');
  245.                     return new JsonResponse([
  246.                         'status' => 'error',
  247.                         'message' => 'Non-existent subscription'
  248.                     ], 404);
  249.                 }
  250.                 $paiementExisting $paiementRepository->findOneBy(['stripeInvoiceID' => $event->data->object->id]);
  251.                 
  252.                 if ($paiementExisting) {
  253.                     $logger->info('Paiement already exists for invoice: ' $event->data->object->id);
  254.                     return new Response('Paiement already exists'200);
  255.                 }
  256.                 $invoice = new Paiement();
  257.                 $invoice->setPayinId($event->data->object->id);
  258.                 $invoice->setStripeInvoiceID($event->data->object->id);
  259.                 $invoice->setAbonnement($subscription);
  260.                 $invoice->setStripeNumber($event->data->object->number);
  261.                 $invoice->setStripeAmountPaid($event->data->object->amount_paid);
  262.                 // Hosted invoice url is now generated by formator
  263.                 $invoice->setHostedInvoiceUrl($event->data->object->hosted_invoice_url);
  264.                 $invoice->setPaid(true); // Set paid to true as the invoice is paid
  265.                 $current_subscription_next_update $subscription->getNextUpdate();
  266.                 // Update next update date
  267.                 $next_update $current_subscription_next_update->modify('+1 month');
  268.                 $subscription->setNextUpdate($next_update);
  269.                 if ($subscription->getIsActive() === false) {
  270.                     $subscription->setIsActive(true);
  271.                 }
  272.                 $entityManager->persist($subscription);
  273.                 $entityManager->persist($invoice);
  274.                 $entityManager->flush();
  275.                 break;
  276.             
  277.             case 'invoice.payment_failed':
  278.                 $logger->info('invoice.payment_failed event');
  279.                 $subscriptionId null;
  280.                 if ($event->data->object->parent !== null) {
  281.                     $subscriptionId $event->data->object->parent->subscription_details->subscription;
  282.                 }
  283.                 if ($subscriptionId === null) {
  284.                     $logger->info('No subscription for invoice.payment_failed');
  285.                     return new JsonResponse([
  286.                         'status' => 'error',
  287.                         'message' => 'No Stripe subscription found'
  288.                     ], 200);
  289.                 }
  290.                 $subscription $abonnementRepository->findOneBy(['stripeSubscriptionId' => $subscriptionId]);
  291.                 if (!$subscription) {
  292.                     $logger->info('Subscription not found for invoice.payment_failed');
  293.                     return new Response('Non-existent subscription'404);
  294.                 }
  295.                 $invoice = new Paiement();
  296.                 $invoice->setPayinId($event->data->object->id);
  297.                 $invoice->setStripeInvoiceID($event->data->object->id);
  298.                 $invoice->setAbonnement($subscription);
  299.                 $invoice->setStripeNumber($event->data->object->number);
  300.                 $invoice->setStripeAmountPaid($event->data->object->amount_paid);
  301.                 // Hosted invoice url is now generated by formator
  302.                 $invoice->setHostedInvoiceUrl($event->data->object->hosted_invoice_url);
  303.                 $invoice->setPaid(false); // Set paid to false as the invoice payment failed
  304.                 if ($subscription->getIsActive() === true) {
  305.                     $subscription->setIsActive(false);
  306.                 }
  307.                 $entityManager->persist($subscription);
  308.                 $entityManager->persist($invoice);
  309.                 
  310.                 $entityManager->flush();
  311.             default:
  312.                 // Unexpected event type
  313.                 $logger->info('Unexpected event type: ' $event->type);
  314.                 http_response_code(400);
  315.                 exit();
  316.         }
  317.         http_response_code(200);
  318.         $response = new Response('success');
  319.         $response->headers->set('Content-Type''application/json');
  320.         return $response;
  321.     }
  322.     /**
  323.      * @Route("/subscription/stripe/details/{id}", name="stripe_subscription_details")
  324.      */
  325.     public function stripeSubscriptionDetails(Company $companyPaiementRepository $paiementRepository)
  326.     {
  327.         //Si l'id de l'utilisateur en cours n'est pas celui de l'utilisateur recherché
  328.         if ($company->getId() !== $this->getUser()->getId()) {
  329.             return $this->redirectToRoute('front_company_space_dashboard');
  330.         }
  331.         $userSubscription null;
  332.         if ($company->getAbonnements()->count() > 0) {
  333.             $userReccurringPaymentRegistrationId $company->getAbonnements()->last()->getStripeSubscriptionId();
  334.             $userSubscription $this->stripeSubscriptionService->getSubscription($userReccurringPaymentRegistrationId); //129585116
  335.         } else {
  336.             $this->addFlash('danger'"Vous n'avez pas encore d'abonnement !!");
  337.             return $this->redirectToRoute('prestat_abn');
  338.         }
  339.         $paiements $paiementRepository->findBy(['abonnement' => $company->getAbonnements()->last()], ['id' => 'DESC']);
  340.         return $this->render('company_space/stripe_subscription/subscription_details.html.twig', [
  341.             'company' => $company,
  342.             'subscription' => $userSubscription,
  343.             'paiements' => $paiements
  344.         ]);
  345.     }
  346.     /**
  347.      * @Route("/subscription/stripe/cancel/{id}", name="stripe_subscription_cancel")
  348.      */
  349.     public function stripeSubscriptionCancel(Request $requestCompany $companyEntityManagerInterface $entityManager)
  350.     {
  351.         if (!$this->getUser() ) {
  352.             return $this->redirectToRoute('front_home');
  353.         }
  354.         //Si l'id de l'utilisateur en cours n'est pas celui de l'utilisateur recherché
  355.         if ($company->getId() !== $this->getUser()->getId())
  356.         {
  357.             return $this->redirectToRoute('front_company_space_dashboard');
  358.         }
  359.         
  360.         if (!$this->isCsrfTokenValid('unsubscribe' $company->getId(), $request->request->get('token'))) {
  361.             return $this->redirectToRoute('front_company_space_dashboard');
  362.         }
  363.         if ($company->getAbonnements()->count() > 0) {
  364.             $subscription $company->getAbonnements()->last();
  365.             $stripeSubscriptionObject $this->stripeSubscriptionService->getSubscription($subscription->getStripeSubscriptionId());
  366.             if ($stripeSubscriptionObject != null) {
  367.                 if ($stripeSubscriptionObject->status != 'canceled') {
  368.                     try {
  369.                         $this->stripeSubscriptionService->cancelSubscription($subscription->getStripeSubscriptionId());
  370.                         $subscription->setIsActive(false);
  371.                         $entityManager->persist($subscription);
  372.                         $entityManager->flush();
  373.                         $this->addFlash('success''Votre abonnement a été annulé avec succès.');
  374.                     } catch (\Throwable $th) {
  375.                         $this->addFlash('danger'"Une erreur est survenue lors de l'annulation de votre abonnement sur Stripe. ");
  376.                     };
  377.                 } else {
  378.                     $subscription->setIsActive(false);
  379.                     $entityManager->persist($subscription);
  380.                     $entityManager->flush();
  381.                     $this->addFlash('danger'"Votre abonnement a déjà été annulé.");
  382.                 }
  383.             }
  384.             
  385.         } else {
  386.             $this->addFlash('danger'"Vous n'avez pas d'abonnement actif à annuler.");
  387.         }
  388.         return $this->redirectToRoute('stripe_subscription_details', ['id' => $company->getId()]);
  389.     }
  390. }