CurrencyExchange.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <?php
  2. namespace App\Utils;
  3. use Cache;
  4. use Http;
  5. use Log;
  6. class CurrencyExchange
  7. {
  8. /**
  9. * @param string $target target Currency
  10. * @param float|int $amount exchange amount
  11. * @param string $base Base Currency
  12. * @return float|null amount in target currency
  13. */
  14. public static function convert(string $target, float|int $amount, string $base = 'default'): ?float
  15. {
  16. if ($base === 'default') {
  17. $base = sysConfig('standard_currency', 'CNY');
  18. }
  19. $cacheKey = "Currency_{$base}_{$target}_ExRate";
  20. $isStored = Cache::has($cacheKey);
  21. if ($isStored) {
  22. return round($amount * Cache::get($cacheKey), 2);
  23. }
  24. $source = 0;
  25. $rate = null;
  26. while ($source <= 7 && $rate === null) {
  27. $rate = match ($source) {
  28. 0 => self::exchangerateApi($base, $target),
  29. 1 => self::k780($base, $target),
  30. 2 => self::it120($base, $target),
  31. 3 => self::exchangerate($base, $target),
  32. 4 => self::fixer($base, $target),
  33. 5 => self::currencyData($base, $target),
  34. 6 => self::exchangeRatesData($base, $target),
  35. 7 => self::jsdelivrFile($base, $target),
  36. };
  37. $source++;
  38. }
  39. if ($rate !== null) {
  40. Cache::put($cacheKey, $rate, Day);
  41. return round($amount * $rate, 2);
  42. }
  43. return null;
  44. }
  45. private static function exchangerateApi(string $base, string $target): ?float
  46. { // Reference: https://www.exchangerate-api.com/docs/php-currency-api
  47. $key = config('services.currency.exchangerate-api_key');
  48. if ($key) {
  49. $url = "https://v6.exchangerate-api.com/v6/$key/pair/$base/$target";
  50. } else {
  51. $url = "https://open.er-api.com/v6/latest/$base";
  52. }
  53. $response = Http::get($url);
  54. if ($response->ok()) {
  55. $data = $response->json();
  56. if ($data['result'] === 'success') {
  57. return $data[$key ? 'conversion_rate' : 'rates'][$target];
  58. }
  59. Log::emergency('[CurrencyExchange]exchangerateApi exchange failed with following message: '.$data['error-type']);
  60. } else {
  61. Log::emergency('[CurrencyExchange]exchangerateApi request failed '.var_export($response, true));
  62. }
  63. return null;
  64. }
  65. private static function k780(string $base, string $target): ?float
  66. { // Reference: https://www.nowapi.com/api/finance.rate
  67. $response = Http::get("https://sapi.k780.com/?app=finance.rate&scur=$base&tcur=$target&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json");
  68. if ($response->ok()) {
  69. $data = $response->json();
  70. if ($data['success'] === '1') {
  71. return $data['result']['rate'];
  72. }
  73. Log::emergency('[CurrencyExchange]Nowapi exchange failed with following message: '.$data['msg']);
  74. } else {
  75. Log::emergency('[CurrencyExchange]Nowapi request failed'.var_export($response, true));
  76. }
  77. return null;
  78. }
  79. private static function it120(string $base, string $target): ?float
  80. { // Reference: https://www.it120.cc/help/fnun8g.html
  81. $response = Http::get("https://api.it120.cc/gooking/forex/rate?fromCode=$target&toCode=$base");
  82. if ($response->ok()) {
  83. $data = $response->json();
  84. if ($data['code'] === 0) {
  85. return $data['data']['rate'];
  86. }
  87. Log::emergency('[CurrencyExchange]it120 exchange failed with following message: '.$data['msg']);
  88. } else {
  89. Log::emergency('[CurrencyExchange]it120 request failed'.var_export($response, true));
  90. }
  91. return null;
  92. }
  93. private static function exchangerate(string $base, string $target): ?float
  94. { // Reference: https://exchangerate.host/#/
  95. $response = Http::get("https://api.exchangerate.host/latest?base=$base&symbols=$target");
  96. if ($response->ok()) {
  97. $data = $response->json();
  98. if ($data['success'] && $data['base'] === $base) {
  99. return $data['rates'][$target];
  100. }
  101. Log::emergency('[CurrencyExchange]exchangerate exchange failed with following message: '.$data['error-type']);
  102. }
  103. Log::emergency('[CurrencyExchange]exchangerate request failed');
  104. return null;
  105. }
  106. private static function fixer(string $base, string $target): ?float
  107. { // Reference: https://apilayer.com/marketplace/fixer-api RATE LIMIT: 100 Requests / Monthly!!!!
  108. $key = config('services.currency.apiLayer_key');
  109. if ($key) {
  110. $response = Http::withHeaders(['apikey' => $key])->get("https://api.apilayer.com/fixer/latest?symbols=$target&base=$base");
  111. if ($response->ok()) {
  112. $data = $response->json();
  113. if ($data['success']) {
  114. return $data['rates'][$target];
  115. }
  116. Log::emergency('[CurrencyExchange]Fixer exchange failed with following message: '.$data['error']['type'] ?? '');
  117. } else {
  118. Log::emergency('[CurrencyExchange]Fixer request failed'.var_export($response, true));
  119. }
  120. }
  121. return null;
  122. }
  123. private static function currencyData(string $base, string $target): ?float
  124. { // Reference: https://apilayer.com/marketplace/currency_data-api RATE LIMIT: 100 Requests / Monthly
  125. $key = config('services.currency.apiLayer_key');
  126. if ($key) {
  127. $response = Http::withHeaders(['apikey' => $key])->get("https://api.apilayer.com/currency_data/live?source=$base&currencies=$target");
  128. if ($response->ok()) {
  129. $data = $response->json();
  130. if ($data['success']) {
  131. return $data['quotes'][$base.$target];
  132. }
  133. Log::emergency('[CurrencyExchange]Currency Data exchange failed with following message: '.$data['error']['info'] ?? '');
  134. } else {
  135. Log::emergency('[CurrencyExchange]Currency Data request failed'.var_export($response, true));
  136. }
  137. }
  138. return null;
  139. }
  140. private static function exchangeRatesData(string $base, string $target): ?float
  141. { // Reference: https://apilayer.com/marketplace/exchangerates_data-api RATE LIMIT: 250 Requests / Monthly
  142. $key = config('services.currency.apiLayer_key');
  143. if ($key) {
  144. $response = Http::withHeaders(['apikey' => $key])->get("https://api.apilayer.com/exchangerates_data/latest?symbols=$target&base=$base");
  145. if ($response->ok()) {
  146. $data = $response->json();
  147. if ($data['success']) {
  148. return $data['rates'][$target];
  149. }
  150. Log::emergency('[CurrencyExchange]Exchange Rates Data exchange failed with following message: '.$data['error']['message'] ?? '');
  151. } else {
  152. Log::emergency('[CurrencyExchange]Exchange Rates Data request failed'.var_export($response, true));
  153. }
  154. }
  155. return null;
  156. }
  157. private static function jsdelivrFile(string $base, string $target): ?float
  158. { // Reference: https://github.com/fawazahmed0/currency-api
  159. $response = Http::get('https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/'.strtolower($base).'/'.strtolower($target).'.min.json');
  160. if ($response->ok()) {
  161. $data = $response->json();
  162. return $data[strtolower($target)];
  163. }
  164. return null;
  165. }
  166. }