diff --git a/Api/Repository/AdyenNotificationRepositoryInterface.php b/Api/Repository/AdyenNotificationRepositoryInterface.php
new file mode 100644
index 000000000..67453ec80
--- /dev/null
+++ b/Api/Repository/AdyenNotificationRepositoryInterface.php
@@ -0,0 +1,39 @@
+
+ */
+
+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;
+}
diff --git a/Cron/CleanupNotifications.php b/Cron/CleanupNotifications.php
new file mode 100644
index 000000000..909f87636
--- /dev/null
+++ b/Cron/CleanupNotifications.php
@@ -0,0 +1,72 @@
+
+ */
+
+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 Adyen\Payment\Model\Notification;
+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) {
+ /** @var Notification $notificationToCleanup */
+ foreach ($provider->provide() as $notificationToCleanup) {
+ $isSuccessfullyDeleted = $this->adyenNotificationRepository->delete($notificationToCleanup);
+
+ if ($isSuccessfullyDeleted) {
+ $message = __('%1: Notification with entityId %2 has been deleted.',
+ $provider->getProviderName(), $notificationToCleanup->getEntityId());
+ $this->adyenLogger->addAdyenNotification($message);
+
+ $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);
+ }
+ }
+}
diff --git a/Cron/Providers/NotificationsProviderInterface.php b/Cron/Providers/NotificationsProviderInterface.php
new file mode 100644
index 000000000..34f9b0625
--- /dev/null
+++ b/Cron/Providers/NotificationsProviderInterface.php
@@ -0,0 +1,25 @@
+
+ */
+
+namespace Adyen\Payment\Cron\Providers;
+
+interface NotificationsProviderInterface
+{
+ /**
+ * @return array
+ */
+ public function provide(): array;
+
+ /**
+ * @return string
+ */
+ public function getProviderName(): string;
+}
diff --git a/Cron/Providers/ProcessedOldNotificationsProvider.php b/Cron/Providers/ProcessedOldNotificationsProvider.php
new file mode 100644
index 000000000..60196b7c1
--- /dev/null
+++ b/Cron/Providers/ProcessedOldNotificationsProvider.php
@@ -0,0 +1,63 @@
+
+ */
+
+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";
+ }
+}
diff --git a/Helper/Config.php b/Helper/Config.php
index 9bbebe10a..14b15ca55 100644
--- a/Helper/Config.php
+++ b/Helper/Config.php
@@ -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;
@@ -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 getIsCvcRequiredForRecurringCardPayments(int $storeId = null): bool
{
return (bool) $this->getConfigData(
diff --git a/Model/AdyenNotificationRepository.php b/Model/AdyenNotificationRepository.php
new file mode 100644
index 000000000..277d12be6
--- /dev/null
+++ b/Model/AdyenNotificationRepository.php
@@ -0,0 +1,62 @@
+
+ */
+
+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;
+ }
+}
diff --git a/Test/Unit/Cron/CleanupNotificationsTest.php b/Test/Unit/Cron/CleanupNotificationsTest.php
new file mode 100644
index 000000000..cc2b04420
--- /dev/null
+++ b/Test/Unit/Cron/CleanupNotificationsTest.php
@@ -0,0 +1,101 @@
+
+ */
+
+namespace Adyen\Payment\Test\Cron;
+
+use Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface;
+use Adyen\Payment\Cron\CleanupNotifications;
+use Adyen\Payment\Cron\Providers\NotificationsProviderInterface;
+use Adyen\Payment\Helper\Config;
+use Adyen\Payment\Logger\AdyenLogger;
+use Adyen\Payment\Model\Notification;
+use Adyen\Payment\Test\Unit\AbstractAdyenTestCase;
+use Magento\Store\Api\Data\StoreInterface;
+use Magento\Store\Model\StoreManagerInterface;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class CleanupNotificationsTest extends AbstractAdyenTestCase
+{
+ protected ?CleanupNotifications $cleanupNotifications;
+ protected AdyenLogger|MockObject $adyenLoggerMock;
+ protected Config|MockObject $configHelperMock;
+ protected StoreManagerInterface|MockObject $storeManagerMock;
+ protected AdyenNotificationRepositoryInterface|MockObject $adyenNotificationRepositoryMock;
+ protected NotificationsProviderInterface|MockObject $notificationsProvider;
+ protected array $providers;
+
+ const STORE_ID = PHP_INT_MAX;
+
+ protected function setUp(): void
+ {
+ $this->adyenLoggerMock = $this->createMock(AdyenLogger::class);
+ $this->configHelperMock = $this->createMock(Config::class);
+ $this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
+ $this->adyenNotificationRepositoryMock =
+ $this->createMock(AdyenNotificationRepositoryInterface::class);
+ $this->notificationsProvider = $this->createMock(NotificationsProviderInterface::class);
+
+ $this->providers[] = $this->notificationsProvider;
+
+ $storeMock = $this->createMock(StoreInterface::class);
+ $storeMock->method('getId')->willReturn(self::STORE_ID);
+ $this->storeManagerMock->method('getStore')->willReturn($storeMock);
+
+ $this->cleanupNotifications = new CleanupNotifications(
+ $this->providers,
+ $this->adyenLoggerMock,
+ $this->configHelperMock,
+ $this->storeManagerMock,
+ $this->adyenNotificationRepositoryMock
+ );
+ }
+
+ protected function tearDown(): void
+ {
+ $this->cleanupNotifications = null;
+ }
+
+ public function testExecuteConfigEnabled()
+ {
+ $this->configHelperMock->expects($this->once())
+ ->method('getIsWebhookCleanupEnabled')
+ ->with(self::STORE_ID)
+ ->willReturn(true);
+
+ $notificationMock = $this->createMock(Notification::class);
+ $providerResult[] = $notificationMock;
+
+ $this->notificationsProvider->method('provide')->willReturn($providerResult);
+
+ $this->adyenNotificationRepositoryMock->expects($this->once())
+ ->method('delete')
+ ->with($notificationMock)
+ ->willReturn(true);
+
+ $this->adyenLoggerMock->expects($this->once())->method('addAdyenDebug');
+
+ $this->cleanupNotifications->execute();
+ }
+
+ public function testExecuteConfigDisabled()
+ {
+ $this->configHelperMock->expects($this->once())
+ ->method('getIsWebhookCleanupEnabled')
+ ->with(self::STORE_ID)
+ ->willReturn(false);
+
+ $this->notificationsProvider->expects($this->never())->method('provide');
+ $this->adyenNotificationRepositoryMock->expects($this->never())->method('delete');
+ $this->adyenLoggerMock->expects($this->once())->method('addAdyenDebug');
+
+ $this->cleanupNotifications->execute();
+ }
+}
diff --git a/Test/Unit/Cron/Providers/ProcessedOldNotificationsProviderTest.php b/Test/Unit/Cron/Providers/ProcessedOldNotificationsProviderTest.php
new file mode 100644
index 000000000..dd77a1af1
--- /dev/null
+++ b/Test/Unit/Cron/Providers/ProcessedOldNotificationsProviderTest.php
@@ -0,0 +1,147 @@
+
+ */
+
+namespace Adyen\Payment\Test\Cron\Providers;
+
+use Adyen\Payment\Api\Repository\AdyenNotificationRepositoryInterface;
+use Adyen\Payment\Cron\Providers\ProcessedOldNotificationsProvider;
+use Adyen\Payment\Helper\Config;
+use Adyen\Payment\Logger\AdyenLogger;
+use Adyen\Payment\Test\Unit\AbstractAdyenTestCase;
+use Magento\Framework\Api\SearchCriteria;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\Api\SearchResultsInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Store\Api\Data\StoreInterface;
+use Magento\Store\Model\StoreManagerInterface;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class ProcessedOldNotificationsProviderTest extends AbstractAdyenTestCase
+{
+ protected ?ProcessedOldNotificationsProvider $notificationsProvider;
+ protected AdyenNotificationRepositoryInterface|MockObject $adyenNotificationRepositoryMock;
+ protected SearchCriteriaBuilder|MockObject $searchCriteriaBuilderMock;
+ protected Config|MockObject $configHelperMock;
+ protected StoreManagerInterface|MockObject $storeManagerMock;
+ protected AdyenLogger|MockObject $adyenLoggerMock;
+
+ const STORE_ID = PHP_INT_MAX;
+
+ protected function setUp(): void
+ {
+ $this->adyenNotificationRepositoryMock =
+ $this->createMock(AdyenNotificationRepositoryInterface::class);
+ $this->searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class);
+ $this->configHelperMock = $this->createMock(Config::class);
+ $this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
+ $this->adyenLoggerMock = $this->createMock(AdyenLogger::class);
+
+ $storeMock = $this->createMock(StoreInterface::class);
+ $storeMock->method('getId')->willReturn(self::STORE_ID);
+ $this->storeManagerMock->method('getStore')->willReturn($storeMock);
+
+ $this->notificationsProvider = new ProcessedOldNotificationsProvider(
+ $this->adyenNotificationRepositoryMock,
+ $this->searchCriteriaBuilderMock,
+ $this->configHelperMock,
+ $this->storeManagerMock,
+ $this->adyenLoggerMock
+ );
+ }
+
+ protected function tearDown(): void
+ {
+ $this->notificationsProvider = null;
+ }
+
+ public function testProvideSuccess()
+ {
+ $expiryDays = 90;
+
+ $this->configHelperMock->expects($this->once())
+ ->method('getRequiredDaysForOldWebhooks')
+ ->with(self::STORE_ID)
+ ->willReturn($expiryDays);
+
+ $dateMock = date('Y-m-d H:i:s', time() - $expiryDays * 24 * 60 * 60);
+
+ $this->searchCriteriaBuilderMock->expects($this->exactly(3))
+ ->method('addFilter')
+ ->withConsecutive(
+ ['done', 1, 'eq'],
+ ['processing', 0, 'eq'],
+ ['created_at', $dateMock, 'lteq']
+ )
+ ->willReturnSelf();
+
+ $searchCriteriaMock = $this->createMock(SearchCriteria::class);
+
+ $this->searchCriteriaBuilderMock->expects($this->once())
+ ->method('create')
+ ->willReturn($searchCriteriaMock);
+
+ $searchResultsMock = $this->createMock(SearchResultsInterface::class);
+ $searchResultsMock->expects($this->once())
+ ->method('getItems')
+ ->willReturn([]);
+
+ $this->adyenNotificationRepositoryMock->expects($this->once())
+ ->method('getList')
+ ->with($searchCriteriaMock)
+ ->willReturn($searchResultsMock);
+
+ $this->notificationsProvider->provide();
+ }
+
+ public function testProvideFailure()
+ {
+ $expiryDays = 90;
+
+ $this->configHelperMock->expects($this->once())
+ ->method('getRequiredDaysForOldWebhooks')
+ ->with(self::STORE_ID)
+ ->willReturn($expiryDays);
+
+ $dateMock = date('Y-m-d H:i:s', time() - $expiryDays * 24 * 60 * 60);
+
+ $this->searchCriteriaBuilderMock->expects($this->exactly(3))
+ ->method('addFilter')
+ ->withConsecutive(
+ ['done', 1, 'eq'],
+ ['processing', 0, 'eq'],
+ ['created_at', $dateMock, 'lteq']
+ )
+ ->willReturnSelf();
+
+ $searchCriteriaMock = $this->createMock(SearchCriteria::class);
+
+ $this->searchCriteriaBuilderMock->expects($this->once())
+ ->method('create')
+ ->willReturn($searchCriteriaMock);
+
+ $this->adyenNotificationRepositoryMock->expects($this->once())
+ ->method('getList')
+ ->willThrowException(new LocalizedException(__('mock error message')));
+
+ $this->adyenLoggerMock->expects($this->once())->method('error');
+
+ $result = $this->notificationsProvider->provide();
+ $this->assertEmpty($result);
+ }
+
+ public function testGetProviderName()
+ {
+ $this->assertEquals(
+ 'Adyen processed old webhook notifications',
+ $this->notificationsProvider->getProviderName()
+ );
+ }
+}
diff --git a/Test/Unit/Helper/ConfigTest.php b/Test/Unit/Helper/ConfigTest.php
index c67a06cbc..abbbdb780 100644
--- a/Test/Unit/Helper/ConfigTest.php
+++ b/Test/Unit/Helper/ConfigTest.php
@@ -169,4 +169,45 @@ public function testGetIsCvcRequiredForRecurringCardPayments()
$result = $this->configHelper->getIsCvcRequiredForRecurringCardPayments($storeId);
$this->assertEquals($expectedResult, $result);
}
+
+ public function testGetIsWebhookCleanupEnabled()
+ {
+ $storeId = PHP_INT_MAX;
+
+ $path = sprintf(
+ "%s/%s/%s",
+ Config::XML_PAYMENT_PREFIX,
+ Config::XML_ADYEN_ABSTRACT_PREFIX,
+ Config::XML_CLEANUP_OLD_WEBHOOKS
+ );
+
+ $this->scopeConfigMock->expects($this->once())
+ ->method('isSetFlag')
+ ->with($this->equalTo($path), $this->equalTo(ScopeInterface::SCOPE_STORE), $this->equalTo($storeId))
+ ->willReturn(true);
+
+ $result = $this->configHelper->getIsWebhookCleanupEnabled($storeId);
+ $this->assertTrue($result);
+ }
+
+ public function testGetRequiredDaysForOldWebhooks()
+ {
+ $storeId = PHP_INT_MAX;
+
+ $path = sprintf(
+ "%s/%s/%s",
+ Config::XML_PAYMENT_PREFIX,
+ Config::XML_ADYEN_ABSTRACT_PREFIX,
+ Config::XML_REQUIRED_DAYS_OLD_WEBHOOKS
+ );
+
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->with($this->equalTo($path), $this->equalTo(ScopeInterface::SCOPE_STORE), $this->equalTo($storeId))
+ ->willReturn("90");
+
+ $result = $this->configHelper->getRequiredDaysForOldWebhooks($storeId);
+ $this->assertIsInt($result);
+ $this->assertEquals(90, $result);
+ }
}
diff --git a/Test/Unit/Model/AdyenNotificationRepositoryTest.php b/Test/Unit/Model/AdyenNotificationRepositoryTest.php
new file mode 100644
index 000000000..82dba02a4
--- /dev/null
+++ b/Test/Unit/Model/AdyenNotificationRepositoryTest.php
@@ -0,0 +1,102 @@
+
+ */
+
+namespace Adyen\Payment\Test\Helper\Unit\Model;
+
+use Adyen\Payment\Model\AdyenNotificationRepository;
+use Adyen\Payment\Model\Notification as NotificationEntity;
+use Adyen\Payment\Model\ResourceModel\Notification;
+use Adyen\Payment\Model\ResourceModel\Notification\Collection;
+use Adyen\Payment\Model\ResourceModel\Notification\CollectionFactory;
+use Adyen\Payment\Test\Unit\AbstractAdyenTestCase;
+use Magento\Framework\Api\Search\SearchResultFactory;
+use Magento\Framework\Api\Search\SearchResultInterface;
+use Magento\Framework\Api\SearchCriteria\CollectionProcessor;
+use Magento\Framework\Api\SearchCriteriaInterface;
+use Magento\Framework\Api\SearchResultsInterface;
+use Magento\Framework\ObjectManagerInterface;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class AdyenNotificationRepositoryTest extends AbstractAdyenTestCase
+{
+ protected ?AdyenNotificationRepository $adyenNotificationRepository;
+ protected SearchResultFactory|MockObject $searchResultFactoryMock;
+ protected CollectionFactory|MockObject $collectionFactoryMock;
+ protected CollectionProcessor|MockObject $collectionProcessorMock;
+ protected ObjectManagerInterface|MockObject $objectManagerMock;
+
+ const RESOURCE_MODEL = 'Adyen\Payment\Model\ResourceModel\Notification';
+
+ protected function setUp(): void
+ {
+ $this->searchResultFactoryMock = $this->createMock(SearchResultFactory::class);
+ $this->collectionFactoryMock = $this->createGeneratedMock(
+ CollectionFactory::class,
+ ['create']
+ );
+ $this->collectionProcessorMock = $this->createMock(CollectionProcessor::class);
+ $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class);
+
+ $this->adyenNotificationRepository = new AdyenNotificationRepository(
+ $this->searchResultFactoryMock,
+ $this->collectionFactoryMock,
+ $this->collectionProcessorMock,
+ $this->objectManagerMock,
+ self::RESOURCE_MODEL
+ );
+ }
+
+ protected function tearDown(): void
+ {
+ $this->adyenNotificationRepository = null;
+ }
+
+ public function testGetList()
+ {
+ $searchResult = $this->createMock(SearchResultInterface::class);
+ $searchResult->expects($this->once())->method('setItems');
+ $searchResult->expects($this->once())->method('setTotalCount');
+
+ $this->searchResultFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($searchResult);
+
+ $collection = $this->createMock(Collection::class);
+ $this->collectionFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($collection);
+
+ $searchCriteria = $this->createMock(SearchCriteriaInterface::class);
+ $this->collectionProcessorMock->expects($this->once())
+ ->method('process')
+ ->with($searchCriteria, $collection);
+
+
+ $result = $this->adyenNotificationRepository->getList($searchCriteria);
+ $this->assertInstanceOf(SearchResultsInterface::class, $result);
+ }
+
+ public function testDelete()
+ {
+ $entityMock = $this->createMock(NotificationEntity::class);
+
+ $resourceModelMock = $this->createMock(Notification::class);
+ $resourceModelMock->expects($this->once())->method('delete')->with($entityMock);
+
+ $this->objectManagerMock->expects($this->once())
+ ->method('get')
+ ->willReturn($resourceModelMock);
+
+ $result = $this->adyenNotificationRepository->delete($entityMock);
+
+ $this->assertTrue($result);
+ }
+}
diff --git a/etc/adminhtml/system/adyen_testing_performance.xml b/etc/adminhtml/system/adyen_testing_performance.xml
index c38442884..358be8ffc 100644
--- a/etc/adminhtml/system/adyen_testing_performance.xml
+++ b/etc/adminhtml/system/adyen_testing_performance.xml
@@ -55,5 +55,14 @@
]]>
+
+
+ Magento\Config\Model\Config\Source\Yesno
+ payment/adyen_abstract/cleanup_old_webhooks
+
+ 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.
+
+
diff --git a/etc/config.xml b/etc/config.xml
index 81527b619..061f4cc71 100755
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -34,6 +34,8 @@
canceled
manual
1
+ 0
+ 90
0
diff --git a/etc/crontab.xml b/etc/crontab.xml
index d638f45ad..cf093da85 100755
--- a/etc/crontab.xml
+++ b/etc/crontab.xml
@@ -24,5 +24,8 @@
0 0 * * *
+
+ 0 0 * * *
+
diff --git a/etc/di.xml b/etc/di.xml
index e7fc019f4..8e9ceb09b 100755
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -1699,6 +1699,8 @@
+
@@ -1726,6 +1728,20 @@
+
+
+ Adyen\Payment\Model\ResourceModel\Notification
+
+
+
+
+
+ -
+ Adyen\Payment\Cron\Providers\ProcessedOldNotificationsProvider
+
+
+
+