Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 64 additions & 23 deletions Modules/Invoice/Services/CurrencyService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
namespace Modules\Invoice\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Modules\Invoice\Contracts\CurrencyServiceContract;

class CurrencyService implements CurrencyServiceContract
Expand All @@ -17,13 +20,15 @@ public function __construct()

public function setClient()
{
$headers = $headers = [
$headers = [
'apikey' => config('services.currencylayer.access_key'),
];

$this->client = new Client([
// 'base_uri' => 'http://apilayer.net/api', //This is old API URL
'base_uri' => 'https://api.apilayer.com', // This is new API created from https://apilayer.com/marketplace/currency_data-api
'headers' => $headers,
'base_uri' => 'https://api.apilayer.com',
'headers' => $headers,
'timeout' => 10,
'connect_timeout' => 5,
]);
}

Expand All @@ -48,35 +53,71 @@ public function getAllCurrentRatesInINR()
private function fetchExchangeRateInINR()
{
if (! config('services.currencylayer.access_key')) {
return round(config('services.currencylayer.default_rate'), 2);
}
Log::warning('CurrencyLayer API key missing. Using default rate.');

$response = $this->client->get('currency_data/live', [
'query' => [
'access_key' => config('services.currencylayer.access_key'),
'currencies' => 'INR',
],
]);
return round(config('services.currencylayer.default_rate', 83.00), 2);
}
Comment on lines 55 to +59
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract default rate to a helper method.

The default rate calculation round(config('services.currencylayer.default_rate', 83.00), 2) is duplicated across lines 58, 84, 93, and 120. This violates the DRY principle.

Extract this to a private helper method:

+    private function getDefaultRate()
+    {
+        return round(config('services.currencylayer.default_rate', 83.00), 2);
+    }
+
     private function fetchExchangeRateInINR()
     {
         if (! config('services.currencylayer.access_key')) {
             Log::warning('CurrencyLayer API key missing. Using default rate.');
 
-            return round(config('services.currencylayer.default_rate', 83.00), 2);
+            return $this->getDefaultRate();
         }

Then replace all other occurrences (lines 84, 93, 120) with $this->getDefaultRate().

🤖 Prompt for AI Agents
In Modules/Invoice/Services/CurrencyService.php around lines 55-59 (and also
replace duplicates at lines 84, 93, and 120), extract the duplicated default
rate expression round(config('services.currencylayer.default_rate', 83.00), 2)
into a private helper method named getDefaultRate() that returns that rounded
value, then replace each occurrence with $this->getDefaultRate(); ensure the
helper is private, placed in the class, and all four sites use that method so
the logic is centralized.


$data = json_decode($response->getBody()->getContents(), true);
try {
$response = $this->client->get('currency_data/live', [
'query' => [
'currencies' => 'INR',
'source' => 'USD',
],
]);

$data = json_decode($response->getBody()->getContents(), true);

if (empty($data) || empty($data['quotes']['USDINR'])) {
throw new \Exception('Invalid API response structure.');
}
Comment on lines +71 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix potential PHP notice in validation logic.

The expression empty($data['quotes']['USDINR']) on Line 71 will trigger a PHP notice if $data['quotes'] doesn't exist. The validation should check the structure step-by-step.

Apply this diff to fix the validation:

-            if (empty($data) || empty($data['quotes']['USDINR'])) {
+            if (empty($data) || !isset($data['quotes']['USDINR']) || empty($data['quotes']['USDINR'])) {
                 throw new \Exception('Invalid API response structure.');
             }

Alternatively, validate in stages:

-            if (empty($data) || empty($data['quotes']['USDINR'])) {
+            if (empty($data)) {
+                throw new \Exception('Empty API response.');
+            }
+            
+            if (!isset($data['quotes']['USDINR']) || empty($data['quotes']['USDINR'])) {
                 throw new \Exception('Invalid API response structure.');
             }
🤖 Prompt for AI Agents
In Modules/Invoice/Services/CurrencyService.php around lines 71 to 73, the
current validation uses empty($data['quotes']['USDINR']) which can raise a PHP
notice if $data['quotes'] is undefined; update the check to validate the
structure in stages — first ensure $data is not empty and is an array, then
ensure $data['quotes'] exists and is an array (or use isset) before checking
$data['quotes']['USDINR'], and throw the same exception if any of those checks
fail.


return round($data['quotes']['USDINR'], 2);
} catch (ConnectException $e) {
Log::error('Currency API connection failed: ' . $e->getMessage());
} catch (RequestException $e) {
Log::error('Currency API request error: ' . $e->getMessage());
} catch (\Throwable $e) {
Log::error('Unexpected error fetching exchange rate: ' . $e->getMessage());
}

return round($data['quotes']['USDINR'], 2);
return round(config('services.currencylayer.default_rate', 83.00), 2);
}

private function fetchAllExchangeRateInINR()
{
if (! config('services.currencylayer.access_key')) {
return round(config('services.currencylayer.default_rate'), 2);
}
Log::warning('CurrencyLayer API key missing. Using default rate.');

$response = $this->client->get('currency_data/live', [
'query' => [
'access_key' => config('services.currencylayer.access_key'),
],
]);
return [
'USDINR' => round(config('services.currencylayer.default_rate', 83.00), 2),
];
}

$data = json_decode($response->getBody()->getContents(), true);
try {
$response = $this->client->get('currency_data/live', [
'query' => [
'source' => 'USD',
],
]);

$data = json_decode($response->getBody()->getContents(), true);

if (empty($data) || empty($data['quotes'])) {
throw new \Exception('Invalid API response structure.');
}

return $data['quotes'];
} catch (ConnectException $e) {
Log::error('Currency API connection failed: ' . $e->getMessage());
} catch (RequestException $e) {
Log::error('Currency API request error: ' . $e->getMessage());
} catch (\Throwable $e) {
Log::error('Unexpected error fetching all exchange rates: ' . $e->getMessage());
}

return $data['quotes'];
return [
'USDINR' => round(config('services.currencylayer.default_rate', 83.00), 2),
];
}
}