diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php index f52886a14264d..2547974774953 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -7,30 +7,30 @@ namespace Magento\Directory\Model\Currency\Import; +use Magento\Store\Model\ScopeInterface; + class CurrencyConverterApi extends AbstractImport { /** * @var string */ - const CURRENCY_CONVERTER_URL = 'http://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}&compact=ultra'; //@codingStandardsIgnoreLine + const CURRENCY_CONVERTER_URL = 'https://free.currencyconverterapi.com/api/v6/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}' + . '&compact=ultra&apiKey={{ACCESS_KEY}}'; //@codingStandardsIgnoreLine /** * Http Client Factory - * * @var \Magento\Framework\HTTP\ZendClientFactory */ private $httpClientFactory; /** * Core scope config - * * @var \Magento\Framework\App\Config\ScopeConfigInterface */ private $scopeConfig; /** * Initialize dependencies - * * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory @@ -51,6 +51,14 @@ public function __construct( public function fetchRates() { $data = []; + $accessKey = $this->scopeConfig->getValue( + 'currency/currencyconverterapi/api_key', + ScopeInterface::SCOPE_STORE + ); + if (empty($accessKey)) { + $this->_messages[] = __('No API Key was specified or an invalid API Key was specified.'); + return $data; + } $currencies = $this->_getCurrencyCodes(); $defaultCurrencies = $this->_getDefaultCurrencyCodes(); @@ -58,7 +66,7 @@ public function fetchRates() if (!isset($data[$currencyFrom])) { $data[$currencyFrom] = []; } - $data = $this->convertBatch($data, $currencyFrom, $currencies); + $data = $this->convertBatch($data, $currencyFrom, $currencies, $accessKey); ksort($data[$currencyFrom]); } return $data; @@ -67,19 +75,31 @@ public function fetchRates() /** * Return currencies convert rates in batch mode * + * @param string $accessKey * @param array $data * @param string $currencyFrom * @param array $currenciesTo * @return array */ - private function convertBatch($data, $currencyFrom, $currenciesTo) + private function convertBatch($data, $currencyFrom, $currenciesTo, $accessKey) { foreach ($currenciesTo as $to) { set_time_limit(0); try { - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $to, $url); + $url = str_replace( + ['{{ACCESS_KEY}}', '{{CURRENCY_FROM}}', '{{CURRENCY_TO}}'], + [$accessKey, $currencyFrom, $to], + self::CURRENCY_CONVERTER_URL + ); + $response = $this->getServiceResponse($url); + + if (!$this->validateResponse($response, $currencyFrom, $to)) { + $data[$currencyFrom][$to] = null; + + return $data; + } + if ($currencyFrom == $to) { $data[$currencyFrom][$to] = $this->_numberFormat(1); } else { @@ -143,4 +163,27 @@ protected function _convert($currencyFrom, $currencyTo) { return 1; } + + /** + * Validates rates response. + * @param array $response + * @param string $currencyFrom + * @param string $currenciesTo + * @return bool + */ + private function validateResponse(array $response, string $currencyFrom, string $currenciesTo): bool + { + if (isset($response['error'])) { + $this->_messages[] = $response['error']; + + return false; + } + if (empty($response[$currencyFrom . '_' . $currenciesTo])) { + $this->_messages[] = __('We can\'t retrieve a rate for %1.', $currenciesTo); + + return false; + } + + return true; + } } diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php new file mode 100644 index 0000000000000..00e0a20eba844 --- /dev/null +++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php @@ -0,0 +1,124 @@ +currencyFactory = $this->getMockBuilder(CurrencyFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->model = new CurrencyConverterApi($this->currencyFactory, $this->scopeConfig, $this->httpClientFactory); + } + + /** + * Test Fetch Rates + * + * @return void + */ + public function testFetchRates(): void + { + $currencyFromList = ['USD']; + $currencyToList = ['EUR', 'UAH']; + $responseBody = '{"USD_EUR":0.879215}'; + $expectedRateList = ['USD' => ['EUR' => 0.879215, 'UAH' => null]]; + $message = "We can't retrieve a rate for UAH."; + + $this->scopeConfig->method('getValue') + ->withConsecutive( + ['currency/currencyconverterapi/api_key', 'store'], + ['currency/currencyconverterapi/timeout', 'store'] + ) + ->willReturnOnConsecutiveCalls('api_key', 100); + + /** @var Currency|MockObject $currency */ + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var ZendClient|MockObject $httpClient */ + $httpClient = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var DataObject|MockObject $currencyMock */ + $httpResponse = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->setMethods(['getBody']) + ->getMock(); + + $this->currencyFactory->method('create') + ->willReturn($currency); + $currency->method('getConfigBaseCurrencies') + ->willReturn($currencyFromList); + $currency->method('getConfigAllowCurrencies') + ->willReturn($currencyToList); + + $this->httpClientFactory->method('create') + ->willReturn($httpClient); + $httpClient->method('setUri') + ->willReturnSelf(); + $httpClient->method('setConfig') + ->willReturnSelf(); + $httpClient->method('request') + ->willReturn($httpResponse); + $httpResponse->method('getBody') + ->willReturn($responseBody); + + self::assertEquals($expectedRateList, $this->model->fetchRates()); + + $messages = $this->model->getMessages(); + self::assertNotEmpty($messages); + self::assertTrue(is_array($messages)); + self::assertEquals($message, (string)$messages[0]); + } +} diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index ec5fa35b6a152..eb3f2a0e9ef2b 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -47,7 +47,12 @@ - + + + currency/currencyconverterapi/api_key + Magento\Config\Model\Config\Backend\Encrypted + + diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index 276d7088cc2ea..c18c4f29d5822 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -24,6 +24,7 @@ 100 + 0