nurbekjummayev / cbu-currency
Central Bank of Uzbekistan currency rates package for Laravel
Requires
- php: ^8.1|^8.2|^8.3|^8.4
- ext-bcmath: *
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^10|^11|^12
- nurbekjummayev/laravel-api-response-helpers: ^0.1.1
Requires (Dev)
- mockery/mockery: ^1.5
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
README
A Laravel package for working with Central Bank of Uzbekistan (CBU) currency exchange rates. This package provides easy-to-use methods for fetching, storing, and converting currencies with high precision using BCMath.
English version | O'zbek versiyasi
📋 Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Usage
- Artisan Commands
- Auto Synchronization
- Database Structure
- Testing
- API Endpoints
- License
✨ Features
- 📊 CBU API Integration - Fetch currency rates from Central Bank API
- 💱 High-Precision Conversion - Accurate calculations using BCMath
- 🗄️ Database Storage - Store historical rates for fast access
- 🎯 Simple API - Intuitive and convenient interface
- ⚙️ Configurable - Full configuration capabilities
- 🔄 Auto Synchronization - Automatic currency updates
- 📅 Historical Data - Rates for any date
- 🚀 RESTful API - Complete REST API endpoints
- ✅ Full Test Coverage - 43 tests included
- 🎨 Fluent Interface - Beautiful and readable code
📦 Requirements
- PHP ^8.1|^8.2|^8.3|^8.4
- Laravel ^10.0|^11.0|^12.0
- BCMath PHP Extension
- GuzzleHTTP ^7.0
🚀 Installation
1. Install via Composer
composer require nurbekjummayev/cbu-currency
2. Publish Configuration and Migrations
# Publish config file
php artisan vendor:publish --tag=cbu-currency-config
# Publish migrations (optional, auto-loaded by default)
php artisan vendor:publish --tag=cbu-currency-migrations
⚙️ Configuration
Configuration file: config/cbu-currency.php
return [
// CBU API Base URL
'base_url' => env('CBU_BASE_URL', 'https://cbu.uz/ru/arkhiv-kursov-valyut/json'),
// Cache duration in minutes
'cache_duration' => env('CBU_CACHE_DURATION', 60),
// Default currency code
'default_currency' => env('CBU_DEFAULT_CURRENCY', 'USD'),
// BCMath calculation scale (decimal places)
'scale' => env('CBU_SCALE', 2),
// Data source: 'database' or 'api'
'source' => env('CBU_SOURCE', 'database'),
// Enable/disable logging
'log_enabled' => env('CBU_LOG_ENABLED', true),
// API routes configuration
'routes' => [
'prefix' => env('CBU_ROUTES_PREFIX', 'api/cbu'),
'middleware' => ['api'],
],
];
Environment Variables
Add to your .env file:
CBU_BASE_URL=https://cbu.uz/ru/arkhiv-kursov-valyut/json
CBU_CACHE_DURATION=60
CBU_DEFAULT_CURRENCY=USD
CBU_SCALE=2
CBU_SOURCE=database
CBU_LOG_ENABLED=true
CBU_ROUTES_PREFIX=api/cbu
Data Source
The CBU_SOURCE configuration determines where currency rates are fetched from:
Option 1: Database (Recommended)
- Value:
database - Pros: Faster response, works offline, reduced API calls
- Cons: Requires periodic synchronization
- Best for: Production environments
When using database source, you need to:
Step 1: Run Migrations
php artisan migrate
This creates two tables:
currencies- stores currency information (USD, EUR, etc.)currency_rates- stores daily exchange rates
Step 2: Sync Currencies
# Fetch and store all available currencies from CBU
php artisan cbu:sync-currencies
Step 3: Fetch Exchange Rates
# Fetch today's rates
php artisan cbu:fetch-rates
# Fetch rates for specific date
php artisan cbu:fetch-rates 2025-01-25
# Fetch yesterday's rates
php artisan cbu:fetch-rates yesterday
Option 2: API (Direct)
- Value:
api - Pros: Always fresh data, no database needed
- Cons: Slower response, requires internet connection
- Best for: Development or when real-time data is critical
When using api source:
- No migrations needed
- No synchronization required
- Data fetched directly from CBU API on each request
📖 Usage
Currency Conversion
The convert() method provides a fluent interface for currency conversion with high precision using BCMath.
use Cbu\Currency\Facades\CbuCurrency;
// Basic conversion
$result = CbuCurrency::convert()
->from('USD')
->to('EUR')
->amount(100)
->get();
echo $result->result; // 94.11
echo $result->fromRate; // 12705.00
echo $result->toRate; // 13500.00
echo $result->amountInUzs; // 1270500.00
Convert to UZS
$result = CbuCurrency::convert()
->from('USD')
->to('UZS')
->amount(100)
->get();
echo $result->result; // 1270500.00
Convert from UZS
$result = CbuCurrency::convert()
->from('UZS')
->to('USD')
->amount(1000000)
->get();
echo $result->result; // 78.70
Cross Currency Conversion
// USD to EUR through UZS
$result = CbuCurrency::convert()
->from('USD')
->to('EUR')
->amount(100)
->get();
// Calculation: 100 USD * 12705 = 1270500 UZS
// 1270500 UZS / 13500 = 94.11 EUR
echo $result->result; // 94.11
echo $result->amountInUzs; // 1270500.00
Using Numeric Codes
$result = CbuCurrency::convert()
->fromCode(840) // USD
->toCode(978) // EUR
->amount(100)
->get();
Specify Date
// Specific date
$result = CbuCurrency::convert()
->from('USD')
->to('EUR')
->amount(100)
->date('2025-01-25')
->get();
// Today's date
$result = CbuCurrency::convert()
->from('USD')
->to('EUR')
->amount(100)
->today()
->get();
Change Data Source
use Cbu\Currency\Enums\CurrencySource;
// Force API source
$result = CbuCurrency::convert()
->source(CurrencySource::API)
->from('USD')
->to('EUR')
->amount(100)
->get();
// Force database source
$result = CbuCurrency::convert()
->source(CurrencySource::DATABASE)
->from('USD')
->to('EUR')
->amount(100)
->get();
With Caching
// Cache result for 60 minutes
$result = CbuCurrency::convert()
->from('USD')
->to('EUR')
->amount(100)
->cache(60)
->get();
Get Exchange Rates
The rate() method retrieves exchange rates for specific currencies and dates.
use Cbu\Currency\Facades\CbuCurrency;
// Get single currency rate for today
$rate = CbuCurrency::rate('USD');
echo $rate->ccy; // "USD"
echo $rate->rate; // 12705.00
echo $rate->diff; // 15.25
echo $rate->date; // "2025-11-09"
Get Rate for Specific Date
// Get USD rate for specific date
$rate = CbuCurrency::rate('USD', '2025-01-25');
echo $rate->rate; // 12705.00
echo $rate->date; // "2025-01-25"
Get All Rates
// Get all currencies rates for today
$rates = CbuCurrency::rate();
foreach ($rates as $rate) {
echo "{$rate->ccy}: {$rate->rate}\n";
}
// USD: 12705.00
// EUR: 13500.00
// RUB: 130.00
Get All Rates for Specific Date
// Get all rates for specific date
$rates = CbuCurrency::rate(null, '2025-01-25');
foreach ($rates as $rate) {
echo "{$rate->ccy}: {$rate->rate} ({$rate->date})\n";
}
Get Currencies List
The currencies() method retrieves available currency information.
use Cbu\Currency\Facades\CbuCurrency;
// Get all currencies
$currencies = CbuCurrency::currencies();
foreach ($currencies as $currency) {
echo "{$currency->ccy} - {$currency->name_en}\n";
}
// USD - US Dollar
// EUR - Euro
// RUB - Russian Ruble
Get Specific Currency
// Get single currency info
$currency = CbuCurrency::currencies('USD');
echo $currency->ccy; // "USD"
echo $currency->name_en; // "US Dollar"
echo $currency->name_uz; // "AQSH dollari"
echo $currency->name_ru; // "Доллар США"
echo $currency->code; // 840
Get Currency Codes Only
// Get array of currency codes
$codes = CbuCurrency::currencies()->pluck('ccy')->toArray();
// ['USD', 'EUR', 'RUB', 'GBP', 'JPY', ...]
Sync Currencies
The sync() method synchronizes currencies and rates from CBU API to your database.
use Cbu\Currency\Facades\CbuCurrency;
// Sync currencies list
CbuCurrency::sync('currencies');
// Sync today's rates
CbuCurrency::sync('rates');
// Sync rates for specific date
CbuCurrency::sync('rates', '2025-01-25');
Sync Both Currencies and Rates
// Sync both currencies and rates
CbuCurrency::sync('currencies');
CbuCurrency::sync('rates');
🔧 Artisan Commands
The package provides two Artisan commands for managing currency data.
1. Sync Currencies
Fetch and store all available currencies from CBU API to database.
php artisan cbu:sync-currencies
What it does:
- Fetches all currencies from CBU API
- Adds new currencies to
currenciestable - Updates existing currency information
Example output:
Fetching currencies from CBU API...
Found 30 currencies
Synced: USD, EUR, RUB, GBP, JPY, CHF...
Currency synchronization completed successfully.
When to use:
- After initial installation
- When CBU adds new currencies
- Weekly maintenance (recommended)
2. Fetch Exchange Rates
Fetch and store exchange rates for a specific date.
# Fetch today's rates
php artisan cbu:fetch-rates
# Fetch for specific date
php artisan cbu:fetch-rates 2025-01-25
# Fetch yesterday's rates
php artisan cbu:fetch-rates yesterday
# Fetch for relative date
php artisan cbu:fetch-rates "1 week ago"
php artisan cbu:fetch-rates "2025-01-01"
What it does:
- Fetches exchange rates from CBU API for specified date
- Stores rates in
currency_ratestable - Updates existing rates if already present
Example output:
Fetching rates for date: 2025-01-25
Found 30 rates
Saved: USD (12705.00), EUR (13500.00), RUB (130.00), GBP (16200.00)...
Currency rates fetched successfully.
When to use:
- Daily to keep rates up-to-date
- To fetch historical rates
- After syncing currencies
⏰ Auto Synchronization
For production environments, automate currency updates using Laravel's Task Scheduler.
Laravel Scheduler
Add to app/Console/Kernel.php:
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
// Fetch rates daily at 10:00 AM (after CBU updates)
$schedule->command('cbu:fetch-rates')
->dailyAt('10:00')
->onFailure(function () {
// Notify admin if sync fails
});
// Sync currencies every Monday at 9:00 AM
$schedule->command('cbu:sync-currencies')
->weekly()
->mondays()
->at('09:00');
}
}
Activate Scheduler
Ensure the Laravel scheduler is running by adding this to your cron:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Alternative: Direct Cron Jobs
If not using Laravel Scheduler, add cron jobs directly:
# Fetch rates daily at 10:00 AM
0 10 * * * cd /path-to-your-project && php artisan cbu:fetch-rates
# Sync currencies every Monday at 9:00 AM
0 9 * * 1 cd /path-to-your-project && php artisan cbu:sync-currencies
Docker/Kubernetes
For containerized environments, use supervisor or similar:
[program:cbu-scheduler]
command=/usr/bin/php /var/www/html/artisan schedule:work
autostart=true
autorestart=true
🗄️ Database Structure
Currencies Table
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| ccy | string | Currency code (unique) - USD, EUR, RUB |
| name_uz | string | Name in Uzbek - AQSH dollari |
| name_oz | string | Name in Uzbek (Cyrillic) - АҚШ доллари |
| name_ru | string | Name in Russian - Доллар США |
| name_en | string | Name in English - US Dollar |
| code | string | Numeric code - 840 |
| cbu_id | string | CBU identifier |
| created_at | timestamp | Creation time |
| updated_at | timestamp | Update time |
Currency Rates Table
| Column | Type | Description |
|---|---|---|
| id | bigint | Primary key |
| currency_id | bigint | Foreign key to currencies |
| date | date | Rate date (indexed) |
| currency_date | date | Original CBU date |
| rate | decimal(15,4) | Exchange rate - 12705.0000 |
| diff | decimal(15,4) | Difference from previous - 15.2500 |
| nominal | integer | Nominal value - 1 |
| created_at | timestamp | Creation time |
| updated_at | timestamp | Update time |
Indexes:
date- For fast lookups['currency_id', 'date']- Composite index
Unique constraint: ['currency_id', 'date'] - Only one rate per currency per day
🧪 Testing
The package provides full test coverage using Pest PHP framework.
Running Tests
# All tests
composer test
# Unit tests only
vendor/bin/pest --testsuite=Unit
# Feature tests only
vendor/bin/pest --testsuite=Feature
# Verbose mode
vendor/bin/pest --verbose
For detailed testing documentation, see TESTING.md
🌐 API Endpoints
The package automatically registers RESTful API endpoints for accessing currency data.
Base URL: {your-domain}/api/cbu
1. Get All Currencies
GET /api/cbu/currencies
Response:
{
"msg": null,
"error": null,
"success": true,
"data": [
{
"ccy": "USD",
"name_uz": "AQSH dollari",
"name_en": "US Dollar",
"code": 840
},
{
"ccy": "EUR",
"name_uz": "EVRO",
"name_en": "Euro",
"code": 978
}
]
}
2. Get Currency Codes
GET /api/cbu/currencies/codes
Response:
{
"msg": null,
"error": null,
"success": true,
"data": ["USD", "EUR", "RUB", "GBP", "JPY", "CHF"]
}
3. Get Specific Currency
GET /api/cbu/currencies/{code}
Example:
GET /api/cbu/currencies/USD
Response:
{
"msg": null,
"error": null,
"success": true,
"data": {
"ccy": "USD",
"name_uz": "AQSH dollari",
"name_en": "US Dollar",
"name_ru": "Доллар США",
"code": 840
}
}
4. Get Today's Rates
GET /api/cbu/rates/today
Response:
{
"msg": null,
"error": null,
"success": true,
"data": [
{
"ccy": "USD",
"rate": 12705.00,
"diff": 15.25,
"date": "2025-11-09"
},
{
"ccy": "EUR",
"rate": 13500.00,
"diff": -10.50,
"date": "2025-11-09"
}
]
}
5. Get Rates by Date
GET /api/cbu/rates?date={date}
Query Parameters:
date(optional): Date inY-m-dformat (e.g.,2025-01-25)
Example:
GET /api/cbu/rates?date=2025-01-25
6. Get Specific Currency Rate
GET /api/cbu/rates/{code}?date={date}
Example:
GET /api/cbu/rates/USD?date=2025-01-15
Response:
{
"msg": null,
"error": null,
"success": true,
"data": {
"ccy": "USD",
"rate": 12705.00,
"diff": 15.25,
"nominal": 1,
"date": "2025-01-15"
}
}
7. Convert Currency
POST /api/cbu/convert
Content-Type: application/json
Request Body:
{
"amount": 100,
"from": "USD",
"to": "EUR",
"date": "2025-01-15"
}
Response:
{
"msg": null,
"error": null,
"success": true,
"data": {
"amount": 100,
"from_currency": "USD",
"to_currency": "EUR",
"result": 94.11,
"from_rate": 12705.00,
"to_rate": 13500.00,
"amount_in_uzs": 1270500.00,
"date": "2025-01-15"
}
}
Validation Rules:
amount- required, numeric, minimum 0.01from- required, valid 3-letter currency codeto- required, valid 3-letter currency codedate- optional, Y-m-d format, cannot be future date
8. Get Conversion Rate
GET /api/cbu/convert/rate/{from}/{to}?date={date}
Returns the conversion rate for 1 unit of source currency.
Example:
GET /api/cbu/convert/rate/USD/EUR?date=2025-01-15
Response:
{
"msg": null,
"error": null,
"success": true,
"data": {
"amount": 1,
"from_currency": "USD",
"to_currency": "EUR",
"result": 0.94,
"from_rate": 12705.00,
"to_rate": 13500.00,
"amount_in_uzs": 12705.00,
"date": "2025-01-15"
}
}
Error Responses
All endpoints return a consistent error format:
{
"msg": "Currency not found",
"error": "The currency code 'XYZ' does not exist",
"success": false,
"data": []
}
Common HTTP Status Codes:
200- Success400- Bad Request (validation failed)404- Not Found (currency or rate not found)500- Internal Server Error
Customizing API Routes
You can customize the API route prefix in config/cbu-currency.php:
'routes' => [
'prefix' => env('CBU_ROUTES_PREFIX', 'api/cbu'),
'middleware' => ['api'],
],
Or in .env:
CBU_ROUTES_PREFIX=api/v1/currency
📄 License
MIT License. See LICENSE file for details.
👨💻 Author
Nurbek Jummayev
- GitHub: @nurbekjummayev
- Email: jummayevnurbek279@gmail.com