5

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...

  • I guess this will need to be changed to get it to work: github.com/magento/magento2/blob/2.2-develop/app/code/Magento/… For the API_KEY the scope config seems to be already there so shouldn't be too tricky to add a new field in the config, only question is where should this be added in. – oirad Jun 5 '18 at 3:54
  • This is the step to write e new module. In this case i prefer to use The Free Currency Converter API service. At list it doesn't need any subscription. – krybbio Jun 5 '18 at 4:18
11

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__
);
?>
  • Hello Patal, can you offer download link for complete modul? – Matthias Jaekle Jun 6 '18 at 6:40
  • @MatthiasJaekle sorry, there is no way to upload this module here as far as i can see, but you got all files needed in this post. You should only add registration.php. – krybbio Jun 6 '18 at 10:18
  • @krybbio you could use github, bitbucket, or gitlab with a public repo – Rian Jun 6 '18 at 16:26
  • @Rian feel free to do it. ;-) – krybbio Jun 6 '18 at 19:55
  • 2
    I've added this code to github - github.com/flancer32/mage2_ext_free_rates Thanks to @krybbio and all other guys. – Alex Gusev Jun 15 '18 at 9:05
3

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.

  • i am a nice guy – krybbio Jun 5 '18 at 10:34
  • Wow! That's quick! – lizenial Jun 6 '18 at 2:27
  • @krybbio Yes, your module works perfectly. Thanks a lot! – lizenial Jun 7 '18 at 1:04
  • Free Fixer.IO account is working fine, if your base currency is EUR. Converting to lot of different currencies is possible for free. 1000 API request per month. – Matthias Jaekle Jun 13 '18 at 8:34
2

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__
);
?>
1

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 :)

0

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?

  • I think this should not be the answer. If you have some query, you can comment on the post or ask your own question by clicking on Ask Question button. – Mohit Kumar Arora Jul 18 '18 at 13:07
  • 1
    You are right. I'm new to StackExchange, therefore, I cannot comment... Start a new thread is probably also not the right way forward... It's a bit of a chicken and egg situation here. – ShragiL Jul 18 '18 at 13:11
  • I think you have made some mistakes in editing the file. Anyway, modify core file is always wrong. Use one of the two modules in the discussion instead. – krybbio Jul 18 '18 at 17:54

protected by Community Oct 25 '18 at 17:24

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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