Is there any better way to get Currency Exchange Rate in PHP?
Asked Answered
K

3

5

Currency Exchange Rate with below code is working sometimes and not working sometimes and not at all reliable. Is there any better way to get Currency Exchange Rate in PHP?

public function getJPYtoUSDExchangeRate(){
    $from    = 'JPY';
    $to    = 'USD';
    $amount  = 1;
    $data = file_get_contents("https://finance.google.com/finance/converter?a=$amount&from=$from&to=$to");
    preg_match("/<span class=bld>(.*)<\/span>/",$data, $converted);
    $converted = preg_replace("/[^0-9.]/", "", $converted[1][0]);
    return number_format(round($converted, 3),2);
}
Karikaria answered 5/6, 2018 at 3:13 Comment(1)
Possible duplicate of How do I get currency exchange rates via an API such as Google Finance?Intercession
E
11

You have several issues:

  • You're not calling an actual API, you're scraping a web page, which means that:
    • you're most likely violating Google's TOS
    • you're more likely to get rate-limited (or be detected as abuse and blacklisted) at some point if you're fetching this page too often
    • you're dependent on any change made in the HTML structure of the web page
  • You're scraping the page every single time you need to convert an amount to another currency, which means that any failure makes your currency conversion fail.

What you should do:

  • load exchange rates from a legitimate feed or API
  • load them on a regular basis (via a cron job for example) and save them to a local database, that will be used to perform currency conversions

This way, even if an API call fails, you still have access to a slightly outdated exchange rate, which is better than a failure in most cases.


Where do you find a trustable exchange rate feed?

There are plenty of APIs, free or not, that offer this service.

A good source I know of is the European Central Bank, who provides an XML feed that's been there for years and provides exchange rates for 32 currencies relative to EUR.

OpenExchangeRates also offers a free plan with a limit of 1,000 requests per month, which is enough to refresh rates every hour. It provides exchange rates for 170 currencies, relative to USD.

How do you store the values in your database?

Whichever feed you choose, you need to parse it (if XML) or json_decode() it (if JSON) and store the values in your database. Ideally, set up a cron job to run your import script daily or even hourly.

The actual parsing and importing steps are outside the scope of this question, but let's assume a simple MySQL table that holds the records:

CREATE TABLE exchange_rate(
  target_currency CHAR(3) COLLATE ascii_bin NOT NULL PRIMARY KEY,
  exchange_rate DOUBLE NOT NULL
);

How to properly handle currency conversions based on rates relative to a single currency?

This is a question I've answered recently. The feeds above give you rates to convert the base currency (EUR or USD) to another currency, but do not give you a clue on how to convert between two arbitrary currencies. I would suggest you use a proper library that handles these conversions for you, such as brick/money - disclaimer: I'm the author.

Here is how you would configure it to load your exchange rates from the table above:

use Brick\Money\CurrencyConverter;
use Brick\Money\ExchangeRateProvider\PDOProvider;
use Brick\Money\ExchangeRateProvider\PDOProviderConfiguration;
use Brick\Money\ExchangeRateProvider\BaseCurrencyProvider;

// set to whatever your rates are relative to
$baseCurrency = 'USD';

// use your own credentials, or re-use your existing PDO connection
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');

$configuration = new PDOProviderConfiguration();

$configuration->tableName                = 'exchange_rate';
$configuration->exchangeRateColumnName   = 'exchange_rate';
$configuration->targetCurrencyColumnName = 'target_currency';
$configuration->sourceCurrencyCode       = $baseCurrency;

// this provider loads exchange rates from your database
$provider = new PDOProvider($pdo, $configuration);

// this provider calculates exchange rates relative to the base currency
$provider = new BaseCurrencyProvider($provider, $baseCurrency);

// this currency converter can now handle any currency pair
$converter = new CurrencyConverter($provider);

And how you would use it:

use Brick\Math\RoundingMode;
use Brick\Money\Money;

$money = Money::of(10, 'EUR'); // EUR 10.00
$converter->convert($money, 'CAD', RoundingMode::DOWN); // CAD 15.27
Execrate answered 5/6, 2018 at 10:39 Comment(3)
Thank you Benjamin for detailed explanation. Your approach is very good.Karikaria
Still no reliable free currencies mid market exchange rate API for 2020?Defrost
Good catch fore European Central Bank XML feed! Searching for the USD similar XML feed!Locarno
G
5

CurrencyFreaks API provides trusty exchange rates for 179 currencies worldwide in JSON and XML formats compatible with multiple programming languages. By using CurrencyFreaks API, you can also change the 'base' currency and can get exchange rates for specific currencies.

Here is a simple currency exchange rate endpoint by using PHP:

