Skip to content

Commit

Permalink
PIPRES-319: Atomic process for controllers (#842)
Browse files Browse the repository at this point in the history
* PIPRES-319: Lock process adapter (#839)

* PIPRES-319: Webhook controller protection (#841)

* PIPRES-319: Webhook controller protection

* phpstan and test fix

* warning fix

* stan fix

* webhook thrown exception improvements

* removed comment

* csfixer
  • Loading branch information
mandan2 authored and JevgenijVisockij committed Jan 16, 2024
1 parent ae7959f commit ea7bf2d
Show file tree
Hide file tree
Showing 15 changed files with 616 additions and 94 deletions.
2 changes: 1 addition & 1 deletion controllers/front/payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
*/
class MolliePaymentModuleFrontController extends ModuleFrontController
{
const FILE_NAME = 'payment';
private const FILE_NAME = 'payment';

/** @var bool */
public $ssl = true;
Expand Down
2 changes: 1 addition & 1 deletion controllers/front/return.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class MollieReturnModuleFrontController extends AbstractMollieController
/** @var Mollie */
public $module;

const FILE_NAME = 'return';
private const FILE_NAME = 'return';

/** @var bool */
public $ssl = true;
Expand Down
92 changes: 79 additions & 13 deletions controllers/front/subscriptionUpdateWebhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
* @codingStandardsIgnoreStart
*/

use Mollie\Adapter\ToolsAdapter;
use Mollie\Controller\AbstractMollieController;
use Mollie\Errors\Http\HttpStatusCode;
use Mollie\Handler\ErrorHandler\ErrorHandler;
use Mollie\Infrastructure\Response\JsonResponse;
use Mollie\Logger\PrestaLoggerInterface;
use Mollie\Subscription\Handler\SubscriptionPaymentMethodUpdateHandler;

if (!defined('_PS_VERSION_')) {
Expand All @@ -20,6 +24,8 @@

class MollieSubscriptionUpdateWebhookModuleFrontController extends AbstractMollieController
{
private const FILE_NAME = 'subscriptionUpdateWebhook';

/** @var Mollie */
public $module;
/** @var bool */
Expand All @@ -40,29 +46,89 @@ protected function displayMaintenancePage()

public function initContent()
{
if (Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG)) {
PrestaShopLogger::addLog('Mollie incoming subscription webhook: ' . Tools::file_get_contents('php://input'));
}
/** @var PrestaLoggerInterface $logger */
$logger = $this->module->getService(PrestaLoggerInterface::class);

exit($this->executeWebhook());
}
/** @var ErrorHandler $errorHandler */
$errorHandler = $this->module->getService(ErrorHandler::class);

protected function executeWebhook()
{
$transactionId = Tools::getValue('id');
$subscriptionId = Tools::getValue('subscription_id');
/** @var ToolsAdapter $tools */
$tools = $this->module->getService(ToolsAdapter::class);

$logger->info(sprintf('%s - Controller called', self::FILE_NAME));

if (!$this->module->getApiClient()) {
$logger->error(sprintf('Unauthorized in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Unauthorized', self::FILE_NAME),
HttpStatusCode::HTTP_UNAUTHORIZED
));
}

$transactionId = (string) $tools->getValue('id');

if (!$transactionId) {
$this->respond('failed', HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY, 'Missing transaction id');
$logger->error(sprintf('Missing transaction id in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Missing transaction id', self::FILE_NAME),
HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY
));
}

$subscriptionId = (string) $tools->getValue('subscription_id');

if (!$subscriptionId) {
$this->respond('failed', HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY, 'Missing subscription id');
$logger->error(sprintf('Missing subscription id in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Missing subscription id', self::FILE_NAME),
HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY
));
}

$lockResult = $this->applyLock(sprintf(
'%s-%s-%s',
self::FILE_NAME,
$transactionId,
$subscriptionId
));

if (!$lockResult->isSuccessful()) {
$logger->error(sprintf('Resource conflict in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Resource conflict', self::FILE_NAME),
HttpStatusCode::HTTP_CONFLICT
));
}

/** @var SubscriptionPaymentMethodUpdateHandler $subscriptionPaymentMethodUpdateHandler */
$subscriptionPaymentMethodUpdateHandler = $this->module->getService(SubscriptionPaymentMethodUpdateHandler::class);
$subscriptionPaymentMethodUpdateHandler->handle($transactionId, $subscriptionId);

return 'OK';
try {
$subscriptionPaymentMethodUpdateHandler->handle($transactionId, $subscriptionId);
} catch (\Throwable $exception) {
$logger->error('Failed to handle subscription update', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

$errorHandler->handle($exception, null, false);

$this->releaseLock();

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Failed to handle subscription update', self::FILE_NAME),
$exception->getCode()
));
}

$this->releaseLock();

$logger->info(sprintf('%s - Controller action ended', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::success([]));
}
}
72 changes: 55 additions & 17 deletions controllers/front/subscriptionWebhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
* @codingStandardsIgnoreStart
*/

