Skip to content

[6.6.x] feat: silence webhook token errors #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Resources/config/packages/shopware.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ shopware:
SWAG_PAYPAL__API_CANNOT_BE_ZERO_OR_NEGATIVE: notice
SWAG_PAYPAL__API_PAYMENT_SOURCE_INFO_CANNOT_BE_VERIFIED: notice
SWAG_PAYPAL__API_PAYMENT_SOURCE_DECLINED_BY_PROCESSOR: notice
SWAG_PAYPAL_POS__WEBHOOK_ID_INVALID: warning
41 changes: 22 additions & 19 deletions src/Webhook/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,26 @@ public function deregisterWebhook(string $salesChannelId): JsonResponse
#[Route(path: '/api/_action/paypal/webhook/execute', name: 'api.action.paypal.webhook.execute', methods: ['POST'], defaults: ['auth_required' => false])]
public function executeWebhook(Request $request, Context $context): Response
{
$token = $this->getShopwareToken($request);
$this->validateShopwareToken($token, $context);
$token = $request->query->getAlnum(WebhookService::PAYPAL_WEBHOOK_TOKEN_NAME);
if (!$this->validateShopwareToken($token, $context)) {
if ($id = $request->request->get('id')) {
$this->logger->warning(
'[PayPal Webhook] Shopware token of webhook "{webhookId}" is invalid. Please check for stale webhooks.',
['webhookId' => $id],
);
}

return new Response('Shopware token is invalid', Response::HTTP_BAD_REQUEST);
}

$webhook = $this->createWebhookFromPostData($request);
$this->tryToExecuteWebhook($context, $webhook);

try {
$this->tryToExecuteWebhook($context, $webhook);
} catch (BadRequestHttpException $e) {
// errors are already logged, no need to throw this exception
return new Response($e->getMessage(), $e->getStatusCode());
}

return new Response();
}
Expand Down Expand Up @@ -198,34 +213,22 @@ protected function tryToExecuteWebhook(Context $context, Webhook $webhook): void
}
}

/**
* @throws BadRequestHttpException
*/
private function getShopwareToken(Request $request): string
private function validateShopwareToken(string $token, Context $context): bool
{
$token = $request->query->getAlnum(WebhookService::PAYPAL_WEBHOOK_TOKEN_NAME);
if ($token === '') {
throw new BadRequestHttpException('Shopware token is invalid');
return false;
}

return $token;
}

/**
* @throws BadRequestHttpException
*/
private function validateShopwareToken(string $token, Context $context): void
{
$criteria = (new Criteria())->addFilter(new EqualsFilter('configurationValue', $token));
/** @var SystemConfigCollection $systemConfigCollection */
$systemConfigCollection = $this->systemConfigRepository->search($criteria, $context)->getEntities();

foreach ($systemConfigCollection as $systemConfigEntity) {
if ($systemConfigEntity->getConfigurationKey() === Settings::WEBHOOK_EXECUTE_TOKEN) {
return;
return true;
}
}

throw new BadRequestHttpException('Shopware token is invalid');
return false;
}
}
64 changes: 46 additions & 18 deletions tests/Webhook/WebhookControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ public function testExecuteWebhookThrowsPayPalApiException(): void
$context->addExtension(self::THROW_PAYPAL_API_EXCEPTION, new ArrayStruct());
$request = $this->createRequestWithWebhookData();

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('An error occurred during execution of webhook');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('An error occurred during execution of webhook', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());

static::assertTrue(
$this->logger->hasRecordThatContains('testPayPalApiExceptionMessage', Level::Error),
Expand All @@ -132,9 +133,10 @@ public function testExecuteWebhookThrowsWebhookException(): void
$context->addExtension(self::THROW_WEBHOOK_EXCEPTION, new ArrayStruct());
$request = $this->createRequestWithWebhookData();

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('An error occurred during execution of webhook');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('An error occurred during execution of webhook', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());

static::assertTrue(
$this->logger->hasRecordThatContains('testWebhookExceptionMessage', Level::Error),
Expand All @@ -148,9 +150,10 @@ public function testExecuteWebhookThrowsGeneralException(): void
$context->addExtension(self::THROW_GENERAL_EXCEPTION, new ArrayStruct());
$request = $this->createRequestWithWebhookData();

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('An error occurred during execution of webhook');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('An error occurred during execution of webhook', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());

static::assertTrue(
$this->logger->hasRecordThatContains('testGeneralExceptionMessage', Level::Error),
Expand All @@ -164,19 +167,26 @@ public function testExecuteWebhookEmptyToken(): void
$context->addExtension(self::EMPTY_TOKEN, new ArrayStruct());
$request = $this->createRequestWithWebhookData();

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('Shopware token is invalid');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('Shopware token is invalid', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
static::assertTrue(
$this->logger->hasRecordThatContains('Shopware token of webhook "{webhookId}" is invalid', Level::Warning),
'Expected invalid-shopware-token log entry not found',
);
}

public function testExecuteWebhookEmptyTokenSent(): void
{
$context = Context::createDefaultContext();
$request = new Request();

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('Shopware token is invalid');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('Shopware token is invalid', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
static::assertEquals([], $this->logger->getRecords());
}

public function testExecuteWebhookInvalidToken(): void
Expand All @@ -186,9 +196,27 @@ public function testExecuteWebhookInvalidToken(): void
[WebhookService::PAYPAL_WEBHOOK_TOKEN_NAME => 'invalid-token']
);

$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('Shopware token is invalid');
$this->controller->executeWebhook($request, $context);
$response = $this->controller->executeWebhook($request, $context);

static::assertSame('Shopware token is invalid', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
static::assertEquals([], $this->logger->getRecords());
}

public function testExecuteWebhookInvalidTokenWithId(): void
{
$context = Context::createDefaultContext();
$request = $this->createRequestWithWebhookData();
$request->query->set(WebhookService::PAYPAL_WEBHOOK_TOKEN_NAME, 'invalid-token');

$response = $this->controller->executeWebhook($request, $context);

static::assertSame('Shopware token is invalid', $response->getContent());
static::assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
static::assertTrue(
$this->logger->hasRecordThatContains('Shopware token of webhook "{webhookId}" is invalid', Level::Warning),
'Expected invalid-shopware-token log entry not found',
);
}

public function testExecuteWebhookNoData(): void
Expand Down