diff --git a/Controller/Process/Json.php b/Controller/Process/Json.php index d0f6aa59e..afb9c9091 100755 --- a/Controller/Process/Json.php +++ b/Controller/Process/Json.php @@ -23,6 +23,7 @@ namespace Adyen\Payment\Controller\Process; +use Adyen\Util\HmacSignature; use Symfony\Component\Config\Definition\Exception\Exception; use Magento\Framework\App\Request\Http as Http; @@ -56,6 +57,21 @@ class Json extends \Magento\Framework\App\Action\Action */ private $serializer; + /** + * @var \Adyen\Payment\Helper\Config + */ + protected $configHelper; + + /** + * @var \Adyen\Payment\Helper\IpAddress + */ + protected $ipAddressHelper; + + /** + * @var HmacSignature + */ + private $hmacSignature; + /** * Json constructor. * @@ -63,12 +79,18 @@ class Json extends \Magento\Framework\App\Action\Action * @param \Adyen\Payment\Helper\Data $adyenHelper * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger * @param \Magento\Framework\Serialize\SerializerInterface $serializer + * @param \Adyen\Payment\Helper\Config $configHelper + * @param \Adyen\Payment\Helper\IpAddress $ipAddressHelper + * @param HmacSignature $hmacSignature */ public function __construct( \Magento\Framework\App\Action\Context $context, \Adyen\Payment\Helper\Data $adyenHelper, \Adyen\Payment\Logger\AdyenLogger $adyenLogger, - \Magento\Framework\Serialize\SerializerInterface $serializer + \Magento\Framework\Serialize\SerializerInterface $serializer, + \Adyen\Payment\Helper\Config $configHelper, + \Adyen\Payment\Helper\IpAddress $ipAddressHelper, + HmacSignature $hmacSignature ) { parent::__construct($context); $this->_objectManager = $context->getObjectManager(); @@ -76,6 +98,9 @@ public function __construct( $this->_adyenHelper = $adyenHelper; $this->_adyenLogger = $adyenLogger; $this->serializer = $serializer; + $this->configHelper = $configHelper; + $this->ipAddressHelper = $ipAddressHelper; + $this->hmacSignature = $hmacSignature; // Fix for Magento2.3 adding isAjax to the request params if (interface_exists(\Magento\Framework\App\CsrfAwareActionInterface::class)) { @@ -180,6 +205,25 @@ protected function _validateNotificationMode($notificationMode) */ protected function _processNotification($response, $notificationMode) { + if ($this->configHelper->getNotificationsIpHmacCheck()) { + //Validate if the notification comes from a verified IP + if (!$this->isIpValid()) { + $this->_adyenLogger->addAdyenNotification( + "Notification has been rejected because the IP address could not be verified" + ); + return false; + } + + if ($this->hmacSignature->isHmacSupportedEventCode($response)) { + //Validate the Hmac calculation + if (!$this->hmacSignature->isValidNotificationHMAC($this->configHelper->getNotificationsHmacKey(), + $response)) { + $this->_adyenLogger->addAdyenNotification('HMAC key validation failed ' . print_r($response, 1)); + return false; + } + } + } + // validate the notification if ($this->authorised($response)) { // log the notification @@ -271,18 +315,18 @@ protected function authorised($response) } // validate username and password - if ((!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']))) { + if ((!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))) { if ($this->_isTestNotification($response['pspReference'])) { $this->_returnResult( - 'Authentication failed: PHP_AUTH_USER and PHP_AUTH_PW are empty. See Adyen Magento manual CGI mode' + 'Authentication failed: PHP_AUTH_USER or PHP_AUTH_PW are empty. See Adyen Magento manual CGI mode' ); } return false; } - $usernameCmp = strcmp($_SERVER['PHP_AUTH_USER'], $username); - $passwordCmp = strcmp($_SERVER['PHP_AUTH_PW'], $password); - if ($usernameCmp === 0 && $passwordCmp === 0) { + $usernameIsValid = hash_equals($username, $_SERVER['PHP_AUTH_USER']); + $passwordIsValid = hash_equals($password, $_SERVER['PHP_AUTH_PW']); + if ($usernameIsValid && $passwordIsValid) { return true; } @@ -295,6 +339,29 @@ protected function authorised($response) return false; } + /** + * Checks if any of the possible remote IP address sending the notification is verified and returns the validation result + * + * @return bool + */ + protected function isIpValid() + { + $ipAddress = []; + + //Getting remote and possibly forwarded IP addresses + if (!empty($_SERVER['REMOTE_ADDR'])) { + array_push($ipAddress, $_SERVER['REMOTE_ADDR']); + } + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + array_push($ipAddress, $_SERVER['HTTP_X_FORWARDED_FOR']); + } + if (!empty($_SERVER['HTTP_CLIENT_IP'])) { + array_push($ipAddress, $_SERVER['HTTP_CLIENT_IP']); + } + + return $this->ipAddressHelper->isIpAddressValid($ipAddress); + } + /** * If notification is already saved ignore it * diff --git a/Controller/Process/ResultPos.php b/Controller/Process/ResultPos.php deleted file mode 100644 index 258671700..000000000 --- a/Controller/Process/ResultPos.php +++ /dev/null @@ -1,283 +0,0 @@ - - */ - -namespace Adyen\Payment\Controller\Process; - -class ResultPos extends \Magento\Framework\App\Action\Action -{ - /** - * @var \Adyen\Payment\Helper\Data - */ - protected $_adyenHelper; - - /** - * @var \Magento\Sales\Model\OrderFactory - */ - protected $_orderFactory; - - /** - * @var \Magento\Sales\Model\Order - */ - protected $_order; - - /** - * @var \Magento\Sales\Model\Order\Status\HistoryFactory - */ - protected $_orderHistoryFactory; - - /** - * @var \Magento\Checkout\Model\Session - */ - protected $_session; - - /** - * @var \Adyen\Payment\Logger\AdyenLogger - */ - protected $_adyenLogger; - - /** - * ResultPos constructor. - * - * @param \Magento\Framework\App\Action\Context $context - * @param \Adyen\Payment\Helper\Data $adyenHelper - * @param \Magento\Sales\Model\OrderFactory $orderFactory - * @param \Magento\Sales\Model\Order\Status\HistoryFactory $orderHistoryFactory - * @param \Magento\Checkout\Model\Session $session - * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger - */ - public function __construct( - \Magento\Framework\App\Action\Context $context, - \Adyen\Payment\Helper\Data $adyenHelper, - \Magento\Sales\Model\OrderFactory $orderFactory, - \Magento\Sales\Model\Order\Status\HistoryFactory $orderHistoryFactory, - \Magento\Checkout\Model\Session $session, - \Adyen\Payment\Logger\AdyenLogger $adyenLogger - ) { - $this->_adyenHelper = $adyenHelper; - $this->_orderFactory = $orderFactory; - $this->_orderHistoryFactory = $orderHistoryFactory; - $this->_session = $session; - $this->_adyenLogger = $adyenLogger; - parent::__construct($context); - } - - /** - * Return result - */ - public function execute() - { - $response = $this->getRequest()->getParams(); - $this->_adyenLogger->addAdyenResult(print_r($response, true)); - - $result = $this->_validateResponse($response); - - if ($result) { - $session = $this->_session; - $session->getQuote()->setIsActive(false)->save(); - $this->_redirect('checkout/onepage/success', ['_query' => ['utm_nooverride' => '1']]); - } else { - $this->_cancel($response); - $this->_redirect($this->_adyenHelper->getAdyenAbstractConfigData('return_path')); - } - } - - /** - * @param $response - * @return bool - */ - private function _validateResponse($response) - { - $result = false; - - if ($response != null && $response['result'] != "" && $this->_validateChecksum($response)) { - $incrementId = $response['merchantReference']; - $responseResult = $response['result']; - - if ($incrementId) { - $order = $this->_getOrder($incrementId); - if ($order->getId()) { - $comment = __( - '%1
Result: %2
paymentMethod: %3', - 'Adyen App Result URL Notification:', - $responseResult, - 'POS' - ); - - if ($responseResult == 'APPROVED') { - $this->_adyenLogger->addAdyenResult('Result is approved'); - - $history = $this->_orderHistoryFactory->create() - //->setStatus($status) - ->setComment($comment) - ->setEntityName('order') - ->setOrder($order); - $history->save(); - - // needed becuase then we need to save $order objects - $order->setAdyenResulturlEventCode("POS_APPROVED"); - - // save order - $order->save(); - - return true; - } else { - $this->_adyenLogger->addAdyenResult('Result is:' . $responseResult); - - $history = $this->_orderHistoryFactory->create() - //->setStatus($status) - ->setComment($comment) - ->setEntityName('order') - ->setOrder($order); - $history->save(); - - // cancel the order - if ($order->canCancel()) { - $order->cancel()->save(); - $this->_adyenLogger->addAdyenResult('Order is cancelled'); - } else { - $this->_adyenLogger->addAdyenResult('Order can not be cancelled'); - } - } - } else { - $this->_adyenLogger->addAdyenResult('Order does not exists with increment_id: ' . $incrementId); - } - } else { - $this->_adyenLogger->addAdyenResult('Empty merchantReference'); - } - } else { - $this->_adyenLogger->addAdyenResult('actionName or checksum failed or response is empty'); - } - return $result; - } - - /** - * Validate checksum from result parameters - * - * @param $response - * @return bool - */ - protected function _validateChecksum($response) - { - $checksum = $response['cs']; - $result = $response['result']; - $amount = $response['originalCustomAmount']; - $currency = $response['originalCustomCurrency']; - $sessionId = $response['sessionId']; - - // for android sessionis is with low i - if ($sessionId == "") { - $sessionId = $response['sessionid']; - } - - // calculate amount checksum - $amountChecksum = 0; - - $amountLength = strlen($amount); - for ($i = 0; $i < $amountLength; $i++) { - // ASCII value use ord - $checksumCalc = ord($amount[$i]) - 48; - $amountChecksum += $checksumCalc; - } - - $currencyChecksum = 0; - $currencyLength = strlen($currency); - for ($i = 0; $i < $currencyLength; $i++) { - $checksumCalc = ord($currency[$i]) - 64; - $currencyChecksum += $checksumCalc; - } - - $resultChecksum = 0; - $resultLength = strlen($result); - for ($i = 0; $i < $resultLength; $i++) { - $checksumCalc = ord($result[$i]) - 64; - $resultChecksum += $checksumCalc; - } - - $sessionIdChecksum = 0; - $sessionIdLength = strlen($sessionId); - for ($i = 0; $i < $sessionIdLength; $i++) { - $checksumCalc = $this->_getAscii2Int($sessionId[$i]); - $sessionIdChecksum += $checksumCalc; - } - - $totalResultChecksum = (($amountChecksum + $currencyChecksum + $resultChecksum) * $sessionIdChecksum) % 100; - - // check if request is valid - if ($totalResultChecksum == $checksum) { - $this->_adyenLogger->addAdyenResult('Checksum is valid'); - return true; - } - $this->_adyenLogger->addAdyenResult('Checksum is invalid!'); - return false; - } - - /** - * @param $ascii - * @return int - */ - protected function _getAscii2Int($ascii) - { - if (is_numeric($ascii)) { - $int = ord($ascii) - 48; - } else { - $int = ord($ascii) - 64; - } - return $int; - } - - /** - * @param $incrementId - * @return \Magento\Sales\Model\Order - */ - protected function _getOrder($incrementId) - { - if (!$this->_order) { - $this->_order = $this->_orderFactory->create()->loadByIncrementId($incrementId); - } - return $this->_order; - } - - /** - * @param $response - */ - protected function _cancel($response) - { - $session = $this->_session; - - // restore the quote - $session->restoreQuote(); - - $order = $this->_order; - - if ($order) { - $this->_adyenHelper->cancelOrder($order); - - if (isset($response['authResult']) && - $response['authResult'] == \Adyen\Payment\Model\Notification::CANCELLED) { - $this->messageManager->addError(__('You have cancelled the order. Please try again')); - } else { - $this->messageManager->addError(__('Your payment failed, Please try again later')); - } - } - } -} diff --git a/Cron/ServerIpAddress.php b/Cron/ServerIpAddress.php new file mode 100644 index 000000000..de545b10a --- /dev/null +++ b/Cron/ServerIpAddress.php @@ -0,0 +1,65 @@ + + */ + +namespace Adyen\Payment\Cron; + +use Adyen\Payment\Helper\IpAddress; +use Adyen\Payment\Logger\AdyenLogger; + +class ServerIpAddress +{ + + /** + * @var IpAddress $ipAddressHelper + */ + protected $ipAddressHelper; + + /** + * @var AdyenLogger $adyenLogger + */ + protected $adyenLogger; + + /** + * ServerIpAddress constructor. + * @param IpAddress $ipAddressHelper + * @param AdyenLogger $adyenLogger + */ + public function __construct( + IpAddress $ipAddressHelper, + AdyenLogger $adyenLogger + ) { + $this->ipAddressHelper = $ipAddressHelper; + $this->adyenLogger = $adyenLogger; + } + + public function execute() + { + //Check if there are already verified IP addresses in cache and refresh when empty + if (empty($this->ipAddressHelper->getIpAddressesFromCache())) { + $this->adyenLogger->addAdyenNotificationCronjob( + 'There are no verified Adyen IP addresses in cache. Updating IP records.' + ); + $this->ipAddressHelper->updateCachedIpAddresses(); + } + } +} diff --git a/Helper/Config.php b/Helper/Config.php index 38a71b55d..d57a6d2fd 100644 --- a/Helper/Config.php +++ b/Helper/Config.php @@ -24,27 +24,47 @@ namespace Adyen\Payment\Helper; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Encryption\EncryptorInterface; class Config { const XML_PAYMENT_PREFIX = "payment"; const XML_ADYEN_ABSTRACT_PREFIX = "adyen_abstract"; const XML_NOTIFICATIONS_CAN_CANCEL_FIELD = "notifications_can_cancel"; + const XML_NOTIFICATIONS_IP_HMAC_CHECK = "notifications_ip_hmac_check"; + const XML_NOTIFICATIONS_HMAC_KEY_LIVE = "notification_hmac_key_live"; + const XML_NOTIFICATIONS_HMAC_KEY_TEST = "notification_hmac_key_test"; /** * @var Magento\Framework\App\Config\ScopeConfigInterface */ protected $scopeConfig; + /** + * @var EncryptorInterface + */ + private $encryptor; + + /** + * @var \Adyen\Payment\Helper\Data + */ + private $adyenHelper; + /** * Config constructor. * * @param Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param EncryptorInterface $encryptor + * @param \Adyen\Payment\Helper\Data $adyenHelper */ public function __construct( - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + EncryptorInterface $encryptor, + \Adyen\Payment\Helper\Data $adyenHelper ) { $this->scopeConfig = $scopeConfig; + $this->encryptor = $encryptor; + $this->adyenHelper = $adyenHelper; } /** @@ -63,6 +83,48 @@ public function getNotificationsCanCancel($storeId = null) ); } + /** + * Retrieve flag for notifications_ip_hmac_check + * + * @param int $storeId + * @return bool + */ + public function getNotificationsIpHmacCheck($storeId = null) + { + return (bool)$this->getConfigData( + self::XML_NOTIFICATIONS_IP_HMAC_CHECK, + self::XML_ADYEN_ABSTRACT_PREFIX, + $storeId, + true + ); + } + + /** + * Retrieve key for notifications_hmac_key + * + * @param int $storeId + * @return string + */ + public function getNotificationsHmacKey($storeId = null) + { + if ($this->adyenHelper->isDemoMode($storeId)) { + $key = $this->getConfigData( + self::XML_NOTIFICATIONS_HMAC_KEY_TEST, + self::XML_ADYEN_ABSTRACT_PREFIX, + $storeId, + false + ); + } else { + $key = $this->getConfigData( + self::XML_NOTIFICATIONS_HMAC_KEY_LIVE, + self::XML_ADYEN_ABSTRACT_PREFIX, + $storeId, + false + ); + } + return $this->encryptor->decrypt(trim($key)); + } + /** * Retrieve information from payment configuration * diff --git a/Helper/IpAddress.php b/Helper/IpAddress.php new file mode 100644 index 000000000..0ec8500c3 --- /dev/null +++ b/Helper/IpAddress.php @@ -0,0 +1,148 @@ + + */ + +namespace Adyen\Payment\Helper; + +use Adyen\Util\IpAddress as IpAddressUtil; +use Adyen\Payment\Logger\AdyenLogger; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Serialize\SerializerInterface; + +/** + * Class IpAddress + * @package Adyen\Payment\Helper + */ +class IpAddress +{ + + const IP_ADDRESS_CACHE_ID = "Adyen_ip_address"; + const IP_ADDRESS_CACHE_LIFETIME = 86400; + + /** + * @var IpAddressUtil + */ + private $ipAddressUtil; + + /** + * @var CacheInterface + */ + private $cache; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var AdyenLogger $adyenLogger + */ + protected $adyenLogger; + + /** + * IpAddress constructor. + * + * @param IpAddressUtil $ipAddressUtil + * @param CacheInterface $cache + * @param SerializerInterface $serializer + * @param AdyenLogger $adyenLogger + */ + public function __construct( + IpAddressUtil $ipAddressUtil, + CacheInterface $cache, + SerializerInterface $serializer, + AdyenLogger $adyenLogger + ) { + $this->ipAddressUtil = $ipAddressUtil; + $this->cache = $cache; + $this->serializer = $serializer; + $this->adyenLogger = $adyenLogger; + } + + /** + * Checks if the provided array of IPs addresses has been validated + * + * @param string[] $ipAddresses + * @return bool + */ + public function isIpAddressValid($ipAddresses) + { + if (empty($ipAddresses)) { + return false; + } + + $cachedIpsArray = $this->getIpAddressesFromCache(); + + if (empty($cachedIpsArray)) { + $this->adyenLogger->addAdyenDebug( + 'There are no verified Adyen IP addresses in cache. Updating IP records.' + ); + $this->updateCachedIpAddresses(); + } + + foreach ($ipAddresses as $ipAddress) { + //If the IP is already cached return true + if (in_array($ipAddress, $cachedIpsArray)) { + return true; + } + } + + return false; + } + + /** + * Updates cache key containing Adyen webhook IP addresses with newly resolved records + */ + public function updateCachedIpAddresses() + { + $this->saveIpAddressesToCache($this->ipAddressUtil->getAdyenIpAddresses()); + } + + /** + * Saves array of IP addresses in cache key + * + * @param string[] $ipAddresses + */ + public function saveIpAddressesToCache($ipAddresses) + { + $this->cache->save( + $this->serializer->serialize($ipAddresses), + self::IP_ADDRESS_CACHE_ID, + [], + self::IP_ADDRESS_CACHE_LIFETIME + ); + } + + /** + * Loads value of IP addresses cache key and returns it as array + * + * @return array|bool|float|int|string|null + */ + public function getIpAddressesFromCache() + { + $serializedIpAddresses = $this->cache->load(self::IP_ADDRESS_CACHE_ID); + if (!empty($serializedIpAddresses)) { + return $this->serializer->unserialize($serializedIpAddresses); + } + return []; + } +} diff --git a/README.md b/README.md index 8553e0493..fab14c71f 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,23 @@ For more information see our [installation section](https://docs.adyen.com/devel ## Setup Cron -Make sure that your Magento cron is running every minute. We are using a cronjob to process the notifications, our webhook service. The cronjob will be executed every minute. It only executes the notifications that have been received at least 2 minutes ago. This is to ensure that Magento has created the order, and all save after events are executed. A handy tool to get insight into your cronjobs is AOE scheduler. You can download this tool through Magento Connect or GitHub. -If you need to setup your cronjob in Magento this is described here +Make sure that your Magento cron is running every minute. We are using a cronjob to process the notifications (our webhook service) and to update Adyen servers' IP addresses. The cronjobs will be executed every minute. -We have defined this: ``` */1 * * * * + + */1 * * * * + ``` +The notification processing service queries the records that have been received at least 2 minutes ago. This is to ensure that Magento has created the order, and all save after events are executed. A handy tool to get insight into your cronjobs is AOE scheduler. You can download this tool through Magento Connect or GitHub. + +If you need to setup your cronjob in Magento this is described here + ## Support If you have a feature request, or spotted a bug or a technical problem, create a GitHub issue. For other questions, contact our [support team](https://support.adyen.com/hc/en-us/requests/new?ticket_form_id=360000705420). diff --git a/Test/Unit/Helper/IpAddressTest.php b/Test/Unit/Helper/IpAddressTest.php new file mode 100755 index 000000000..6012fdd46 --- /dev/null +++ b/Test/Unit/Helper/IpAddressTest.php @@ -0,0 +1,110 @@ + + */ + +namespace Adyen\Payment\Tests\Helper; + +class IpAddressTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Adyen\Payment\Helper\IpAddress + */ + private $ipAddressHelper; + + private function getSimpleMock($originalClassName) + { + return $this->getMockBuilder($originalClassName) + ->disableOriginalConstructor() + ->getMock(); + } + + protected function setUp() + { + $cache = $this->getSimpleMock(\Magento\Framework\App\CacheInterface::class); + $cache->method('load')->willReturn( + array( + '1.2.3.4', + '20.20.20.20' + ) + ); + $serializer = $this->getSimpleMock(\Magento\Framework\Serialize\SerializerInterface::class); + $serializer->method('unserialize')->willReturnArgument(0); + $ipAddressUtil = $this->getSimpleMock(\Adyen\Util\IpAddress::class); + $adyenLogger = $this->getSimpleMock(\Adyen\Payment\Logger\AdyenLogger::class); + + $this->ipAddressHelper = new \Adyen\Payment\Helper\IpAddress( + $ipAddressUtil, + $cache, + $serializer, + $adyenLogger + ); + } + + /** + * @dataProvider ipAddressesProvider + */ + public function testIsIpAddressValid($ipAddress, $expectedResult) + { + $this->assertEquals($expectedResult, $this->ipAddressHelper->isIpAddressValid([$ipAddress])); + } + + public function testUpdateCachedIpAddresses() + { + $this->assertNull($this->ipAddressHelper->updateCachedIpAddresses()); + } + + public function testSaveIpAddressesToCache() + { + $this->assertNull($this->ipAddressHelper->saveIpAddressesToCache([])); + } + + public function testGetIpAddressesFromCache() + { + $this->assertTrue(is_array($this->ipAddressHelper->getIpAddressesFromCache())); + } + + public static function ipAddressesProvider() + { + return array( + array( + '1.2.3.4', + true + ), + array( + '20.20.20.20', + true + ), + array( + '8.8.8.8', + false + ), + array( + '192.168.100.10', + false + ), + array( + '500.168.100.10', + false + ) + ); + } +} diff --git a/composer.json b/composer.json index 0cad70253..424241e6d 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "adyen/php-api-library": "~6", + "adyen/php-api-library": "^6.3", "magento/framework": ">=101.0.8 <102 || >=102.0.1", "magento/module-vault": "101.*", "magento/module-paypal": ">=100.2.6" diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 44f30e9b4..56f094b75 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -39,6 +39,7 @@ + diff --git a/etc/adminhtml/system/adyen_security.xml b/etc/adminhtml/system/adyen_security.xml new file mode 100644 index 000000000..71939c824 --- /dev/null +++ b/etc/adminhtml/system/adyen_security.xml @@ -0,0 +1,73 @@ + + + + + + Magento\Config\Block\System\Config\Form\Fieldset + + + Enabling these features is strongly recommended to improve the security of your integration. +

+ ]]> +
+ + + Magento\Config\Model\Config\Source\Yesno + payment/adyen_abstract/notifications_ip_hmac_check + + Adyen documentation. + ]]> + + + + + Magento\Config\Model\Config\Backend\Encrypted + payment/adyen_abstract/notification_hmac_key_test + + Adyen documentation. + ]]> + + + + + Magento\Config\Model\Config\Backend\Encrypted + payment/adyen_abstract/notification_hmac_key_live + + Adyen documentation. + ]]> + + +
+
diff --git a/etc/crontab.xml b/etc/crontab.xml index 9b018080b..0e0958dc9 100755 --- a/etc/crontab.xml +++ b/etc/crontab.xml @@ -27,5 +27,8 @@ */1 * * * * + + */1 * * * * + - \ No newline at end of file + diff --git a/etc/di.xml b/etc/di.xml index 4fc779901..1e3ea0c4f 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -1057,6 +1057,7 @@ 1 1 1 + 1 1 @@ -1075,6 +1076,8 @@ 1 1 1 + 1 + 1