1

I'm having a problem with the Magento 2 currency rate. I have a question is it also possible to automate Magento 2.3.4 currencies, for example, JPY, HKD and USD? can you show me the process of how to do it Programmatically or any extension?

Thanks

2 Answers 2

1

I think you want to auto update currencies rates in your Magento installation.

Magento has inbuilt functionality to update currency rates.

To configure you need to get API key from either fixer.io or Currency Converter API

After getting API key you need to update in configuration at

Update API key at Stores >> Configuration >> Currency Setup >> (fixer.io/Currency Converter API)

finally enable Scheduled Import Settings with service for which you added API key

enter image description here

Note: You only need one API key and can choose any one

17
  • Yes, I did this for Fixer.io, but every time error came up. "No API Key was specified, or an invalid API Key was specified." and Currency converter API is only for 2 currencies. I want 3 currencies.
    – Galib
    Commented Jan 16, 2023 at 7:04
  • try to create new API for fixer.io and update configuration.
    – Pawan
    Commented Jan 16, 2023 at 7:16
  • Still getting the same error; I tested the key in postman is working, but when I put the key in Magento inbuilt feature and tried to update currency rates using the import button then, an error came up
    – Galib
    Commented Jan 16, 2023 at 7:29
  • Magento version ?
    – Pawan
    Commented Jan 16, 2023 at 8:47
  • 1
    It seems there is an issue or update in API end point..will create a issue if anyone has idea.
    – Pawan
    Commented Jan 16, 2023 at 14:52
0

The api for Fixer.io has changed from what Magento has integrated. First of all the url has changed, and secondly the api key must be sent in as a custom header with the key "apikey", not as part of the url. To make these changes in Magento requires a custom module to override the Magento Fixer class.

First of all, the standard files for creating and registering a module:

Dev\FixerIo\registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'Dev_FixerIo',
   __DIR__
);

Dev\FixerIo\etc\module.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Dev_FixerIo" setup_version="1.0.1"></module>
</config>

Next, we tell Magento to load out FixerIo instead of their own FixerIo:

Dev\FixerIo\etc\di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    
    <preference for="Magento\Directory\Model\Currency\Import\FixerIo" 
                type="Dev\FixerIo\Model\Currency\Import\FixerIo"
    />
    
</config>

And finally, we copy/paste the existing Magento code and make our changes. We have to copy/paste the entire file because Magento have used private scope on a lot of functions / properties instead of protected and we require access to them, so...

The key differences here are line 11 (const CURRENCY_CONVERTER_URL is new api endpoint) and lines 129-132, where we set the headers with our api key.

Dev\FixerIo\Model\Currency\Import\FixerIo.php

<?php

namespace Dev\FixerIo\Model\Currency\Import;

use \Magento\Store\Model\ScopeInterface;

class FixerIo extends \Magento\Directory\Model\Currency\Import\FixerIo
{
    private $scopeConfig;

    const CURRENCY_CONVERTER_URL = 'https://api.apilayer.com/fixer/latest?symbols{{CURRENCY_TO}}&base={{CURRENCY_FROM}}'; // changed constant
    
    const API_KEY_CONFIG_PATH = 'currency/fixerio/api_key';
    
    /**
     * Initialize dependencies
     *
     * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory
     */
    public function __construct(
        \Magento\Directory\Model\CurrencyFactory $currencyFactory,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory
    ) {
        parent::__construct($currencyFactory, $scopeConfig, $httpClientFactory);
        $this->scopeConfig = $scopeConfig;
    }

    public function fetchRates()
    {
        $data = [];
        $currencies = $this->_getCurrencyCodes();
        $defaultCurrencies = $this->_getDefaultCurrencyCodes();

        foreach ($defaultCurrencies as $currencyFrom) {
            if (!isset($data[$currencyFrom])) {
                $data[$currencyFrom] = [];
            }
            $data = $this->convertBatch($data, $currencyFrom, $currencies);
            ksort($data[$currencyFrom]);
        }
        return $data;
    }

