nurbekjummayev / cbu-currency
Central Bank of Uzbekistan currency rates package for Laravel
Requires
- php: ^8.1
- ext-bcmath: *
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- nurbekjummayev/laravel-api-response-helpers: ^1.2
Requires (Dev)
- laravel/mcp: ^0.8.1
- laravel/pint: ^1.13
- mockery/mockery: ^1.5
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.0|^3.0|^4.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0|^4.0
Suggests
- laravel/mcp: Required to expose the CBU Currency MCP server (AI agent tools) (^0.8).
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.5
- Laravel ^10.0|^11.0|^12.0|^13.0
- BCMath PHP Extension
- GuzzleHTTP ^7.0
Note: Laravel 11/12 require PHP >= 8.2, and Laravel 13 requires PHP >= 8.3. PHP 8.1 works with Laravel 10 only.
🚀 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'), // Decimal places for the FINAL conversion result (default: 0 = no // rounding, full computed precision). Internal calculations always run // at full precision and are never rounded mid-calculation. Set a // positive value or use ->scale(n) per call to round the final result // half-up, or round the returned result yourself with ->round(n). 'scale' => env('CBU_SCALE', 0), // Data source: 'database' or 'api' 'source' => env('CBU_SOURCE', 'database'), // Enable/disable logging 'log_enabled' => env('CBU_LOG_ENABLED', true), // API routes configuration 'routes' => [ 'enabled' => env('CBU_ROUTES_ENABLED', true), 'prefix' => env('CBU_ROUTES_PREFIX', 'api/currency'), '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=0 CBU_SOURCE=database CBU_LOG_ENABLED=true CBU_ROUTES_ENABLED=true CBU_ROUTES_PREFIX=api/currency
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",
"scale": 2
}
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 datescale- optional, integer 0–20; when sent, the final result is rounded to that many decimals, otherwise full precision is returned
8. Get Conversion Rate
GET /api/cbu/convert/rate/{from}/{to}?date={date}&scale={scale}
Returns the conversion rate for 1 unit of source currency. The optional scale query parameter (0–20) rounds the final result; without it the full-precision value is returned.
Example:
GET /api/cbu/convert/rate/USD/EUR?date=2025-01-15&scale=4
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 enable/disable package routes and customize the route prefix in config/cbu-currency.php:
'routes' => [ 'enabled' => env('CBU_ROUTES_ENABLED', true), 'prefix' => env('CBU_ROUTES_PREFIX', 'api/currency'), 'middleware' => ['api'], ],
Or in .env:
CBU_ROUTES_ENABLED=true CBU_ROUTES_PREFIX=api/v1/currency
If you only use the package via the CbuCurrency facade and don't need the built-in HTTP endpoints, disable them entirely:
CBU_ROUTES_ENABLED=false
🤖 AI / Laravel Boost Integration
This package ships with Laravel Boost AI guidelines and an agent skill (cbu-currency-development). If your project uses Boost, run:
php artisan boost:install
Boost will automatically detect this package and offer to install its guidelines and skill, so AI agents (Claude Code, Cursor, Codex, etc.) know how to use the CbuCurrency facade, builders, sync commands, and API endpoints correctly. To pick up the skill after adding this package to an existing Boost setup, run php artisan boost:update --discover.
MCP Server (AI Agent Tools)
The package ships an MCP (Model Context Protocol) server that lets AI agents call currency tools directly. It activates automatically when the optional laravel/mcp package is installed:
composer require laravel/mcp
Available tools:
| Tool | Description |
|---|---|
get-rates |
All CBU rates for a date |
get-rate |
Single currency rate by code and date |
convert-currency |
Convert between currencies (optional scale for rounding) |
list-currencies |
Currency metadata with multilingual names |
sync-rates |
Sync rates from the CBU API into the database |
The local (stdio) server is registered as cbu-currency. Connect it to your AI agent, e.g. for Claude Code:
claude mcp add cbu-currency -- php artisan mcp:start cbu-currency
Test it with the MCP Inspector:
php artisan mcp:inspector cbu-currency
Configuration (config/cbu-currency.php):
'mcp' => [ 'enabled' => env('CBU_MCP_ENABLED', true), 'name' => env('CBU_MCP_NAME', 'cbu-currency'), 'web' => [ 'enabled' => env('CBU_MCP_WEB_ENABLED', false), // opt-in HTTP server 'path' => env('CBU_MCP_WEB_PATH', '/mcp/cbu-currency'), 'middleware' => ['api'], ], ],
📄 License
MIT License. See LICENSE file for details.
👨💻 Author
Nurbek Jummayev
- GitHub: @nurbekjummayev
- Email: jummayevnurbek279@gmail.com