Question

Magento ver. 2.2.3

Seems today fixer.io stopped working. It was the only service that still works for currency rates. If I try to reach the url from web browser I get this

0   "#################################################################################################################################"
1   "#                                                                                                                               #"
2   "# IMPORTANT - PLEASE UPDATE YOUR API ENDPOINT                                                                                   #"
3   "#                                                                                                                               #"
4   "# This API endpoint is deprecated and has now been shut down. To keep using the Fixer API, please update your integration       #"
5   "# to use the new Fixer API endpoint, designed as a simple drop-in replacement.                                                  #"
6   "# You will be required to create an account at https://fixer.io and obtain an API access key.                                   #"
7   "#                                                                                                                               #"
8   "# For more information on how to upgrade please visit our Github Tutorial at: https://github.com/fixerAPI/fixer#readme          #"
9   "#                                                                                                                               #"
a   "#################################################################################################################################"

You can still get a free account, but code change needed to make it works with new API.

Does anybody know if a fix coming with the next upgrade of Magento?

Alternatively in Magento 1.9.3.8 I'm getting currency rates from here, by custom module.

Does one already exist that use this service?

If there are no answers to these questions, the only solution will be to write a new custom module.

I hope for an escape...

Was it helpful?

Solution

The Free Currency Converter API module:

app/code/MyCompany/FreeCurrencyConverter/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="MyCompany_FreeCurrencyConverter" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Directory"/>
        </sequence>
    </module>
</config>

app/code/MyCompany/FreeCurrencyConverter/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">
    <type name="Magento\Directory\Model\Currency\Import\Config">
        <arguments>
            <argument name="servicesConfig" xsi:type="array">
                <item name="fcc" xsi:type="array">
                    <item name="label" xsi:type="string">Free Currency Converter</item>
                    <item name="class" xsi:type="string">MyCompany\FreeCurrencyConverter\Model\Currency\Import\Fcc</item>
                </item>
            </argument>
        </arguments>
    </type>
</config>

app/code/MyCompany/FreeCurrencyConverter/etc/config.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <currency>
            <fcc>
                <timeout>100</timeout>
                <delay>1</delay>
            </fcc>
        </currency>
    </default>
</config>

app/code/MyCompany/FreeCurrencyConverter/etc/adminhtml/system.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="currency">
            <group id="fcc" translate="label" sortOrder="41" showInDefault="1" showInWebsite="0" showInStore="0">
                <label>Free Currency Converter</label>
                <field id="timeout" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Connection Timeout in Seconds</label>
                </field>
                <field id="delay" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Connection Delay Time in Seconds</label>
                </field>
            </group>
        </section>
    </system>
</config>

app/code/MyCompany/FreeCurrencyConverter/Model/Currency/Import/Fcc.php

<?php
namespace MyCompany\FreeCurrencyConverter\Model\Currency\Import;
class Fcc extends \Magento\Directory\Model\Currency\Import\AbstractImport
{
    /**
     * @var string
     */
    const CURRENCY_CONVERTER_URL = 'https://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}';
    /**
     * HTTP client
     *
     * @var \Magento\Framework\HTTP\ZendClient
     */
    protected $_httpClient;
    /**
     * Core store config
     *
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $_scopeConfig;
    /**
     * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     */
    protected $jsonHelper;

    public function __construct(
        \Magento\Directory\Model\CurrencyFactory $currencyFactory,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\Json\Helper\Data $jsonHelper
    ) {
        parent::__construct($currencyFactory);
        $this->_scopeConfig = $scopeConfig;
        $this->_httpClient = new \Magento\Framework\HTTP\ZendClient();
        $this->jsonHelper = $jsonHelper;
    }
    /**
     * @param string $currencyFrom
     * @param string $currencyTo
     * @param int $retry
     * @return float|null
     */
    protected function _convert($currencyFrom, $currencyTo, $retry = 0)
    {
        $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL);
        $url = str_replace('{{CURRENCY_TO}}', $currencyTo, $url);
        try {
            sleep($this->_scopeConfig->getValue(
                'currency/fcc/delay',
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
            ));
            $response = $this->_httpClient->setUri(
                $url
            )->setConfig(
                [
                    'timeout' => $this->_scopeConfig->getValue(
                        'currency/fcc/timeout',
                        \Magento\Store\Model\ScopeInterface::SCOPE_STORE
                    ),
                ]
            )->request(
                'GET'
            )->getBody();
            $resultKey = $currencyFrom.'_'.$currencyTo;
            $data = $this->jsonHelper->jsonDecode($response);
            $results = $data['results'][$resultKey];
            $queryCount = $data['query']['count'];
            if( !$queryCount &&  !isset($results)) {
                $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url);
                return null;
            }
            return (float)$results['val'];
        } catch (\Exception $e) {
            if ($retry == 0) {
                $this->_convert($currencyFrom, $currencyTo, 1);
            } else {
                $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url);
            }
        }
    }
}