setUrl('https://api.currencyfreaks.com/latest
    ?apikey=YOUR_APIKEY
    &base=GBP');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

The JSON response will be:

{
    "date": "2020-10-06 11:22:00+00",
    "base": "GBP",
    "rates": {
        "FJD": "2.737385252915371",
        "MXN": "27.74546375788785",
        "STD": "27185.017172962733",
        "LVL": "0.8482572402792966",
        "SCR": "23.257414775003944",
        "CDF": "2535.4260357935937",
        "BBD": "2.585121591194042",
        "GTQ": "10.055244048403818",
        "CLP": "1031.8523300993463",
        "HNL": "31.82062875327341",
        "UGX": "4769.159332676713",
        "ZAR": "21.445845580346873",
        "TND": "3.542262860333636",
        "CUC": "1.2926654930214643",
        "BSD": "1.292560795597021",
        "SLL": "12676.789824444395",
        "SDG": "71.5109260164052",
        "IQD": "1542.8992384231794",
        "GMD": "66.89002117214584",
        "CUP": "34.25286108332106",
        "TWD": "37.17921872455271",
        "RSD": "128.99756740058268",
        "DOP": "75.46618143934401",
        "KMF": "540.1610026652604",
          .
          .
          .
        [179 Currencies]
    }
}

I hope it works.

Gallop answered 6/10, 2020 at 11:45 Comment(0)
P
1

The bank of Canada provides RSS feed for these currencies : AUD, BRL, CNY, EUR, HKD, INR, IDR, JPY, MXN, NZD, NOK, PEN, RUB, SAR, SGD, ZAR, KRW, SEK, CHF, TWD, TRY, GBP, USD

Here is a way of getting currency conversions without API or 3rd party service:

<?php

class EXCHANGE {

    public $Rates;
    public $Rate;

    public function __construct(){
        $this->Rates = $this->fetchAllRates();
        foreach($this->Rates as $currency => $rate){
            $this->Rate[$currency] = $rate['latest'];
        }
    }

    public function fetchAllRates(){
        $currencies = ["AUD","BRL","CNY","EUR","HKD","INR","IDR","JPY","MXN","NZD","NOK","PEN","RUB","SAR","SGD","ZAR","KRW","SEK","CHF","TWD","TRY","GBP","USD"];
        $cURL = curl_init();
        curl_setopt($cURL, CURLOPT_URL, "https://www.bankofcanada.ca/valet/observations/group/FX_RATES_DAILY/json?start_date=2010-01-01");
        curl_setopt($cURL, CURLOPT_RETURNTRANSFER, 1);
        $rates = curl_exec($cURL);
        curl_close($cURL);
        $rates = json_decode($rates,true)['observations'];
        foreach($currencies as $currency){
            foreach($rates as $rate){
                $AllRates[$currency][$rate['d']] = $rate['FX'.$currency.'CAD']['v'];
                $AllRates[$currency]['latest'] = $rate['FX'.$currency.'CAD']['v'];
            }
        }
        return $AllRates;
    }

    public function convert($value,$from,$to){
        if($to == "CAD"){ return $value*$this->Rate[$from]; }
        else { return ($value*$this->Rate[$from])/$this->Rate[$to]; }
    }

}

$Exchange = new EXCHANGE();

foreach($Exchange->Rate as $currency => $rate){
    echo $currency.': '.$rate."<br>\n"; // Listing all the exchange rates
}

echo "Converting 2.00 USD to CAD : ".$Exchange->convert(2,"USD","CAD")."\n"; //2022-02-23 = 2.5486

echo "Converting 2.00 USD to AUD : ".$Exchange->convert(2,"USD","AUD")."\n"; //2022-02-23 =  2.7708197434225

Update: I had initially forgot to put the convert method.

Information: This class is using the RSS feed of the Bank of Canada. It is not the most accurate data, because it is only updated once per business day. The $Rate property contains the exchange rates for CAD currency. Thus to convert other currencies, it first converts the initial currency to CAD and then to the new currency. So in the example provided above, 2.00 USD is converted to 2.5486 CAD. Then divided by the exchange rate of AUD resulting in 2.7708197434225 AUD.

Purgatory answered 20/4, 2021 at 13:19 Comment(4)
That's a good solution! You are missing the convert function in the class in the snippet above btw.Locarno
Sorry, just updated the class.Purgatory
Thanks! I Now it works, I'm not sure how this api works though, values seems to be different from USD Live Exchange rates exchangerates.org.uk/US-Dollar-USD-currency-table.html any idea?Locarno
Well this class is based on the Bank of Canada RSS Feed. Unfortunately, compared to paid services like the ones mentioned above, it is less accurate. By that I mean that it is not updated as often.(once per day) You can try fetching the information from the live data at exchangerates.org.uk/data/currencies.Purgatory

© 2022 - 2024 — McMap. All rights reserved.