Skip to content

Commit 886c283

Browse files
committed
Added frankfurter.dev API provider
1 parent a549f2b commit 886c283

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed

src/Service/Frankfurter.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
namespace Exchanger\Service;
7+
8+
use Exchanger\Contract\ExchangeRate;
9+
use Exchanger\Contract\ExchangeRateQuery;
10+
use Exchanger\Contract\HistoricalExchangeRateQuery as HistoricalExchangeRateQueryContract;
11+
use Exchanger\Exception\UnsupportedCurrencyPairException;
12+
13+
/**
14+
* Uses the free API at https://frankfurter.dev/
15+
*/
16+
final class Frankfurter extends HttpService
17+
{
18+
19+
private const BASE_URL = "https://api.frankfurter.dev/v1/";
20+
private const SUPPORTED_CURRENCIES = [
21+
'AUD', 'BGN', 'BRL', 'CAD', 'CHF', 'CNY', 'CZK', 'DKK', 'EUR',
22+
'GBP', 'HKD', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'JPY',
23+
'KRW', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RON',
24+
'SEK', 'SGD', 'THB', 'TRY', 'USD', 'ZAR'
25+
];
26+
27+
public function supportQuery(ExchangeRateQuery $exchangeQuery): bool
28+
{
29+
$currencyPair = $exchangeQuery->getCurrencyPair();
30+
return in_array($currencyPair->getBaseCurrency(), self::SUPPORTED_CURRENCIES) &&
31+
in_array($currencyPair->getQuoteCurrency(), self::SUPPORTED_CURRENCIES);
32+
}
33+
34+
public function getName(): string
35+
{
36+
return 'frankfurter';
37+
}
38+
39+
public function getExchangeRate(ExchangeRateQuery $exchangeQuery): ExchangeRate
40+
{
41+
$currencyPair = $exchangeQuery->getCurrencyPair();
42+
$base = $currencyPair->getBaseCurrency();
43+
$quote = $currencyPair->getQuoteCurrency();
44+
45+
if ($exchangeQuery instanceof HistoricalExchangeRateQueryContract) {
46+
$date = $exchangeQuery->getDate()->format('Y-m-d');
47+
$url = self::BASE_URL . "{$date}?base={$base}&symbols={$quote}";
48+
} else {
49+
$url = self::BASE_URL . "latest?base={$base}&symbols={$quote}";
50+
}
51+
52+
$content = $this->request($url);
53+
$data = json_decode($content, true);
54+
55+
if (!isset($data['rates'][$quote])) {
56+
throw new UnsupportedCurrencyPairException($currencyPair, $this);
57+
}
58+
59+
$rate = (float)$data['rates'][$quote];
60+
$date = new \DateTime($data['date']);
61+
62+
return $this->createRate($currencyPair, $rate, $date);
63+
}
64+
}

src/Service/Registry.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public static function getServices(): array
5555
'exchangeratehost' => ExchangerateHost::class,
5656
'apilayer_fixer' => ApiLayer\Fixer::class,
5757
'apilayer_currency_data' => ApiLayer\CurrencyData::class,
58-
'apilayer_exchange_rates_data' => ApiLayer\ExchangeRatesData::class
58+
'apilayer_exchange_rates_data' => ApiLayer\ExchangeRatesData::class,
59+
'frankfurter' => Frankfurter::class,
5960
];
6061
}
6162
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"amount": 1,
3+
"base": "EUR",
4+
"date": "2025-09-05",
5+
"rates": {
6+
"USD": 1.1697
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"amount": 1,
3+
"base": "EUR",
4+
"date": "1999-04-13",
5+
"rates": {
6+
"USD": 1.0765
7+
}
8+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Exchanger\Tests\Service;
4+
5+
use Exchanger\CurrencyPair;
6+
use Exchanger\ExchangeRateQuery;
7+
use Exchanger\HistoricalExchangeRateQuery;
8+
use Exchanger\Service\Frankfurter;
9+
10+
class FrankfurterTest extends ServiceTestCase
11+
{
12+
public function testItHasAName(): void
13+
{
14+
$service = new Frankfurter($this->createMock('Http\Client\HttpClient'));
15+
16+
$this->assertSame('frankfurter', $service->getName());
17+
}
18+
19+
public function testFetchLatestRate(): void
20+
{
21+
$url = 'https://api.frankfurter.dev/v1/latest?base=EUR&symbols=USD';
22+
$content = file_get_contents(__DIR__.'/../../Fixtures/Service/Frankfurter/EUR_USD.json');
23+
24+
$pair = CurrencyPair::createFromString('EUR/USD');
25+
$service = new Frankfurter($this->getHttpAdapterMock($url, $content));
26+
27+
$rate = $service->getExchangeRate(new ExchangeRateQuery($pair));
28+
29+
$this->assertSame(1.1697, $rate->getValue());
30+
$this->assertEquals(new \DateTime('2025-09-05'), $rate->getDate());
31+
}
32+
33+
public function testFetchHistoricRate(): void
34+
{
35+
$url = 'https://api.frankfurter.dev/v1/1999-04-13?base=EUR&symbols=USD';
36+
$content = file_get_contents(__DIR__.'/../../Fixtures/Service/Frankfurter/EUR_USD_historic.json');
37+
38+
$pair = CurrencyPair::createFromString('EUR/USD');
39+
$service = new Frankfurter($this->getHttpAdapterMock($url, $content));
40+
41+
$rate = $service->getExchangeRate(new HistoricalExchangeRateQuery($pair, new \DateTime("1999-04-13")));
42+
43+
$this->assertSame(1.0765, $rate->getValue());
44+
$this->assertEquals(new \DateTime('1999-04-13'), $rate->getDate());
45+
}
46+
}

0 commit comments

Comments
 (0)