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

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 $request, Context $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 $context, PayPalApiStruct $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 $token, Context $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. }