From 7fe65db4844d2a3e197a618b990f504c8ceccfd8 Mon Sep 17 00:00:00 2001 From: mandan2 Date: Wed, 8 Nov 2023 16:55:32 +0200 Subject: [PATCH] PIPRES-319: Lock webhook controller --- controllers/front/webhook.php | 121 +++++++++++++++++++++------------- src/Logger/PrestaLogger.php | 3 + 2 files changed, 80 insertions(+), 44 deletions(-) diff --git a/controllers/front/webhook.php b/controllers/front/webhook.php index 699298883..f6a032926 100644 --- a/controllers/front/webhook.php +++ b/controllers/front/webhook.php @@ -10,11 +10,12 @@ * @codingStandardsIgnoreStart */ -use Mollie\Api\Exceptions\ApiException; use Mollie\Config\Config; use Mollie\Controller\AbstractMollieController; use Mollie\Errors\Http\HttpStatusCode; use Mollie\Handler\ErrorHandler\ErrorHandler; +use Mollie\Infrastructure\Adapter\Lock; +use Mollie\Logger\PrestaLoggerInterface; use Mollie\Service\TransactionService; use Mollie\Utility\TransactionUtility; @@ -38,71 +39,103 @@ class MollieWebhookModuleFrontController extends AbstractMollieController * * @return void */ - protected function displayMaintenancePage() + protected function displayMaintenancePage(): void { } - /** - * @throws ApiException - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - */ - public function initContent() + public function initContent(): void { + /** @var PrestaLoggerInterface $logger */ + $logger = $this->module->getService(PrestaLoggerInterface::class); + + /** @var ErrorHandler $errorHandler */ + $errorHandler = $this->module->getService(ErrorHandler::class); + if ((int) Configuration::get(Config::MOLLIE_DEBUG_LOG) === Config::DEBUG_LOG_ALL) { - PrestaShopLogger::addLog('Mollie incoming webhook: ' . Tools::file_get_contents('php://input')); + $logger->info('Mollie incoming webhook: ' . Tools::file_get_contents('php://input')); + } + + $transactionId = (string) Tools::getValue('id'); + + if (!$transactionId) { + $this->respond('failed', HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY, 'Missing transaction id'); + } + + if (!$this->module->getApiClient()) { + $this->respond('failed', HttpStatusCode::HTTP_UNAUTHORIZED, 'API key is missing or incorrect'); + } + + /** @var Lock $lock */ + $lock = $this->module->getService(Lock::class); + + try { + $lock->create($transactionId); + + $acquired = $lock->acquire(); + } catch (\Throwable $exception) { + $logger->error( + 'Failed to lock process', + [ + 'Exception message' => $exception->getMessage(), + 'Exception code' => $exception->getCode(), + 'transaction_id' => $transactionId, + ] + ); + + $errorHandler->handle($exception, $exception->getCode(), false); + + $this->respond('failed', HttpStatusCode::HTTP_BAD_REQUEST, 'Failed to lock process'); + } + + if (!$acquired) { + $this->respond('failed', HttpStatusCode::HTTP_BAD_REQUEST, 'Another process is locked'); } try { - exit($this->executeWebhook()); + $result = $this->executeWebhook($transactionId); } catch (\Throwable $exception) { - PrestaShopLogger::addLog('Error occurred: ' . $exception->getMessage(), 3, null, 'Mollie'); + $logger->error( + 'Failed to process webhook', + [ + 'Exception message' => $exception->getMessage(), + 'Exception code' => $exception->getCode(), + 'transaction_id' => $transactionId, + ] + ); + + $errorHandler->handle($exception, $exception->getCode(), false); + + $this->respond('failed', HttpStatusCode::HTTP_BAD_REQUEST, 'Failed to process webhook'); } + + $this->respond('success', HttpStatusCode::HTTP_OK, $result); } /** - * @return string - * - * @throws ApiException - * @throws PrestaShopDatabaseException - * @throws PrestaShopException + * @throws \Throwable */ - protected function executeWebhook() + protected function executeWebhook(string $transactionId): string { /** @var TransactionService $transactionService */ $transactionService = $this->module->getService(TransactionService::class); - /** @var ErrorHandler $errorHandler */ - $errorHandler = $this->module->getService(ErrorHandler::class); - - $transactionId = Tools::getValue('id'); - if (!$transactionId) { - $this->respond('failed', HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY, 'Missing transaction id'); - } + // TODO even if transaction is not found, we should return OK 200 - if (!$this->module->getApiClient()) { - $this->respond('failed', HttpStatusCode::HTTP_UNAUTHORIZED, 'API key is missing or incorrect'); - } + if (TransactionUtility::isOrderTransaction($transactionId)) { + $transaction = $this->module->getApiClient()->orders->get($transactionId, ['embed' => 'payments']); + } else { + $transaction = $this->module->getApiClient()->payments->get($transactionId); - try { - if (TransactionUtility::isOrderTransaction($transactionId)) { - $transaction = $this->module->getApiClient()->orders->get($transactionId, ['embed' => 'payments']); - } else { - $transaction = $this->module->getApiClient()->payments->get($transactionId); - if ($transaction->orderId) { - $transaction = $this->module->getApiClient()->orders->get($transaction->orderId, ['embed' => 'payments']); - } + if ($transaction->orderId) { + $transaction = $this->module->getApiClient()->orders->get($transaction->orderId, ['embed' => 'payments']); } - $metaData = $transaction->metadata; - $cartId = $metaData->cart_id ?? 0; - $this->setContext($cartId); - $payment = $transactionService->processTransaction($transaction); - } catch (\Throwable $e) { - $errorHandler->handle($e, $e->getCode(), false); - $this->respond('failed', $e->getCode(), $e->getMessage()); } - /* @phpstan-ignore-next-line */ + $metaData = $transaction->metadata; + $cartId = $metaData->cart_id ?? 0; + $this->setContext($cartId); + $payment = $transactionService->processTransaction($transaction); + if (is_string($payment)) { return $payment; } @@ -110,7 +143,7 @@ protected function executeWebhook() return 'OK'; } - private function setContext(int $cartId) + private function setContext(int $cartId): void { if (!$cartId) { return; diff --git a/src/Logger/PrestaLogger.php b/src/Logger/PrestaLogger.php index 67afcc3c8..c9683cffc 100644 --- a/src/Logger/PrestaLogger.php +++ b/src/Logger/PrestaLogger.php @@ -16,6 +16,9 @@ class PrestaLogger implements PrestaLoggerInterface { + // TODO integrate sentry into logger service + // TODO integrate logs switch into logger service + public function emergency($message, array $context = []) { throw new NotImplementedException('not implemented method');