enjoy!

EDIT:

as Bizzy Bee says:

Don't forget the following file to get it all going properly:

app/code/MyCompany/FreeCurrencyConverter/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'MyCompany_FreeCurrencyConverter',
    __DIR__
);
?>

OTHER TIPS

It seems we need to subscribe Basic $10/month plan because Fixer new free plan offers only Euro base currency. If you purchase $10/month plan, you can fix this issue editing:

[root]/vendor/magento/module-directory/Model/Currency/Import/FixerIo.php

line 16 replace

const CURRENCY_CONVERTER_URL = 'http://api.fixer.io/latest?base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}';

with

const CURRENCY_CONVERTER_URL = 'http://data.fixer.io/api/latest?access_key=1234yourkey5678&base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}';

It's better to wait some nice guy will create a new module with free converter API.

Thanks for the module code!

Don't forget the following file to get it all going properly:

app/code/MyCompany/FreeCurrencyConverter/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'MyCompany_FreeCurrencyConverter',
    __DIR__
);
?>

To extend on @lizenial's answer: Modifying core code is never a good idea, that's why you can override the original class, though (as far as I know) it requires overriding the entire class as it mostly consists of private methods, which we cannot modify using plugins.

app/code/[Vendor]/[Module]/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="[Vendor]\[Module]\Model\Currency\Import\FixFixerIo" />
</config>

app/code/[Vendor]/[Module]/etc/adminhtml/system.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="currency" translate="label" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1">
            <tab>general</tab>
            <group id="fixerio" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0">
                <field id="api_key" translate="label,comment" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Api key</label>
                    <comment><![CDATA[New updated API now requires an access key and a new URL.]]></comment>
                </field>
            </group>
        </section>
    </system>
</config>

app/code/[Vendor]/[Module]/Model/Currency/Import/FixFixerIo.php

<?php

namespace [Vendor]\[Module]\Model\Currency\Import;

use \Magento\Store\Model\ScopeInterface;

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

    const CURRENCY_CONVERTER_URL = 'http://data.fixer.io/api/latest?base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}&access_key='; // changed constant

    const API_KEY_CONFIG_PATH = 'currency/fixerio/api_key';

    public function __construct(
        \Magento\Directory\Model\CurrencyFactory $currencyFactory,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory
    ) {
        $this->scopeConfig = $scopeConfig;
        parent::__construct($currencyFactory, $scopeConfig, $httpClientFactory);
    }

    /**
     * @return array
     * @override
     * @see \Magento\Directory\Model\Currency\Import\FixerIo::fetchRates()
     */
    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;
    }

    /**
     * @param array $data
     * @param string $currencyFrom
     * @param array $currenciesTo
     * @return array
     * @override
     * @see \Magento\Directory\Model\Currency\Import\FixerIo::convertBatch()
     */
    private function convertBatch($data, $currencyFrom, $currenciesTo)
    {
        $currenciesStr = implode(',', $currenciesTo);
        $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL);
        $url = str_replace('{{CURRENCY_TO}}', $currenciesStr, $url);

        set_time_limit(0);
        try {
            $response = $this->getServiceResponse($url);
        } finally {
            ini_restore('max_execution_time');
        }

        foreach ($currenciesTo as $currencyTo) {
            if ($currencyFrom == $currencyTo) {
                $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1);
            } else {
                if (empty($response['rates'][$currencyTo])) {
                    $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $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);
        $url .= $accessKey;
        /** @var \Magento\Framework\HTTP\ZendClient $httpClient */
        $httpClient = $this->httpClientFactory->create();
        $response = [];

        try {
            $jsonResponse = $httpClient->setUri(
                $url
            )->setConfig(
                [
                    'timeout' => $this->scopeConfig->getValue(
                        'currency/fixerio/timeout',
                        \Magento\Store\Model\ScopeInterface::SCOPE_STORE
                    ),
                ]
            )->request(
                'GET'
            )->getBody();

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

}

I know this (probably) isn't the cleanest solution as it overwrites pretty much the entire class but if any of you have a cleaner solution feel free to comment :)

I tried the solution of updating the fixerIo.php file and for the sake of testing I even changed the store base currency to EUR, however, I get the following error after I have amended the code:

Fatal error: Namespace declaration statement has to be the very first statement or after any declare call in the script in /public_html/vendor/magento/module-directory/Model/Currency/Import/FixerIo.php on line 6

Are there any other changes needed besides for those in the fixerio.php file?

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top