    /**
     * Return currencies convert rates in batch mode
     *
     * @param array $data
     * @param string $currencyFrom
     * @param array $currenciesTo
     * @return array
     */
    private function convertBatch(array $data, string $currencyFrom, array $currenciesTo): array
    {
        $accessKey = $this->scopeConfig->getValue(self::API_KEY_CONFIG_PATH, ScopeInterface::SCOPE_STORE);
        
        if (empty($accessKey)) {
            $this->_messages[] = __('No API Key was specified or an invalid API Key was specified.');
            $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo);
            return $data;
        }

        $currenciesStr = implode(',', $currenciesTo);
        $url = str_replace(
            ['{{ACCESS_KEY}}', '{{CURRENCY_FROM}}', '{{CURRENCY_TO}}'],
            [$accessKey, $currencyFrom, $currenciesStr],
            self::CURRENCY_CONVERTER_URL
        );
        // phpcs:ignore Magento2.Functions.DiscouragedFunction
        set_time_limit(0);
        try {
            $response = $this->getServiceResponse($url);
        } finally {
            ini_restore('max_execution_time');
        }

        if (!$this->validateResponse($response, $currencyFrom)) {
            $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo);
            return $data;
        }

        foreach ($currenciesTo as $currencyTo) {
            if ($currencyFrom == $currencyTo) {
                $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1);
            } else {
                if (empty($response['rates'][$currencyTo])) {
                    $serviceHost =  $this->getServiceHost($url);
                    $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $serviceHost, $currencyTo);
                    $data[$currencyFrom][$currencyTo] = null;
                } else {
                    $data[$currencyFrom][$currencyTo] = $this->_numberFormat(
                        (double)$response['rates'][$currencyTo]
                    );
                }
            }
        }
        return $data;
    }

    /**
     * @param string $url
     * @param int $retry
     * @return array|mixed
     * @override
     * @see \Magento\Directory\Model\Currency\Import\FixerIo::convertBatch()
     */
    private function getServiceResponse($url, $retry = 0)
    {
        $accessKey = $this->scopeConfig->getValue(self::API_KEY_CONFIG_PATH, ScopeInterface::SCOPE_STORE);
        
        
        /** @var \Magento\Framework\HTTP\ZendClient $httpClient */
        $httpClient = $this->httpClientFactory->create();
        $response = [];
        
        try {
            
            $jsonResponse = $httpClient->setUri($url)
                ->setConfig(
                    [
                        'timeout' => $this->scopeConfig->getValue(
                            'currency/fixerio/timeout',
                            ScopeInterface::SCOPE_STORE
                        ),
                    ]
                )
                ->setHeaders([
                    'Content-Type' => 'text/plain',
                    'apikey' => $accessKey,
                ])
                ->request('GET')
                ->getBody();

            $response = json_decode($jsonResponse, true);
            
        } catch (\Exception $e) {
            if ($retry == 0) {
                $response = $this->getServiceResponse($url, 1);
            }
        }
        return $response;
    }

    /**
     * Validates rates response.
     *
     * @param array $response
     * @param string $baseCurrency
     * @return bool
     */
    private function validateResponse(array $response, string $baseCurrency): bool
    {
        if ($response['success']) {
            return true;
        }

        $errorCodes = [
            101 => __('No API Key was specified or an invalid API Key was specified.'),
            102 => __('The account this API request is coming from is inactive.'),
            105 => __('The "%1" is not allowed as base currency for your subscription plan.', $baseCurrency),
            201 => __('An invalid base currency has been entered.'),
        ];

        $this->_messages[] = $errorCodes[$response['error']['code']] ?? __('Currency rates can\'t be retrieved.');

        return false;
    }

    /**
     * Creates array for provided currencies with empty rates.
     *
     * @param array $currenciesTo
     * @return array
     */
    private function makeEmptyResponse(array $currenciesTo): array
    {
        return array_fill_keys($currenciesTo, null);
    }

    /**
     * Get currency converter service host.
     *
     * @param string $url
     * @return string
     */
    private function getServiceHost(string $url): string
    {
        if (!$this->currencyConverterServiceHost) {
            // phpcs:ignore Magento2.Functions.DiscouragedFunction
            $this->currencyConverterServiceHost = parse_url($url, PHP_URL_SCHEME) . '://'
                // phpcs:ignore Magento2.Functions.DiscouragedFunction
                . parse_url($url, PHP_URL_HOST);
        }
        return $this->currencyConverterServiceHost;
    }

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.