Skip to content

Commit

Permalink
[ECP-9177] Implement webhook clean-up cronjob
Browse files Browse the repository at this point in the history
  • Loading branch information
Can Demiralp committed Dec 27, 2024
1 parent 6316bb3 commit 1e81dcf
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 0 deletions.
39 changes: 39 additions & 0 deletions Api/Repository/AdyenNotificationRepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
*
* Adyen Payment Module
*
* Copyright (c) 2024 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Api\Repository;

use Adyen\Payment\Api\Data\NotificationInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\LocalizedException;

interface AdyenNotificationRepositoryInterface
{
/**
* Retrieve Adyen Notification entities which match a specified criteria.
*
* @param SearchCriteriaInterface $searchCriteria
* @return SearchResultsInterface
*
* @throws LocalizedException
*/
public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;

/**
* Deletes a specified Adyen notification.
*
* @param NotificationInterface $entity The notification ID.
* @return bool
*/
public function delete(NotificationInterface $entity): bool;
}
66 changes: 66 additions & 0 deletions Cron/CleanupNotifications.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Cron;

use Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface;
use Adyen\Payment\Cron\Providers\NotificationsProviderInterface;
use Adyen\Payment\Helper\Config;
use Adyen\Payment\Logger\AdyenLogger;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;

class CleanupNotifications
{
/**
* @param NotificationsProviderInterface[] $providers
*/
public function __construct(
private readonly array $providers,
private readonly AdyenLogger $adyenLogger,
private readonly Config $configHelper,
private readonly StoreManagerInterface $storeManager,
private readonly AdyenNotificationRepositoryInterface $adyenNotificationRepository
) { }

/**
* @return void
* @throws NoSuchEntityException
*/
public function execute(): void
{
$storeId = $this->storeManager->getStore()->getId();
$isWebhookCleanupEnabled = $this->configHelper->getIsWebhookCleanupEnabled($storeId);

if ($isWebhookCleanupEnabled) {
$numberOfItemsRemoved = 0;

foreach ($this->providers as $provider) {
foreach ($provider->provide() as $notificationToCleanup) {
$isSuccessfullyDeleted = $this->adyenNotificationRepository->delete($notificationToCleanup);

if ($isSuccessfullyDeleted) {
$numberOfItemsRemoved++;
}
}
}

$successMessage = sprintf(
__('%s webhook notifications have been cleaned-up by the CleanupNotifications job.'),
$numberOfItemsRemoved
);
$this->adyenLogger->addAdyenDebug($successMessage);
} else {
$message = __('Webhook notification clean-up feature is disabled. The job has been skipped!');
$this->adyenLogger->addAdyenDebug($message);
}
}
}
25 changes: 25 additions & 0 deletions Cron/Providers/NotificationsProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Cron\Providers;

interface NotificationsProviderInterface
{
/**
* @return array
*/
public function provide(): array;

/**
* @return string
*/
public function getProviderName(): string;
}
63 changes: 63 additions & 0 deletions Cron/Providers/ProcessedOldNotificationsProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Cron\Providers;

use Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface;
use Adyen\Payment\Helper\Config;
use Adyen\Payment\Logger\AdyenLogger;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Exception\LocalizedException;
use Magento\Store\Model\StoreManagerInterface;

class ProcessedOldNotificationsProvider implements NotificationsProviderInterface
{
public function __construct(
private readonly AdyenNotificationRepositoryInterface $adyenNotificationRepository,
private readonly SearchCriteriaBuilder $searchCriteriaBuilder,
private readonly Config $configHelper,
private readonly StoreManagerInterface $storeManager,
private readonly AdyenLogger $adyenLogger
) { }

public function provide(): array
{
$storeId = $this->storeManager->getStore()->getId();
$numberOfDays = $this->configHelper->getRequiredDaysForOldWebhooks($storeId);

$dateFrom = date('Y-m-d H:i:s', time() - $numberOfDays * 24 * 60 * 60);

$searchCriteria = $this->searchCriteriaBuilder
->addFilter('done', 1)
->addFilter('processing', 0)
->addFilter('created_at', $dateFrom, 'lteq')
->create();

try {
$items = $this->adyenNotificationRepository->getList($searchCriteria);
return $items->getItems();
} catch (LocalizedException $e) {
$errorMessage = sprintf(
__('An error occurred while providing notifications older than %s days!'),
$numberOfDays
);

$this->adyenLogger->error($errorMessage);

return [];
}
}

public function getProviderName(): string
{
return "Adyen processed old webhook notifications";
}
}
21 changes: 21 additions & 0 deletions Helper/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class Config
const XML_RECURRING_CONFIGURATION = 'recurring_configuration';
const XML_ALLOW_MULTISTORE_TOKENS = 'allow_multistore_tokens';
const XML_THREEDS_FLOW = 'threeds_flow';
const XML_CLEANUP_OLD_WEBHOOKS = 'cleanup_old_webhooks';
const XML_REQUIRED_DAYS_OLD_WEBHOOKS = 'required_days_old_webhooks';

protected ScopeConfigInterface $scopeConfig;
private EncryptorInterface $encryptor;
Expand Down Expand Up @@ -592,6 +594,25 @@ public function getThreeDSFlow(int $storeId = null): string
);
}

public function getIsWebhookCleanupEnabled(int $storeId = null): bool
{
return $this->getConfigData(
self::XML_CLEANUP_OLD_WEBHOOKS,
self::XML_ADYEN_ABSTRACT_PREFIX,
$storeId,
true
);
}

