Skip to content

Commit be3e4b3

Browse files
committed
feat: api layer exchange rates data
1 parent 6b8aff1 commit be3e4b3

16 files changed

+459
-7
lines changed

src/Service/ApiLayer/CurrencyData.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Exchanger\Contract\ExchangeRateQuery;
1818
use Exchanger\Contract\HistoricalExchangeRateQuery;
1919
use Exchanger\Exception\Exception;
20+
use Exchanger\Exception\NonBreakingInvalidArgumentException;
2021
use Exchanger\Exception\UnsupportedCurrencyPairException;
2122
use Exchanger\ExchangeRate;
2223
use Exchanger\Service\HttpService;
@@ -25,7 +26,9 @@
2526
use Exchanger\Contract\ExchangeRate as ExchangeRateContract;
2627

2728
/**
28-
* ApiLayer Currency Data Service (https://apilayer.com/marketplace/currency_data-api).
29+
* ApiLayer Currency Data Service.
30+
*
31+
* @see https://apilayer.com/marketplace/currency_data-api
2932
*
3033
* @author Florian Voutzinos <[email protected]>
3134
*/
@@ -45,7 +48,7 @@ final class CurrencyData extends HttpService
4548
public function processOptions(array &$options): void
4649
{
4750
if (!isset($options[self::API_KEY_OPTION])) {
48-
throw new \InvalidArgumentException('The "api_key" option must be provided to use CurrencyData (https://apilayer.com/marketplace/currency_data-api).');
51+
throw new NonBreakingInvalidArgumentException('The "api_key" option must be provided to use CurrencyData (https://apilayer.com/marketplace/currency_data-api).');
4952
}
5053
}
5154

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Exchanger.
7+
*
8+
* (c) Florian Voutzinos <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Exchanger\Service\ApiLayer;
15+
16+
use Exchanger\Contract\CurrencyPair;
17+
use Exchanger\Contract\ExchangeRate as ExchangeRateContract;
18+
use Exchanger\Contract\ExchangeRateQuery;
19+
use Exchanger\Contract\HistoricalExchangeRateQuery;
20+
use Exchanger\Exception\Exception;
21+
use Exchanger\Exception\NonBreakingInvalidArgumentException;
22+
use Exchanger\Exception\UnsupportedCurrencyPairException;
23+
use Exchanger\ExchangeRate;
24+
use Exchanger\Service\HttpService;
25+
use Exchanger\Service\SupportsHistoricalQueries;
26+
use Exchanger\StringUtil;
27+
28+
/**
29+
* ApiLayer Exchange Rates Data API.
30+
*
31+
* @see https://apilayer.com/marketplace/exchangerates_data-api
32+
*
33+
* @author Florian Voutzinos <[email protected]>
34+
*/
35+
final class ExchangeRatesData extends HttpService
36+
{
37+
use SupportsHistoricalQueries;
38+
39+
const API_KEY_OPTION = 'api_key';
40+
41+
const LATEST_URL = 'https://api.apilayer.com/exchangerates_data/latest?base=%s&apikey=%s&symbols=%s';
42+
43+
const HISTORICAL_URL = 'https://api.apilayer.com/exchangerates_data/%s?base=%s&apikey=%s&symbols=%s';
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function processOptions(array &$options): void
49+
{
50+
if (!isset($options[self::API_KEY_OPTION])) {
51+
throw new NonBreakingInvalidArgumentException('The "api_key" option must be provided to use Exchange Rates Data (https://apilayer.com/marketplace/exchangerates_data-api).');
52+
}
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
protected function getLatestExchangeRate(ExchangeRateQuery $exchangeQuery): ExchangeRateContract
59+
{
60+
$currencyPair = $exchangeQuery->getCurrencyPair();
61+
62+
$url = sprintf(
63+
self::LATEST_URL,
64+
$currencyPair->getBaseCurrency(),
65+
$this->options[self::API_KEY_OPTION],
66+
$currencyPair->getQuoteCurrency()
67+
);
68+
69+
return $this->doCreateRate($url, $currencyPair);
70+
}
71+
72+
/**
73+
* {@inheritdoc}
74+
*/
75+
protected function getHistoricalExchangeRate(HistoricalExchangeRateQuery $exchangeQuery): ExchangeRateContract
76+
{
77+
$currencyPair = $exchangeQuery->getCurrencyPair();
78+
79+
$url = sprintf(
80+
self::HISTORICAL_URL,
81+
$exchangeQuery->getDate()->format('Y-m-d'),
82+
$exchangeQuery->getCurrencyPair()->getBaseCurrency(),
83+
$this->options[self::API_KEY_OPTION],
84+
$currencyPair->getQuoteCurrency()
85+
);
86+
87+
return $this->doCreateRate($url, $currencyPair);
88+
}
89+
90+
/**
91+
* {@inheritdoc}
92+
*/
93+
public function supportQuery(ExchangeRateQuery $exchangeQuery): bool
94+
{
95+
return true;
96+
}
97+
98+
/**
99+
* Creates a rate.
100+
*
101+
* @param string $url
102+
*
103+
* @throws Exception
104+
*/
105+
private function doCreateRate($url, CurrencyPair $currencyPair): ExchangeRate
106+
{
107+
$content = $this->request($url);
108+
$data = StringUtil::jsonToArray($content);
109+
110+
if (isset($data['error'])) {
111+
if (isset($data['error']['code'])) {
112+
if (\in_array($data['error']['code'], [
113+
'invalid_currency_codes',
114+
'invalid_base_currency',
115+
'no_rates_available',
116+
], true)) {
117+
throw new UnsupportedCurrencyPairException($currencyPair, $this);
118+
}
119+
if (isset($data['error']['message'])) {
120+
throw new Exception($data['error']['message']);
121+
} else {
122+
throw new Exception('Service return error code: '.$data['error']['code']);
123+
}
124+
} else {
125+
throw new Exception('Service return unhandled error');
126+
}
127+
}
128+
129+
if (isset($data['rates'][$currencyPair->getQuoteCurrency()])) {
130+
$date = new \DateTime($data['date']);
131+
$rate = $data['rates'][$currencyPair->getQuoteCurrency()];
132+
133+
return $this->createRate($currencyPair, (float) $rate, $date);
134+
}
135+
136+
throw new UnsupportedCurrencyPairException($currencyPair, $this);
137+
}
138+
139+
/**
140+
* {@inheritdoc}
141+
*/
142+
public function getName(): string
143+
{
144+
return 'apilayer_exchange_rates_data';
145+
}
146+
}

src/Service/ApiLayer/Fixer.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Exchanger\Contract\ExchangeRateQuery;
1818
use Exchanger\Contract\HistoricalExchangeRateQuery;
1919
use Exchanger\Exception\Exception;
20+
use Exchanger\Exception\NonBreakingInvalidArgumentException;
2021
use Exchanger\Exception\UnsupportedCurrencyPairException;
2122
use Exchanger\ExchangeRate;
2223
use Exchanger\Service\HttpService;
@@ -25,7 +26,9 @@
2526
use Exchanger\Contract\ExchangeRate as ExchangeRateContract;
2627

2728
/**
28-
* ApiLayer Fixer Service (https://apilayer.com/marketplace/fixer-api).
29+
* ApiLayer Fixer Service.
30+
*
31+
* @see https://apilayer.com/marketplace/fixer-api
2932
*
3033
* @author Florian Voutzinos <[email protected]>
3134
*/
@@ -45,7 +48,7 @@ final class Fixer extends HttpService
4548
public function processOptions(array &$options): void
4649
{
4750
if (!isset($options[self::API_KEY_OPTION])) {
48-
throw new \InvalidArgumentException('The "api_key" option must be provided to use Fixer (https://apilayer.com/marketplace/fixer-api).');
51+
throw new NonBreakingInvalidArgumentException('The "api_key" option must be provided to use Fixer (https://apilayer.com/marketplace/fixer-api).');
4952
}
5053
}
5154

src/Service/Registry.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ public static function getServices(): array
5454
'abstract_api' => AbstractApi::class,
5555
'exchangeratehost' => ExchangerateHost::class,
5656
'apilayer_fixer' => ApiLayer\Fixer::class,
57-
'apilayer_currency_data' => ApiLayer\CurrencyData::class
57+
'apilayer_currency_data' => ApiLayer\CurrencyData::class,
58+
'apilayer_exchange_rates_data' => ApiLayer\ExchangeRatesData::class
5859
];
5960
}
6061
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"error":{
3+
"code":"base_currency_access_restricted",
4+
"message":"An unexpected error ocurred. [Technical Support: [email protected]]"
5+
}
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"success":true,
3+
"timestamp":1618531199,
4+
"historical":true,
5+
"base":"EUR",
6+
"date":"2021-04-15",
7+
"rates":{
8+
"USD":1.196953
9+
}
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"error":{
3+
"code":"https_access_restricted",
4+
"message":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"error":{
3+
"code":"invalid_access_key",
4+
"message":"You have not supplied a valid API Access Key. [Technical Support: [email protected]]"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"error":{
3+
"code":"invalid_base_currency",
4+
"message":"An unexpected error ocurred. [Technical Support: [email protected]]"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"error":{
3+
"code":"invalid_currency_codes",
4+
"message":"You have provided one or more invalid Currency Codes. [Required format: currencies=EUR,USD,GBP,...]"
5+
}
6+
}

0 commit comments

Comments
 (0)