src/Controller/WebhooksController.php line 97

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Constant\ShopifyApi;
  4. use App\Constant\StripeSubscription;
  5. use App\Constant\UserStatus;
  6. use App\Entity\Account;
  7. use App\Entity\Membership;
  8. use App\Entity\Payment;
  9. use App\Entity\Project;
  10. use App\Entity\Role;
  11. use App\Entity\Service;
  12. use App\Entity\Subscription;
  13. use Doctrine\Persistence\ManagerRegistry;
  14. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  15. use Symfony\Component\HttpFoundation\Response;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\Routing\Annotation\Route;
  18. /**
  19.  * @Route("/webhooks")
  20.  */
  21. class WebhooksController extends AbstractController
  22. {
  23.     private $doctrine;
  24.     public function __construct(ManagerRegistry $doctrine) {
  25.         $this->doctrine $doctrine;
  26.     }
  27.     /**
  28.      * @Route("/stripe", name="webhook_stripe")
  29.      */
  30.     public function webhookStripe(Request $requestStripeSubscription $stripeSubscription)
  31.     {
  32.         \Stripe\Stripe::setApiKey($this->getParameter('stripe_sk'));
  33.         $payload = @file_get_contents('php://input');
  34.         $event null;
  35.         try {
  36.             $event = \Stripe\Event::constructFrom(
  37.                 json_decode($payloadtrue)
  38.             );
  39.         } catch(\UnexpectedValueException $e) {
  40.             // Invalid payload
  41.             return new Response(''400);
  42.         }
  43.         // Handle the event
  44.         switch ($event->type) {
  45.             case 'invoice.payment_succeeded':
  46.                 $invoice $event->data->object;
  47.                 $this->handlePaymentIntentSucceeded($invoice->subscription);
  48.                 break;
  49.             case 'invoice.payment_failed':
  50.                 $invoice $event->data->object;
  51.                 if ($invoice->attempt_count == $stripeSubscription->getLastStripeInvoiceAttempt()){
  52.                     // TODO - send email to user, subscription expired...
  53.                 }
  54.                 break;
  55.             case 'customer.subscription.deleted':
  56.                 $subscription $event->data->object;
  57.                 $subscriptionId $subscription->id;
  58.                 $subscription $this->doctrine->getRepository(Subscription::class)->findOneBy(['stripeSubscriptionId' => $subscriptionId]);
  59.                 $subscription->setStripeCanceled(true);
  60.                 $subscription->setCanceled(true);
  61.                 $em $this->doctrine->getManager();
  62.                 $em->persist($subscription);
  63.                 $em->flush();
  64.                 break;
  65.             default:
  66.                 echo 'Received unknown event type ' $event->type;
  67.         }
  68.         return new Response(''200);
  69.     }
  70.     private function handlePaymentIntentSucceeded($subscriptionId) :void {
  71.         $subscription $this->doctrine->getRepository(Subscription::class)->findOneBy(['stripeSubscriptionId' => $subscriptionId]);
  72.         // init stripe
  73.         $stripe = new \Stripe\StripeClient($this->getParameter('stripe_sk'));
  74.         $stripeSubscription $stripe->subscriptions->retrieve(
  75.             $subscriptionId,
  76.             []
  77.         );
  78.         $periodEnd $stripeSubscription->current_period_end;
  79.         $dateExpiration date('Y-m-d'$periodEnd);
  80.         $dateTo = \DateTime::createFromFormat('Y-m-d'$dateExpiration);
  81.         $subscription->setExpiryDate($dateTo);
  82.         $em $this->doctrine->getManager();
  83.         $em->persist($subscription);
  84.         $em->flush();
  85.         $this->registerPayment($subscription);
  86.         return;
  87.     }
  88.     /**
  89.      * @Route("/SHP", name="webhook_shopify")
  90.      */
  91.     public function webhookShopify(Request $request)
  92.     {
  93.         $valid true;
  94.         $message null;
  95.         $data json_decode(file_get_contents('php://input'), true);
  96.         $project $this->doctrine->getRepository(Project::class)->findOneBy(['shopIdentity' => $data['shopID']]);
  97.         $subscriptionId $data['subscriptionId'];
  98.         if (!$project){
  99.             $valid false;
  100.             $message 'Subscription not found';
  101.         } else {
  102.             $em $this->doctrine->getManager();
  103.             $subscription $project->getAccount()->getSubscription();
  104.             $pluginId $this->getParameter('shopify_plugin_id');
  105.             $shopUrl $project->getShopIdentity();
  106.             $shopData $project->getShopData();
  107.             $token $shopData["accessToken"];
  108.             $shopifyUrl 'https://'.$shopUrl.'/admin/api/'.ShopifyApi::API_VERSION.'/recurring_application_charges.json';
  109.             $ch curl_init();
  110.             curl_setopt($chCURLOPT_URL$shopifyUrl);
  111.             curl_setopt($chCURLOPT_HTTPAUTHCURLAUTH_BASIC);
  112.             curl_setopt($chCURLOPT_USERPWD"$pluginId:$token");
  113.             curl_setopt($chCURLOPT_HTTPHEADER, array('Content-Type:application/json'));
  114.             curl_setopt($chCURLOPT_RETURNTRANSFER,1);
  115.             $resultShopify curl_exec($ch);
  116.             curl_close($ch);
  117.             $responseShopify json_decode($resultShopifytrue);
  118.             if (isset($responseShopify['recurring_application_charges'][0])){
  119.                 $charge $responseShopify['recurring_application_charges'][0];
  120.                 $expiryDate = new \DateTime($subscription->getExpiryDate()->format("Y-m-d"));
  121.                 $trialEnds = new \DateTime($subscription->getCreatedAt()->modify('+14 days')->format("Y-m-d"));
  122.                 if ($expiryDate != $trialEnds){
  123.                     if ($charge['status'] == 'active' && str_contains($charge['return_url'], 'subscription')) {
  124.                         $newExpiryDate = new \DateTime($charge['billing_on']);
  125.                         $subscription->setExpiryDate($newExpiryDate);
  126.                         $subscription->setStripeSubscriptionId($subscriptionId);
  127.                         $em->persist($subscription);
  128.                         $em->flush();
  129.                         $em->persist($subscription);
  130.                     }
  131.                     $this->registerPayment($subscription);
  132.                 }
  133.                 
  134.                 $message 'Subscription valid to '.$expiryDate->format('Y-m-d');
  135.             } else {
  136.                 $valid false;
  137.                 $message 'Cannot retrieve data from Shopify';
  138.             }
  139.         }
  140.         return new Response(json_encode([
  141.             'valid' => $valid,
  142.             'message' => $message
  143.         ]));
  144.     }
  145.     /**
  146.      * @Route("/WC", name="webhook_woocommerce")
  147.      */
  148.     public function webhookWoocommerce(Request $requestUserStatus $userStatus)
  149.     {
  150.         $em $this->doctrine->getManager();
  151.         $data json_decode(file_get_contents('php://input'), true);
  152.         $subscription $this->doctrine->getRepository(Subscription::class)->findOneBy(['stripeSubscriptionId' => $data['id']]);
  153.         if ($data['status'] == 'active' && $subscription){
  154.             $shouldRegisterPayment true;
  155.             $dateExpiration date('Y-m-d'strtotime($data['next_payment_date']));
  156.             $dateTo = \DateTime::createFromFormat('Y-m-d'$dateExpiration);
  157.             $subscription->setExpiryDate($dateTo);
  158.             $account $subscription->getAccount();
  159.             $account->setIsActive(true);
  160.             $em->persist($account);
  161.             $role $this->doctrine->getRepository(Role::class)->find(1);
  162.             $membership $this->doctrine->getRepository(Membership::class)->findOneBy(['account' => $account'role' => $role]);
  163.             $user $membership->getUser();
  164.             $statusPending $userStatus->getUserStatusId('Pending');
  165.             $statusActive $userStatus->getUserStatusId('Active');
  166.             $currentStatus $user->getStatus();
  167.             if ($currentStatus == $statusPending){
  168.                 $user->setStatus($statusActive);
  169.                 $shouldRegisterPayment false;
  170.             }
  171.             $user->setToken(null);
  172.             $user->setTokenCreatedAt(null);
  173.             $em->persist($user);
  174.             // update service
  175.             $billingIntents $data['billing_intents'];
  176.             $ids array_column($billingIntents'id');
  177.             array_multisort($idsSORT_DESC$billingIntents);
  178.             foreach ($billingIntents as $billing){
  179.                 if ($billing['status'] == 'completed' || $billing['status'] == 'pending'){
  180.                     $serviceName $billing['payload']['name'];
  181.                     $unit substr($billing['payload']['billing_period'],01);
  182.                     $service $this->doctrine->getRepository(Service::class)->findOneBy([
  183.                         'name' => $serviceName,
  184.                         'unit' => $unit,
  185.                         'enablePlugins' => true
  186.                     ]);
  187.                     $subscription->setService($service);
  188.                     $subscription->setCanceled(false);
  189.                     break;
  190.                 }
  191.             }
  192.             if ($shouldRegisterPayment){
  193.                 $account $subscription->getAccount();
  194.                 foreach ($billingIntents as $billing){
  195.                     if ($billing['status'] == 'completed'){
  196.                         $now = new \DateTime('now');
  197.                         $nowDate $now->format('Y-m-d');
  198.                         $payments $account->getPayments();
  199.                         $shouldRegister true;
  200.                         foreach ($payments as $payment){
  201.                             $datePayment $payment->getCreatedAt()->format('Y-m-d');
  202.                             if ($nowDate == $datePayment){
  203.                                 $shouldRegister false;
  204.                             }
  205.                         }
  206.                         if ($shouldRegister){
  207.                             $this->registerPayment($subscription);
  208.                         }
  209.                     }
  210.                 }
  211.             }
  212.             $em->persist($subscription);
  213.             $em->flush();
  214.         }
  215.         if ($data['status'] == 'canceled' && $subscription){
  216.             // store to db
  217.             $em $this->doctrine->getManager();
  218.             $subscription->setCanceled(true);
  219.             $em->persist($subscription);
  220.             $em->flush();
  221.         }
  222.         return new Response(''200);
  223.     }
  224.     /**
  225.      * @param Subscription $subscription
  226.      */
  227.     private function registerPayment (Subscription $subscription) :void
  228.     {
  229.         $payment = new Payment();
  230.         $payment->setAccount($subscription->getAccount());
  231.         $payment->setAmount($subscription->getService()->getPrice());
  232.         $payment->setUnit($subscription->getService()->getUnit());
  233.         $em $this->doctrine->getManager();
  234.         $em->persist($payment);
  235.         $em->flush();
  236.     }
  237. }