public function getRequiredDaysForOldWebhooks(int $storeId = null): int
{
return (int) $this->getConfigData(
self::XML_REQUIRED_DAYS_OLD_WEBHOOKS,
self::XML_ADYEN_ABSTRACT_PREFIX,
$storeId
);
}

public function getConfigData(string $field, string $xmlPrefix, ?int $storeId, bool $flag = false): mixed
{
$path = implode("/", [self::XML_PAYMENT_PREFIX, $xmlPrefix, $field]);
Expand Down
62 changes: 62 additions & 0 deletions Model/AdyenNotificationRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Model;

use Adyen\Payment\Api\Data\NotificationInterface;
use Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface;
use Adyen\Payment\Model\ResourceModel\Notification\CollectionFactory;
use Magento\Framework\Api\Search\SearchResultFactory;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\ObjectManagerInterface;

class AdyenNotificationRepository implements AdyenNotificationRepositoryInterface
{
/**
* @param SearchResultFactory $searchResultsFactory
* @param CollectionFactory $collectionFactory
* @param CollectionProcessor $collectionProcessor
* @param ObjectManagerInterface $objectManager
* @param string $resourceModel
*/
public function __construct(
private readonly SearchResultFactory $searchResultsFactory,
private readonly CollectionFactory $collectionFactory,
private readonly CollectionProcessor $collectionProcessor,
private readonly ObjectManagerInterface $objectManager,
private readonly string $resourceModel
) { }

/**
* @param SearchCriteriaInterface $searchCriteria
* @return SearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface
{
$searchResult = $this->searchResultsFactory->create();
$collection = $this->collectionFactory->create();
$this->collectionProcessor->process($searchCriteria, $collection);
$searchResult->setItems($collection->getItems());
$searchResult->setTotalCount($collection->getSize());

return $searchResult;
}

public function delete(NotificationInterface $entity): bool
{
$resource = $this->objectManager->get($this->resourceModel);
$resource->delete($entity);

return true;
}
}
9 changes: 9 additions & 0 deletions etc/adminhtml/system/adyen_testing_performance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,14 @@
]]>
</comment>
</field>
<field id="cleanup_old_webhooks" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Clean-up old webhooks</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/adyen_abstract/cleanup_old_webhooks</config_path>
<tooltip>
Webhooks older than certain days will be removed from the database by a cronjob if this feature is enabled.
The default value is 90 days and this can be configured by overriding `payment/adyen_abstract/required_days_old_webhooks` configuration path.
</tooltip>
</field>
</group>
</include>
2 changes: 2 additions & 0 deletions etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
<payment_cancelled>canceled</payment_cancelled>
<capture_for_openinvoice>manual</capture_for_openinvoice>
<allow_multistore_tokens>1</allow_multistore_tokens>
<cleanup_old_webhooks>0</cleanup_old_webhooks>
<required_days_old_webhooks>90</required_days_old_webhooks>
</adyen_abstract>
<adyen_cc>
<active>0</active>
Expand Down
3 changes: 3 additions & 0 deletions etc/crontab.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
<job name="adyen_payment_state_data_clean_up" instance="Adyen\Payment\Cron\StateDataCleanUp" method="execute">
<schedule>0 0 * * *</schedule>
</job>
<job name="adyen_payment_cleanup_notification" instance="Adyen\Payment\Cron\CleanupNotifications" method="execute">
<schedule>0 0 * * *</schedule>
</job>
</group>
</config>
16 changes: 16 additions & 0 deletions etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,8 @@
<preference for="Adyen\Payment\Api\Data\NotificationInterface" type="Adyen\Payment\Model\Notification" />
<preference for="Adyen\Payment\Api\AdyenPaymentMethodsBalanceInterface" type="Adyen\Payment\Model\Api\AdyenPaymentMethodsBalance" />
<preference for="Adyen\Payment\Api\TokenDeactivateInterface" type="Adyen\Payment\Model\Api\TokenDeactivate" />
<preference for="Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface"
type="Adyen\Payment\Model\AdyenNotificationRepository" />
<type name="Magento\Vault\Api\PaymentTokenRepositoryInterface">
<plugin name="AdyenPaymentVaultDeleteToken" type="Adyen\Payment\Plugin\PaymentVaultDeleteToken" sortOrder="10" />
</type>
Expand All @@ -1717,6 +1719,20 @@
<type name="Adyen\Payment\Block\Checkout\Multishipping\Billing">
<plugin name="MultishippingPaymentMethods" type="Adyen\Payment\Plugin\MultishippingPaymentMethods" sortOrder="10" />
</type>
<type name="Adyen\Payment\Model\AdyenNotificationRepository">
<arguments>
<argument name="resourceModel" xsi:type="string">Adyen\Payment\Model\ResourceModel\Notification</argument>
</arguments>
</type>
<type name="Adyen\Payment\Cron\CleanupNotifications">
<arguments>
<argument name="providers" xsi:type="array">
<item name="processedOldNotifications" xsi:type="object">
Adyen\Payment\Cron\Providers\ProcessedOldNotificationsProvider
</item>
</argument>
</arguments>
</type>
<virtualType name="AdyenCancelOrders" type="Adyen\Payment\Cron\CancelOrders">
<arguments>
<argument name="providers" xsi:type="array">
Expand Down

0 comments on commit 1e81dcf

Please sign in to comment.