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

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