src/Service/Subscription/StripeWebhookService.php line 344

Open in your IDE?
  1. <?php
  2. namespace App\Service\Subscription;
  3. use Psr\Log\LoggerInterface;
  4. use Stripe\Event;
  5. use App\Entity\Payment\Subscription;
  6. use App\Entity\Factures;
  7. use App\Entity\FailedPaymentNotification;
  8. use App\Entity\Centre;
  9. use App\Entity\Audio;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use App\Service\PublicFunction;
  12. use App\Service\Subscription\StripeService;
  13. use App\Entity\SpecificSubscription;
  14. use App\Service\Billing\BillingService;
  15. use App\Service\Notification\FailedPaymentNotificationService;
  16. use App\Service\Billing\BillingAtolService;
  17. use Doctrine\Persistence\ManagerRegistry;
  18. use DateTime;
  19. use App\Service\Notification\EmailNotificationService;
  20. use DateTimeZone;
  21. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  22. class StripeWebhookService
  23. {
  24.     private $logger;
  25.     private $entityManager;
  26.     private $invoice;
  27.     private $invoiceAtol;
  28.     private $publicFunction;
  29.     private $doctrine;
  30.     private $stripe;
  31.     private $emailNotificationService;
  32.     private $failedNotificationService;
  33.     private $parameterBag;
  34.     public function __construct(EntityManagerInterface $entityManager,ParameterBagInterface $parameterBag,EmailNotificationService $emailNotificationService,FailedPaymentNotificationService $failedNotificationService,ManagerRegistry $doctrineLoggerInterface $loggerPublicFunction $publicFunctionBillingService $invoiceBillingAtolService $invoiceAtolStripeService $stripe)
  35.     {
  36.         $this->entityManager $entityManager;
  37.         $this->logger $logger;
  38.         $this->publicFunction $publicFunction;
  39.         $this->invoice $invoice;
  40.         $this->invoiceAtol $invoiceAtol;
  41.         $this->doctrine $doctrine;
  42.         $this->stripe $stripe;
  43.         $this->emailNotificationService $emailNotificationService;
  44.         $this->failedNotificationService $failedNotificationService;
  45.         $this->parameterBag $parameterBag;
  46.     }
  47.     public function constructEventFromPayload($payload$sig_header)
  48.     {
  49.         try {
  50.             $event \Stripe\Event::constructFrom(json_decode($payloadtrue));
  51.             $this->logger->info("Stripe webhook received", ['event' => $event->type]);
  52.             return $event;
  53.         } catch (\Exception $e) {
  54.             $this->logger->error("Error in Stripe webhook", ['message' => $e->getMessage()]);
  55.             throw $e;
  56.         }
  57.     }
  58.     public function handleEvent(Event $event)
  59.     {
  60.         try {
  61.             switch ($event->type) {
  62.                 case 'invoice.paid':
  63.                     $this->handlePaymentSuccess($event);
  64.                     break;
  65.                 case 'invoice.payment_failed':
  66.                     $this->handlePaymentFailed($event);
  67.                     break;  
  68.                 /*case 'invoice.payment_succeeded':
  69.                     $this->handlePaymentSuccess($event);
  70.                     break;*/
  71.                 case 'customer.subscription.trial_will_end':
  72.                     $this->handleTrialWillEnd($event);
  73.                     break;
  74.                 case 'customer.subscription.deleted':
  75.                     $this->handleSubscriptionCancellation($event);
  76.                     break;
  77.   
  78.                 case 'payment_method.attached':
  79.                     $this->retryInvoiceWithNewPaymentMethod($event);
  80.                     break;
  81.                 /*case 'customer.source.created':
  82.                     $this->retryInvoiceWithNewPaymentMethod($event);
  83.                     break;*/
  84.                     
  85.             }
  86.         } catch (\Exception $e) {
  87.             $this->logger->error("Error handling Stripe event", [
  88.                 'event' => $event->type
  89.                 'message' => $e->getMessage()
  90.             ]);
  91.         }
  92.     }
  93.     private function handleTrialWillEnd(Event $event)
  94.     {
  95.         $subscriptionId $event->data->object->id;
  96.         $customerId $event->data->object->customer;
  97.         $invoiceId $event->data->object->id;
  98.         $subscription $this->doctrine
  99.         ->getRepository(Subscription::class)
  100.         ->findOneBy(['stripeCustomerId' => $customerId]);
  101.         // we could place it later in dry function private and also move the subcsription here not in controller center
  102.         
  103.         $centre $this->doctrine
  104.         ->getRepository(Centre::class)
  105.         ->find($subscription->getCentre()->getId());
  106.         $audio $this->doctrine
  107.         ->getRepository(Audio::class)
  108.         ->find($subscription->getAudio()->getId());
  109.     
  110.         $user = [];
  111.         $user['nomAudioprothesiste'] = $audio->getLastName();
  112.         $user['prenomAudioprothesiste'] = $audio->getName();
  113.         $user['emailAudioprothesiste'] = $audio->getMail();
  114.         $user['telephoneAudioprothesiste'] = $audio->getPhone();
  115.         $user['adresseAudioprothesiste'] = $centre->getAddress();
  116.         $user['dateInscription'] = $subscription->getCreated()->format('d-m-Y H:i');
  117.         $user['dateDebutEssai'] = $subscription->getPlanPeriodStart()->format('d-m-Y H:i');
  118.         $user['dateFinEssai'] = $subscription->getPlanPeriodEnd()->format('d-m-Y H:i');
  119.         $user['formule'] = $subscription->getPlan()->getSlug();
  120.         $user['signDate'] = $centre->getSignupDate()->format('d-m-Y H:i');
  121.         $user['typeAbonnement'] = $subscription->getPlanInterval() == "year" "Annuel" "Mensuel";
  122.         $user['nbrLicence'] = $subscription->getQuantity();
  123.         $user['montant'] = $subscription->getPaidAmount();
  124.         $user['urlProfil'] = $centre->getSlug();
  125.         $this->logger->info("Stripe webhook trial user", ['event' => $user]);
  126.         // we send parametre for the trait methid
  127.         $params $this->emailNotificationService->prepareEmailParams($user);
  128.         // send email notifcation
  129.         $this->emailNotificationService->sendEmail($params,"myaudio.fr@gmail.com"'Fin de la période d\'essai gratuit sur My Audio Pro'140);
  130.         $this->emailNotificationService->sendEmail($params,"contact@myaudio.fr"'Fin de la période d\'essai gratuit sur My Audio Pro'140);
  131.         $this->logger->info("Stripe webhook trial", ['event' => "success"]);
  132.     }
  133.     private function handlePaymentSuccess(Event $event)
  134.     {
  135.         $subscriptionId $event->data->object->id;
  136.         $customerId $event->data->object->customer;
  137.         $invoiceId $event->data->object->id;
  138.        
  139.         $lines $event->data['object']['lines']['data'];
  140. foreach ($lines as $line) {
  141.     if (isset($line['period']['end'])) {
  142.         $currentPeriodEnd = (new DateTime())->setTimestamp($line['period']['end']);
  143.     }
  144.     if (isset($line['period']['start'])) {
  145.         $currentPeriodStart = (new DateTime())->setTimestamp($line['period']['start']);
  146.     }
  147.     
  148. }
  149.         // Convert timestamps to DateTime objects
  150.     
  151.         // we will create invoice also need to update the facture->paiment to sripe invoiceid
  152.         /*invoice.finalized
  153.           invoice.created
  154.           charge.failed
  155.           invoice.paid */ 
  156.         sleep(2);    
  157.         $subscription $this->doctrine
  158.         ->getRepository(Subscription::class)
  159.         ->findOneBy(['stripeCustomerId' => $customerId]);
  160.        
  161.         $existing $this->doctrine->getRepository(Factures::class)->findOneBy(['paiement' => $invoiceId]);
  162.         if ($existing) {
  163.            return;
  164.         }
  165.         if($subscription && $event->data->object->amount_due !== 0){
  166.         $center $this->doctrine
  167.         ->getRepository(Centre::class)
  168.         ->find($subscription->getCentre()->getId());
  169.         $specificSubscription $this->doctrine
  170.         ->getRepository(SpecificSubscription::class)
  171.         ->findOneBy(['center' => $center]);
  172.         $data = ["quantity" => $subscription->getQuantity(), "period" => $subscription->getPlanInterval(), "plan" => $subscription->getPlan()->getSlug()];
  173.         $today = new DateTime(); 
  174.         $planPeriodStart $subscription->getPlanPeriodStart(); 
  175.         if ($planPeriodStart->format('Y-m-d') !== $today->format('Y-m-d') && $subscription->getPlanInterval() === 'year') {
  176.             $this->invoice->createClientUpdatedYearly($center,$data);
  177.             $this->logger->info('The plan is yearly, and the start date is different from today.');
  178.         }
  179.         else {
  180.             if($specificSubscription->getContractCategory()->getId() == 7){
  181.                 $data = ["quantity" => $subscription->getQuantity(), "period" => "atol""plan" => $subscription->getPlan()->getSlug()];
  182.                 $this->invoiceAtol->createClient($center,$data);    
  183.             } else{
  184.                 $this->invoice->createClient($center,$data);
  185.             }
  186.         }
  187.         $subscription->setPlanPeriodStart($currentPeriodStart);
  188.         $subscription->setPlanPeriodEnd($currentPeriodEnd);
  189.         $this->entityManager->persist($subscription);
  190.         $invoiceBilling $this->doctrine
  191.         ->getRepository(Factures::class)
  192.         ->findOneBy(
  193.             ['centre' => $subscription->getCentre()->getId()],
  194.             ['id' => 'DESC'
  195.         );
  196.         $invoiceBilling->setPaiement($invoiceId);
  197.         $this->entityManager->persist($invoiceBilling);
  198.         $center $this->doctrine
  199.         ->getRepository(Centre::class)
  200.         ->find($subscription->getCentre()->getId());
  201.         
  202.         $center->setIsBlocked(false);
  203.         $this->entityManager->persist($center);
  204.         $this->entityManager->flush();
  205.         
  206.         $planInterval $subscription->getPlanInterval(); 
  207.         if ($planInterval === 'year') {
  208.             $subscriptionStripe \Stripe\Subscription::retrieve($subscription->getStripeSubscriptionId());
  209.             
  210.             $isDev $_ENV['APP_ENV'] === 'dev';
  211. $isCategory7 $specificSubscription && $specificSubscription->getContractCategory()->getId() == 7;
  212. if ($isDev) {
  213.     $newPriceId $isCategory7 
  214.         'price_1RH6HNG7kx5a2kgk6fRFPNvj' 
  215.         'price_1QY6ZDG7kx5a2kgkwymPYd6H';
  216. } else {
  217.     $newPriceId $isCategory7 
  218.         'price_1RH6LfG7kx5a2kgkecTIQe2W' 
  219.         'price_1QY6dxG7kx5a2kgkC4C0mKbE';
  220. }
  221.                     
  222.             $updatedSubscription \Stripe\Subscription::update(
  223.                 $subscriptionStripe->id,
  224.                 [
  225.                     'items' => [
  226.                         [
  227.                             'id' => $subscriptionStripe->items->data[0]->id
  228.                             'price' => $newPriceId,                          
  229.                         ]
  230.                     ],
  231.                     'proration_behavior' => 'none',
  232.                 ]
  233.             );
  234.         
  235.     }
  236.     // need here to check if has notification inside failure for the facturation 
  237.     // if yes : send special email id (231)+udate the table failur and arcihved
  238.     $this->sendCenterBillingMail($center);
  239.         }
  240.         $this->logger->info("Handled payment success", ['event' => $invoiceId]);
  241.     }
  242.     private function handleSubscriptionCancellation(Event $event)
  243.     {
  244.         $subscriptionId $event->data->object->id;
  245.         $customerId $event->data->object->customer;
  246.     
  247.         //customer.subscription.trial_will_end
  248.         
  249.         $this->logger->info("Handled subscription cancellation", [
  250.             'subscriptionId' => $subscriptionId,
  251.             'customerId' => $customerId,
  252.             'event' => $event->jsonSerialize()
  253.         ]);
  254.     
  255.     }
  256.     private function retryInvoiceWithNewPaymentMethod(Event $event) {
  257.         $subscriptionId $event->data->object->id;
  258.         $customerId $event->data->object->customer;
  259.         
  260.         $outstandingInvoices $this->stripe->getOutstandingInvoices($customerId);
  261.     
  262.         foreach ($outstandingInvoices as $invoice) {
  263.             if ($this->stripe->isInvoiceRetryable($invoice)) {
  264.                 $this->stripe->retryBillingInvoice($invoice->id);
  265.                 $subscription $this->doctrine
  266.                 ->getRepository(Subscription::class)
  267.                 ->findOneBy(['stripeCustomerId' => $customerId]);
  268.                 
  269.                 $center $this->doctrine
  270.                 ->getRepository(Centre::class)
  271.                 ->find($subscription->getCentre()->getId());
  272.                 
  273.                 $center->setIsBlocked(false);
  274.         
  275.                 $this->entityManager->persist($center);
  276.         
  277.                 $this->entityManager->flush();
  278.         
  279.             }
  280.         }
  281.         //customer.subscription.trial_will_end
  282.     
  283.         $this->logger->info("Handled payment method new created", [
  284.             'subscriptionId' => $subscriptionId,
  285.             'customerId' => $customerId,
  286.             'event' => $event->jsonSerialize()
  287.         ]);
  288.     }
  289.     
  290.     private function handlePaymentFailed(Event $event)
  291.     {
  292.         $subscriptionId $event->data->object->id;
  293.         $customerId $event->data->object->customer;
  294.         $this->handlePaymentFailureBlocking($event);
  295.         $this->logger->info("Handled payment failed", [
  296.             'subscriptionId' => $subscriptionId,
  297.             'customerId' => $customerId,
  298.             'event' => $event->jsonSerialize()
  299.         ]);
  300.     
  301.     }
  302.     
  303.     public function handlePaymentFailureBlocking($event)
  304.     {
  305.         $subscriptionId $event->data->object->id;
  306.         $customerId $event->data->object->customer;
  307.     
  308.         $subscription $this->doctrine
  309.         ->getRepository(SpecificSubscription::class)
  310.         ->findOneBy(['stripeCustomer' => $customerId]);
  311.         
  312.         $center $this->doctrine
  313.         ->getRepository(Centre::class)
  314.         ->find($subscription->getCenter()->getId());
  315.         
  316.         $invoice $event->data->object;
  317.         $paymentIntentId $invoice->payment_intent;
  318.         if ($paymentIntentId) {
  319.            $paymentIntent \Stripe\PaymentIntent::retrieve($paymentIntentId);
  320.             if ($paymentIntent->last_payment_error) {
  321.                $comment $paymentIntent->last_payment_error->message;
  322.             }
  323.         }
  324.  
  325.         if (!$center->getIsBlocked()) {
  326.              $this->failedNotificationService->create($center$comment);
  327.         }
  328.     }
  329.     public function getCustomerById(Event $event)
  330.     {
  331.         $subscriptionId $event->data->object->id;
  332.         $customerId $event->data->object->customer;
  333.     
  334.         $subscription $this->doctrine
  335.         ->getRepository(Subscription::class)
  336.         ->findOneBy(['stripeCustomerId' => $customerId]);
  337.         return $subscription;
  338.     }
  339.     private function payInvoice($invoiceId) {
  340.         \Stripe\Stripe::setApiKey('your_stripe_secret_key');
  341.     
  342.         try {
  343.             $invoice \Stripe\Invoice::retrieve($invoiceId);
  344.     
  345.             if ($invoice->status === 'draft') {
  346.                 $invoice->finalizeInvoice();
  347.             }
  348.     
  349.             if ($invoice->status === 'past_due') {
  350.                 $paidInvoice $invoice->pay(['expand' => ['payment_intent']]);
  351.             } else if ($invoice->status === 'open') {
  352.                 $paidInvoice $invoice->pay(['expand' => ['payment_intent']]);
  353.             } else {
  354.                 //..
  355.             }
  356.     
  357.             return $paidInvoice;
  358.         } catch (\Exception $e) {
  359.             // Handle exceptions, possibly log them
  360.             $this->logger->error("Error paying invoice", ['message' => $e->getMessage()]);
  361.             return null;
  362.         }
  363.     }
  364.     
  365.     private function sendCenterBillingMail(Centre $center) : void
  366.     {
  367.         $invoice $this->entityManager->getRepository(Factures::class)->findOneBy(['centre' => $center]);       
  368.         $projectDir $this->parameterBag->get('kernel.project_dir');
  369.         $pdfFilePath $projectDir '/public/assets/partner/facture' $invoice->getToken() . '.pdf';
  370.         $this->failedNotificationService->unblockCenterAndArchive($center);
  371.         $params = [
  372.             "nom" => "{$center->getIdGerant()->getLastName()}",
  373.             "lien" =>"{$_ENV['BASE_logiciel']}",
  374.         ];
  375.         $this->publicFunction->sendEmailWithAttachment($params,$center->getIdGerant()->getMail(),$center->getIdGerant()->getLastName(), "Vos factures sont disponibles"191,$pdfFilePath);
  376.       
  377.         $logger->info('Centre found', ['centre' => $center->getId()]);           
  378.     }
  379. }