use Mollie\Adapter\ToolsAdapter;
use Mollie\Controller\AbstractMollieController;
use Mollie\Errors\Http\HttpStatusCode;
use Mollie\Handler\ErrorHandler\ErrorHandler;
use Mollie\Infrastructure\Response\JsonResponse;
use Mollie\Logger\PrestaLoggerInterface;
use Mollie\Subscription\Handler\RecurringOrderHandler;

Expand All @@ -22,6 +24,8 @@

class MollieSubscriptionWebhookModuleFrontController extends AbstractMollieController
{
private const FILE_NAME = 'subscriptionWebhook';

/** @var Mollie */
public $module;
/** @var bool */
Expand All @@ -42,29 +46,54 @@ protected function displayMaintenancePage()

public function initContent()
{
if (Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG)) {
PrestaShopLogger::addLog('Mollie incoming subscription webhook: ' . Tools::file_get_contents('php://input'));
}
/** @var PrestaLoggerInterface $logger */
$logger = $this->module->getService(PrestaLoggerInterface::class);

exit($this->executeWebhook());
}
/** @var ErrorHandler $errorHandler */
$errorHandler = $this->module->getService(ErrorHandler::class);

protected function executeWebhook()
{
$transactionId = Tools::getValue('id');
/** @var ToolsAdapter $tools */
$tools = $this->module->getService(ToolsAdapter::class);

$logger->info(sprintf('%s - Controller called', self::FILE_NAME));

if (!$this->module->getApiClient()) {
$logger->error(sprintf('Unauthorized in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Unauthorized', self::FILE_NAME),
HttpStatusCode::HTTP_UNAUTHORIZED
));
}

$transactionId = (string) $tools->getValue('id');

if (!$transactionId) {
$this->respond('failed', HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY, 'Missing transaction id');
$logger->error(sprintf('Missing transaction id in %s', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Missing transaction id', self::FILE_NAME),
HttpStatusCode::HTTP_UNPROCESSABLE_ENTITY
));
}

/** @var RecurringOrderHandler $recurringOrderHandler */
$recurringOrderHandler = $this->module->getService(RecurringOrderHandler::class);
$lockResult = $this->applyLock(sprintf(
'%s-%s',
self::FILE_NAME,
$transactionId
));

/** @var ErrorHandler $errorHandler */
$errorHandler = $this->module->getService(ErrorHandler::class);
if (!$lockResult->isSuccessful()) {
$logger->error(sprintf('Resource conflict in %s', self::FILE_NAME));

/** @var PrestaLoggerInterface $logger */
$logger = $this->module->getService(PrestaLoggerInterface::class);
$this->ajaxResponse(JsonResponse::error(
$this->module->l('Resource conflict', self::FILE_NAME),
HttpStatusCode::HTTP_CONFLICT
));
}

/** @var RecurringOrderHandler $recurringOrderHandler */
$recurringOrderHandler = $this->module->getService(RecurringOrderHandler::class);

try {
$recurringOrderHandler->handle($transactionId);
Expand All @@ -76,9 +105,18 @@ protected function executeWebhook()

$errorHandler->handle($exception, null, false);

$this->respond('failed', HttpStatusCode::HTTP_BAD_REQUEST);
$this->releaseLock();

$this->ajaxResponse(JsonResponse::error(
$this->module->l('Failed to handle recurring order', self::FILE_NAME),
$exception->getCode()
));
}

$this->respond('OK');
$this->releaseLock();

$logger->info(sprintf('%s - Controller action ended', self::FILE_NAME));

$this->ajaxResponse(JsonResponse::success([]));
}
}
Loading

0 comments on commit ea7bf2d

Please sign in to comment.