custom/plugins/SwagPayPal/src/Webhook/WebhookController.php line 94

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. /*
  3.  * (c) shopware AG <info@shopware.com>
  4.  * For the full copyright and license information, please view the LICENSE
  5.  * file that was distributed with this source code.
  6.  */
  7. namespace Swag\PayPal\Webhook;
  8. use Psr\Log\LoggerInterface;
  9. use Shopware\Core\Framework\Context;
  10. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  13. use Shopware\Core\Framework\Routing\Annotation\Acl;
  14. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  15. use Shopware\Core\Framework\Routing\Annotation\Since;
  16. use Shopware\Core\System\SystemConfig\SystemConfigCollection;
  17. use Swag\PayPal\RestApi\PayPalApiStruct;
  18. use Swag\PayPal\RestApi\V1\Api\Webhook as WebhookV1;
  19. use Swag\PayPal\RestApi\V2\Api\Webhook as WebhookV2;
  20. use Swag\PayPal\Setting\Settings;
  21. use Swag\PayPal\Webhook\Exception\WebhookException;
  22. use Swag\PayPal\Webhook\Exception\WebhookHandlerNotFoundException;
  23. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  28. use Symfony\Component\Routing\Annotation\Route;
  29. /**
  30.  * @RouteScope(scopes={"api"})
  31.  */
  32. class WebhookController extends AbstractController
  33. {
  34.     private LoggerInterface $logger;
  35.     private WebhookServiceInterface $webhookService;
  36.     private EntityRepositoryInterface $systemConfigRepository;
  37.     public function __construct(
  38.         LoggerInterface $logger,
  39.         WebhookServiceInterface $webhookService,
  40.         EntityRepositoryInterface $systemConfigRepository
  41.     ) {
  42.         $this->logger $logger;
  43.         $this->webhookService $webhookService;
  44.         $this->systemConfigRepository $systemConfigRepository;
  45.     }
  46.     /**
  47.      * @Since("0.9.0")
  48.      * @Route(
  49.      *     "/api/_action/paypal/webhook/register/{salesChannelId}",
  50.      *     name="api.action.paypal.webhook.register",
  51.      *     methods={"POST"}
  52.      * )
  53.      * @Acl({"swag_paypal.editor"})
  54.      */
  55.     public function registerWebhook(string $salesChannelId): JsonResponse
  56.     {
  57.         $result $this->webhookService->registerWebhook($salesChannelId !== 'null' $salesChannelId null);
  58.         return new JsonResponse(['result' => $result]);
  59.     }
  60.     /**
  61.      * @Since("1.9.3")
  62.      * @Route(
  63.      *     "/api/_action/paypal/webhook/deregister/{salesChannelId}",
  64.      *     name="api.action.paypal.webhook.deregister",
  65.      *     methods={"DELETE"}
  66.      * )
  67.      * @Acl({"swag_paypal.editor"})
  68.      */
  69.     public function deregisterWebhook(string $salesChannelId): JsonResponse
  70.     {
  71.         $result $this->webhookService->deregisterWebhook($salesChannelId !== 'null' $salesChannelId null);
  72.         return new JsonResponse(['result' => $result]);
  73.     }
  74.     /**
  75.      * @Since("0.9.0")
  76.      * @Route(
  77.      *     "/api/_action/paypal/webhook/execute",
  78.      *     name="api.action.paypal.webhook.execute",
  79.      *     methods={"POST"},
  80.      *     defaults={"auth_required"=false}
  81.      * )
  82.      */
  83.     public function executeWebhook(Request $requestContext $context): Response
  84.     {
  85.         $token $this->getShopwareToken($request);
  86.         $this->validateShopwareToken($token$context);
  87.         $webhook $this->createWebhookFromPostData($request);
  88.         $this->tryToExecuteWebhook($context$webhook);
  89.         return new Response();
  90.     }
  91.     /**
  92.      * @throws BadRequestHttpException
  93.      *
  94.      * @return WebhookV1|WebhookV2
  95.      */
  96.     protected function createWebhookFromPostData(Request $request): PayPalApiStruct
  97.     {
  98.         $postData $request->request->all();
  99.         $this->logger->debug('Received webhook', ['payload' => $postData]);
  100.         if (empty($postData)) {
  101.             throw new BadRequestHttpException('No webhook data sent');
  102.         }
  103.         if (isset($postData['resource_version']) && $postData['resource_version'] === '2.0') {
  104.             $webhook = new WebhookV2();
  105.         } else {
  106.             $webhook = new WebhookV1();
  107.         }
  108.         $webhook->assign($postData);
  109.         return $webhook;
  110.     }
  111.     /**
  112.      * @param WebhookV1|WebhookV2 $webhook
  113.      *
  114.      * @throws BadRequestHttpException
  115.      */
  116.     protected function tryToExecuteWebhook(Context $contextPayPalApiStruct $webhook): void
  117.     {
  118.         try {
  119.             $this->webhookService->executeWebhook($webhook$context);
  120.         } catch (WebhookHandlerNotFoundException $exception) {
  121.             $this->logger->info(\sprintf('[PayPal Webhook] %s'$exception->getMessage()), ['webhook'\json_encode($webhook)]);
  122.         } catch (WebhookException $webhookException) {
  123.             $logMessage \sprintf('[PayPal Webhook] %s'$webhookException->getMessage());
  124.             $logContext = ['type' => $webhookException->getEventType(), 'webhook' => \json_encode($webhook)];
  125.             $this->logger->error($logMessage$logContext);
  126.             throw new BadRequestHttpException('An error occurred during execution of webhook');
  127.         } catch (\Exception $e) {
  128.             $this->logger->error($e->getMessage(), ['error' => $e]);
  129.             throw new BadRequestHttpException('An error occurred during execution of webhook');
  130.         }
  131.     }
  132.     /**
  133.      * @throws BadRequestHttpException
  134.      */
  135.     private function getShopwareToken(Request $request): string
  136.     {
  137.         $token $request->query->getAlnum(WebhookService::PAYPAL_WEBHOOK_TOKEN_NAME);
  138.         if ($token === '') {
  139.             throw new BadRequestHttpException('Shopware token is invalid');
  140.         }
  141.         return $token;
  142.     }
  143.     /**
  144.      * @throws BadRequestHttpException
  145.      */
  146.     private function validateShopwareToken(string $tokenContext $context): void
  147.     {
  148.         $criteria = (new Criteria())->addFilter(new EqualsFilter('configurationValue'$token));
  149.         /** @var SystemConfigCollection $systemConfigCollection */
  150.         $systemConfigCollection $this->systemConfigRepository->search($criteria$context)->getEntities();
  151.         foreach ($systemConfigCollection as $systemConfigEntity) {
  152.             if ($systemConfigEntity->getConfigurationKey() === Settings::WEBHOOK_EXECUTE_TOKEN) {
  153.                 return;
  154.             }
  155.         }
  156.         throw new BadRequestHttpException('Shopware token is invalid');
  157.     }
  158. }