Deploy Ladill Hosting / deploy (push) Failing after 17s
Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
4.8 KiB
PHP
167 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Currency;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class CurrencyConverterService
|
|
{
|
|
private const CACHE_KEY = 'currency:usd_to_ghs';
|
|
private const CACHE_KEY_EUR = 'currency:eur_to_ghs';
|
|
private const CACHE_TTL_MINUTES = 60;
|
|
private const FALLBACK_RATE = 15.50;
|
|
private const FALLBACK_EUR_RATE = 16.90;
|
|
|
|
public function getUsdToGhsRate(): float
|
|
{
|
|
return Cache::remember(self::CACHE_KEY, now()->addMinutes(self::CACHE_TTL_MINUTES), function () {
|
|
return $this->fetchRateFromBase('USD') ?? self::FALLBACK_RATE;
|
|
});
|
|
}
|
|
|
|
public function getEurToGhsRate(): float
|
|
{
|
|
return Cache::remember(self::CACHE_KEY_EUR, now()->addMinutes(self::CACHE_TTL_MINUTES), function () {
|
|
return $this->fetchRateFromBase('EUR') ?? self::FALLBACK_EUR_RATE;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Convert USD amount to GHS.
|
|
*/
|
|
public function convertUsdToGhs(float $usdAmount): float
|
|
{
|
|
return $usdAmount * $this->getUsdToGhsRate();
|
|
}
|
|
|
|
/**
|
|
* Convert USD to GHS with markup percentage.
|
|
*/
|
|
public function convertWithMarkup(float $usdAmount, float $markupPercent = 35): float
|
|
{
|
|
$ghsAmount = $this->convertUsdToGhs($usdAmount);
|
|
return $ghsAmount * (1 + ($markupPercent / 100));
|
|
}
|
|
|
|
/**
|
|
* Convert USD cents to GHS pesewas with markup.
|
|
*/
|
|
public function convertCentsTopesewaswithMarkup(int $usdCents, float $markupPercent = 35): int
|
|
{
|
|
$usdAmount = $usdCents / 100;
|
|
$ghsAmount = $this->convertWithMarkup($usdAmount, $markupPercent);
|
|
return (int) round($ghsAmount * 100);
|
|
}
|
|
|
|
private function fetchRateFromBase(string $base): ?float
|
|
{
|
|
$rate = $this->fetchFromExchangeRateApi($base)
|
|
?? $this->fetchFromFreeCurrencyApi($base)
|
|
?? $this->fetchFromOpenExchangeRates($base);
|
|
|
|
if ($rate !== null) {
|
|
Log::info('Currency rate fetched', ['base' => $base, 'ghs' => $rate]);
|
|
}
|
|
|
|
return $rate;
|
|
}
|
|
|
|
private function fetchFromExchangeRateApi(string $base): ?float
|
|
{
|
|
try {
|
|
$response = Http::timeout(5)->get("https://api.exchangerate-api.com/v4/latest/{$base}");
|
|
|
|
if ($response->successful()) {
|
|
$data = $response->json();
|
|
return (float) ($data['rates']['GHS'] ?? null) ?: null;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('ExchangeRateApi fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function fetchFromFreeCurrencyApi(string $base): ?float
|
|
{
|
|
$apiKey = config('services.freecurrencyapi.key');
|
|
|
|
if (empty($apiKey)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$response = Http::timeout(5)->get('https://api.freecurrencyapi.com/v1/latest', [
|
|
'apikey' => $apiKey,
|
|
'base_currency' => $base,
|
|
'currencies' => 'GHS',
|
|
]);
|
|
|
|
if ($response->successful()) {
|
|
$data = $response->json();
|
|
return (float) ($data['data']['GHS'] ?? null) ?: null;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('FreeCurrencyApi fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function fetchFromOpenExchangeRates(string $base): ?float
|
|
{
|
|
$appId = config('services.openexchangerates.app_id');
|
|
|
|
if (empty($appId)) {
|
|
return null;
|
|
}
|
|
|
|
// Free tier only supports USD as base
|
|
if ($base !== 'USD') {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$response = Http::timeout(5)->get('https://openexchangerates.org/api/latest.json', [
|
|
'app_id' => $appId,
|
|
'symbols' => 'GHS',
|
|
]);
|
|
|
|
if ($response->successful()) {
|
|
$data = $response->json();
|
|
return (float) ($data['rates']['GHS'] ?? null) ?: null;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('OpenExchangeRates fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Force refresh the cached rate.
|
|
*/
|
|
public function refreshRate(): float
|
|
{
|
|
Cache::forget(self::CACHE_KEY);
|
|
Cache::forget(self::CACHE_KEY_EUR);
|
|
return $this->getUsdToGhsRate();
|
|
}
|
|
|
|
/**
|
|
* Get the current cached rate info.
|
|
*/
|
|
public function getRateInfo(): array
|
|
{
|
|
return [
|
|
'rate' => $this->getUsdToGhsRate(),
|
|
'from' => 'USD',
|
|
'to' => 'GHS',
|
|
'cached' => Cache::has(self::CACHE_KEY),
|
|
'fallback' => self::FALLBACK_RATE,
|
|
];
|
|
}
|
|
}
|