custom/plugins/SwagPayPal/src/Webhook/WebhookService.php line 111

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 Shopware\Core\Framework\Context;
  9. use Shopware\Core\Framework\Util\Random;
  10. use Shopware\Core\System\SystemConfig\SystemConfigService;
  11. use Swag\PayPal\RestApi\PayPalApiStruct;
  12. use Swag\PayPal\RestApi\V1\Api\CreateWebhooks;
  13. use Swag\PayPal\RestApi\V1\Api\Webhook as WebhookV1;
  14. use Swag\PayPal\RestApi\V1\Resource\WebhookResource;
  15. use Swag\PayPal\RestApi\V2\Api\Webhook as WebhookV2;
  16. use Swag\PayPal\Setting\Settings;
  17. use Swag\PayPal\Webhook\Exception\WebhookAlreadyExistsException;
  18. use Swag\PayPal\Webhook\Exception\WebhookIdInvalidException;
  19. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  20. use Symfony\Component\Routing\RouterInterface;
  21. class WebhookService implements WebhookServiceInterface
  22. {
  23. public const WEBHOOK_CREATED = 'created';
  24. public const WEBHOOK_UPDATED = 'updated';
  25. public const WEBHOOK_DELETED = 'deleted';
  26. public const NO_WEBHOOK_ACTION_REQUIRED = 'nothing';
  27. public const PAYPAL_WEBHOOK_TOKEN_NAME = 'sw-token';
  28. public const PAYPAL_WEBHOOK_TOKEN_LENGTH = 32;
  29. private WebhookResource $webhookResource;
  30. private RouterInterface $router;
  31. private WebhookRegistry $webhookRegistry;
  32. private SystemConfigService $systemConfigService;
  33. /**
  34. * @internal
  35. */
  36. public function __construct(
  37. WebhookResource $webhookResource,
  38. WebhookRegistry $webhookRegistry,
  39. SystemConfigService $systemConfigService,
  40. RouterInterface $router
  41. ) {
  42. $this->webhookResource = $webhookResource;
  43. $this->webhookRegistry = $webhookRegistry;
  44. $this->router = $router;
  45. $this->systemConfigService = $systemConfigService;
  46. }
  47. public function registerWebhook(?string $salesChannelId): string
  48. {
  49. $webhookExecuteToken = $this->systemConfigService->getString(Settings::WEBHOOK_EXECUTE_TOKEN, $salesChannelId);
  50. if ($salesChannelId !== null && $webhookExecuteToken === $this->systemConfigService->getString(Settings::WEBHOOK_EXECUTE_TOKEN)) {
  51. // inherited
  52. $webhookExecuteToken = '';
  53. }
  54. if ($webhookExecuteToken === '') {
  55. $webhookExecuteToken = Random::getAlphanumericString(self::PAYPAL_WEBHOOK_TOKEN_LENGTH);
  56. }
  57. $this->router->getContext()->setScheme('https');
  58. $webhookUrl = $this->router->generate(
  59. 'api.action.paypal.webhook.execute',
  60. [self::PAYPAL_WEBHOOK_TOKEN_NAME => $webhookExecuteToken],
  61. UrlGeneratorInterface::ABSOLUTE_URL
  62. );
  63. $webhookId = $this->systemConfigService->getString(Settings::WEBHOOK_ID, $salesChannelId);
  64. if ($salesChannelId !== null && $webhookId === $this->systemConfigService->getString(Settings::WEBHOOK_ID)) {
  65. return $this->createWebhook($salesChannelId, $webhookUrl, $webhookExecuteToken);
  66. }
  67. if ($webhookId === '') {
  68. return $this->createWebhook($salesChannelId, $webhookUrl, $webhookExecuteToken);
  69. }
  70. try {
  71. $registeredWebhookUrl = $this->webhookResource->getWebhookUrl($webhookId, $salesChannelId);
  72. if ($registeredWebhookUrl === $webhookUrl) {
  73. return self::NO_WEBHOOK_ACTION_REQUIRED;
  74. }
  75. } catch (WebhookIdInvalidException $e) {
  76. // do nothing, so the following code will be executed
  77. }
  78. try {
  79. $this->webhookResource->updateWebhook($webhookUrl, $webhookId, $salesChannelId);
  80. return self::WEBHOOK_UPDATED;
  81. } catch (WebhookIdInvalidException $e) {
  82. return $this->createWebhook($salesChannelId, $webhookUrl, $webhookExecuteToken);
  83. }
  84. }
  85. /**
  86. * @param WebhookV1|WebhookV2 $webhook
  87. */
  88. public function executeWebhook(PayPalApiStruct $webhook, Context $context): void
  89. {
  90. $webhookHandler = $this->webhookRegistry->getWebhookHandler($webhook->getEventType());
  91. $webhookHandler->invoke($webhook, $context);
  92. }
  93. public function deregisterWebhook(?string $salesChannelId): string
  94. {
  95. $webhookId = $this->systemConfigService->getString(Settings::WEBHOOK_ID, $salesChannelId);
  96. if ($webhookId === '') {
  97. return WebhookService::NO_WEBHOOK_ACTION_REQUIRED;
  98. }
  99. if ($salesChannelId !== null && $webhookId === $this->systemConfigService->getString(Settings::WEBHOOK_ID)) {
  100. // inherited
  101. return WebhookService::NO_WEBHOOK_ACTION_REQUIRED;
  102. }
  103. try {
  104. $this->webhookResource->deleteWebhook($webhookId, $salesChannelId);
  105. $deleted = true;
  106. } catch (WebhookIdInvalidException $e) {
  107. $deleted = false;
  108. }
  109. $this->systemConfigService->delete(Settings::WEBHOOK_EXECUTE_TOKEN, $salesChannelId);
  110. $this->systemConfigService->delete(Settings::WEBHOOK_ID, $salesChannelId);
  111. return $deleted ? WebhookService::WEBHOOK_DELETED : WebhookService::NO_WEBHOOK_ACTION_REQUIRED;
  112. }
  113. private function createWebhook(
  114. ?string $salesChannelId,
  115. string $webhookUrl,
  116. string $webhookExecuteToken
  117. ): string {
  118. $requestData = [
  119. 'url' => $webhookUrl,
  120. 'event_types' => [['name' => WebhookEventTypes::ALL_EVENTS]],
  121. ];
  122. $createWebhooks = new CreateWebhooks();
  123. $createWebhooks->assign($requestData);
  124. try {
  125. $webhookId = $this->webhookResource->createWebhook(
  126. $webhookUrl,
  127. $createWebhooks,
  128. $salesChannelId
  129. );
  130. $this->systemConfigService->set(Settings::WEBHOOK_EXECUTE_TOKEN, $webhookExecuteToken, $salesChannelId);
  131. $this->systemConfigService->set(Settings::WEBHOOK_ID, $webhookId, $salesChannelId);
  132. return self::WEBHOOK_CREATED;
  133. } catch (WebhookAlreadyExistsException $e) {
  134. return self::NO_WEBHOOK_ACTION_REQUIRED;
  135. }
  136. }
  137. }