PHPackages                             azaharizaman/nexus-currency - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. azaharizaman/nexus-currency

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

azaharizaman/nexus-currency
===========================

Framework-agnostic ISO 4217-compliant currency management and exchange rate engine for the Nexus ERP system

v0.1.0-alpha1(1mo ago)025MITPHPPHP ^8.3

Since May 5Pushed 1mo agoCompare

[ Source](https://github.com/azaharizaman/nexus-currency)[ Packagist](https://packagist.org/packages/azaharizaman/nexus-currency)[ RSS](/packages/azaharizaman-nexus-currency/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (3)Versions (2)Used By (5)

Nexus\\Currency
===============

[](#nexuscurrency)

[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](https://opensource.org/licenses/MIT)[![PHP Version](https://camo.githubusercontent.com/c8d8dad6beb757a2b8acba331d16140813699543b88a37af0a81f20bd35f61de/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e332532422d626c7565)](https://php.net)

Framework-agnostic ISO 4217-compliant currency management and exchange rate engine for the Nexus ERP system.

Overview
--------

[](#overview)

The **Nexus\\Currency** package provides authoritative currency metadata and exchange rate management following ISO 4217 standards. It complements the existing `Nexus\Finance\ValueObjects\Money` implementation by providing validation, formatting, and exchange rate coordination without replacing core financial value objects.

Key Features
------------

[](#key-features)

- **ISO 4217 Compliance**: Full support for international currency standards
- **Decimal Precision Rules**: Correct handling of 0-decimal (JPY), 2-decimal (USD), and 3-decimal (BHD) currencies
- **Exchange Rate Management**: Pluggable provider architecture for external rate APIs
- **Intelligent Caching**: Configurable rate storage to minimize API calls
- **Framework-Agnostic**: Pure PHP with no Laravel dependencies
- **BCMath Compatible**: Designed to work with high-precision monetary calculations
- **Stateless Design**: All services are stateless and horizontally scalable

Architecture
------------

[](#architecture)

This package follows the **"Logic in Packages, Implementation in Applications"** pattern:

```
┌─────────────────────────────────────────────────────────────┐
│                    Nexus\Currency                           │
│  (ISO 4217 Authority & Exchange Rate Coordination)          │
└────────────────────┬────────────────────────────────────────┘
                     │ provides metadata
                     ↓
┌─────────────────────────────────────────────────────────────┐
│                    Nexus\Finance                            │
│         (Money & ExchangeRate Value Objects)                │
└────────────────────┬────────────────────────────────────────┘
                     │ used by
                     ↓
┌─────────────────────────────────────────────────────────────┐
│  Nexus\Accounting, Nexus\Payroll, Nexus\Procurement, etc.  │
└─────────────────────────────────────────────────────────────┘

```

### Integration with Nexus\\Finance

[](#integration-with-nexusfinance)

The `Nexus\Currency` package **augments** (not replaces) existing financial components:

- **`Nexus\Finance\ValueObjects\Money`** remains the core monetary value object
- **`Nexus\Finance\ValueObjects\ExchangeRate`** remains the core rate value object
- **`Nexus\Currency`** provides:
    - Currency metadata (symbols, names, decimal rules)
    - Currency code validation
    - Exchange rate provider coordination
    - Formatting utilities

### What This Package Provides

[](#what-this-package-provides)

- **`CurrencyManager`**: High-level currency operations (validation, formatting, metadata)
- **`ExchangeRateService`**: Exchange rate lookup with caching and conversion
- **Value Objects**: `Currency` (metadata), `CurrencyPair` (pair representation)
- **Contracts**: Repository, Provider, and Storage interfaces
- **Exceptions**: Domain-specific errors with static factories

### What the Application Must Implement

[](#what-the-application-must-implement)

The consuming application (`Nexus\Atomy`) must provide:

1. **Currency Repository**: Database-backed implementation with ISO 4217 seed data
2. **Exchange Rate Provider**: Integration with external APIs (ECB, Fixer.io, etc.) using `Nexus\Connector`
3. **Rate Storage**: Redis/Database caching implementation
4. **Service Bindings**: IoC container bindings in service provider

Installation
------------

[](#installation)

```
composer require azaharizaman/nexus-currency:"*@dev"
```

Requirements
------------

[](#requirements)

- PHP 8.3 or higher
- BCMath extension (for precision calculations)
- PSR-3 Logger implementation (optional)

Core Components
---------------

[](#core-components)

### 1. Currency Value Object

[](#1-currency-value-object)

Immutable representation of ISO 4217 currency metadata:

```
use Nexus\Currency\ValueObjects\Currency;

// Currency with full ISO 4217 data
$usd = new Currency(
    code: 'USD',
    name: 'US Dollar',
    symbol: '$',
    decimalPlaces: 2,
    numericCode: '840'
);

// Access metadata
echo $usd->getCode();          // "USD"
echo $usd->getSymbol();        // "$"
echo $usd->getDecimalPlaces(); // 2

// Format amounts
echo $usd->formatAmount('1234.56');              // "$ 1,234.56"
echo $usd->formatAmount('1234.56', false, true); // "1,234.56 USD"

// Check decimal type
$jpy = new Currency('JPY', 'Japanese Yen', '¥', 0, '392');
$jpy->isZeroDecimal(); // true
```

### 2. CurrencyPair Value Object

[](#2-currencypair-value-object)

Represents a currency exchange pair:

```
use Nexus\Currency\ValueObjects\CurrencyPair;

// Create pair
$pair = new CurrencyPair('USD', 'EUR');

// Or from string notation
$pair = CurrencyPair::fromString('USD/EUR');

// Access components
echo $pair->getFromCode(); // "USD"
echo $pair->getToCode();   // "EUR"
echo $pair->toString();    // "USD/EUR"

// Get inverse
$inverse = $pair->inverse(); // EUR/USD
```

### 3. CurrencyManager Service

[](#3-currencymanager-service)

High-level currency management:

```
use Nexus\Currency\Services\CurrencyManager;

$manager = app(CurrencyManager::class);

// Get currency metadata
$usd = $manager->getCurrency('USD');

// Validate currency code
$manager->validateCode('USD'); // void (success)
$manager->validateCode('XXX'); // throws CurrencyNotFoundException

// Check existence
$manager->exists('EUR'); // true
$manager->exists('ZZZ'); // false

// Get decimal precision
$precision = $manager->getDecimalPrecision('JPY'); // 0

// Format amounts
$formatted = $manager->formatAmount('1234.5678', 'USD');
// "$ 1,234.57" (rounded to 2 decimals)

// Get all currencies
$all = $manager->getAllCurrencies();

// Search currencies
$results = $manager->searchCurrencies('dollar');
```

### 4. ExchangeRateService

[](#4-exchangerateservice)

Exchange rate lookup and conversion:

```
use Nexus\Currency\Services\ExchangeRateService;
use Nexus\Currency\ValueObjects\CurrencyPair;
use Nexus\Finance\ValueObjects\Money;

$service = app(ExchangeRateService::class);

// Get current exchange rate
$pair = new CurrencyPair('USD', 'EUR');
$rate = $service->getRate($pair); // Returns Nexus\Finance\ValueObjects\ExchangeRate

// Get historical rate
$date = new DateTimeImmutable('2024-01-15');
$historicalRate = $service->getRate($pair, $date);

// Convert money
$usd = Money::of(100, 'USD');
$eur = $service->convert($usd, 'EUR'); // Money in EUR

// Get multiple rates (batch operation)
$pairs = [
    new CurrencyPair('USD', 'EUR'),
    new CurrencyPair('USD', 'GBP'),
    new CurrencyPair('USD', 'JPY'),
];
$rates = $service->getRates($pairs);

// Refresh specific rates (bypass cache)
$service->refreshRates($pairs);

// Clear all cached rates
$service->clearCache();

// Check provider capabilities
$service->supportsHistoricalRates(); // true/false
$service->getProviderName();         // "ECB" or "Fixer.io"
$service->isProviderAvailable();     // true/false
```

Application Integration
-----------------------

[](#application-integration)

### Step 1: Implement Currency Repository

[](#step-1-implement-currency-repository)

Create a database-backed repository in `apps/Atomy`:

```
namespace App\Repositories;

use Nexus\Currency\Contracts\CurrencyRepositoryInterface;
use Nexus\Currency\ValueObjects\Currency;
use Illuminate\Support\Facades\DB;

class DbCurrencyRepository implements CurrencyRepositoryInterface
{
    public function findByCode(string $code): ?Currency
    {
        $row = DB::table('currencies')
            ->where('code', $code)
            ->where('is_active', true)
            ->first();

        if (!$row) {
            return null;
        }

        return new Currency(
            code: $row->code,
            name: $row->name,
            symbol: $row->symbol,
            decimalPlaces: $row->decimal_places,
            numericCode: $row->numeric_code
        );
    }

    public function getAll(): array
    {
        $rows = DB::table('currencies')
            ->where('is_active', true)
            ->get();

        $currencies = [];
        foreach ($rows as $row) {
            $currencies[$row->code] = new Currency(
                code: $row->code,
                name: $row->name,
                symbol: $row->symbol,
                decimalPlaces: $row->decimal_places,
                numericCode: $row->numeric_code
            );
        }

        return $currencies;
    }

    public function exists(string $code): bool
    {
        return DB::table('currencies')
            ->where('code', $code)
            ->where('is_active', true)
            ->exists();
    }

    // Implement other methods...
}
```

### Step 2: Create Database Migration

[](#step-2-create-database-migration)

```
// database/migrations/xxxx_create_currencies_table.php
Schema::create('currencies', function (Blueprint $table) {
    $table->string('code', 3)->primary();
    $table->string('name', 100);
    $table->string('symbol', 10);
    $table->tinyInteger('decimal_places')->default(2);
    $table->string('numeric_code', 3);
    $table->boolean('is_active')->default(true);
    $table->timestamps();
});
```

### Step 3: Seed ISO 4217 Data

[](#step-3-seed-iso-4217-data)

```
// database/seeders/CurrencySeeder.php
DB::table('currencies')->insert([
    ['code' => 'USD', 'name' => 'US Dollar', 'symbol' => '$', 'decimal_places' => 2, 'numeric_code' => '840'],
    ['code' => 'EUR', 'name' => 'Euro', 'symbol' => '€', 'decimal_places' => 2, 'numeric_code' => '978'],
    ['code' => 'GBP', 'name' => 'British Pound', 'symbol' => '£', 'decimal_places' => 2, 'numeric_code' => '826'],
    ['code' => 'JPY', 'name' => 'Japanese Yen', 'symbol' => '¥', 'decimal_places' => 0, 'numeric_code' => '392'],
    ['code' => 'MYR', 'name' => 'Malaysian Ringgit', 'symbol' => 'RM', 'decimal_places' => 2, 'numeric_code' => '458'],
    ['code' => 'SGD', 'name' => 'Singapore Dollar', 'symbol' => 'S$', 'decimal_places' => 2, 'numeric_code' => '702'],
    ['code' => 'CNY', 'name' => 'Chinese Yuan', 'symbol' => '¥', 'decimal_places' => 2, 'numeric_code' => '156'],
    ['code' => 'BHD', 'name' => 'Bahraini Dinar', 'symbol' => 'BD', 'decimal_places' => 3, 'numeric_code' => '048'],
    // Add more currencies as needed...
]);
```

### Step 4: Implement Exchange Rate Provider

[](#step-4-implement-exchange-rate-provider)

Using `Nexus\Connector` for resilient API integration:

```
namespace App\Services\ExchangeRates;

use Nexus\Currency\Contracts\ExchangeRateProviderInterface;
use Nexus\Currency\ValueObjects\CurrencyPair;
use Nexus\Finance\ValueObjects\ExchangeRate;
use Nexus\Connector\Services\ConnectorManager;
use DateTimeImmutable;

class EcbExchangeRateProvider implements ExchangeRateProviderInterface
{
    public function __construct(
        private readonly ConnectorManager $connector
    ) {}

    public function getRate(CurrencyPair $pair, ?DateTimeImmutable $asOf = null): ExchangeRate
    {
        // Use Nexus\Connector with circuit breaker and retry logic
        $response = $this->connector->execute(
            connectionId: 'ecb-api',
            method: 'GET',
            endpoint: $asOf
                ? "/history/{$asOf->format('Y-m-d')}"
                : '/latest',
            params: [
                'base' => $pair->getFromCode(),
                'symbols' => $pair->getToCode(),
            ]
        );

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

        if (!isset($data['rates'][$pair->getToCode()])) {
            throw ExchangeRateNotFoundException::forPair($pair, $asOf);
        }

        return ExchangeRate::create(
            fromCurrency: $pair->getFromCode(),
            toCurrency: $pair->getToCode(),
            rate: $data['rates'][$pair->getToCode()],
            effectiveDate: $asOf ?? new DateTimeImmutable()
        );
    }

    public function supportsHistoricalRates(): bool
    {
        return true;
    }

    public function getProviderName(): string
    {
        return 'European Central Bank';
    }

    public function isAvailable(): bool
    {
        return $this->connector->isHealthy('ecb-api');
    }

    // Implement getRates()...
}
```

### Step 5: Implement Rate Storage (Redis)

[](#step-5-implement-rate-storage-redis)

```
namespace App\Services\ExchangeRates;

use Nexus\Currency\Contracts\RateStorageInterface;
use Nexus\Currency\ValueObjects\CurrencyPair;
use Nexus\Finance\ValueObjects\ExchangeRate;
use Illuminate\Support\Facades\Redis;
use DateTimeImmutable;

class RedisRateStorage implements RateStorageInterface
{
    private const PREFIX = 'exchange_rate:';

    public function get(CurrencyPair $pair, ?DateTimeImmutable $asOf = null): ?ExchangeRate
    {
        $key = $this->buildKey($pair, $asOf);
        $data = Redis::get($key);

        if (!$data) {
            return null;
        }

        $decoded = json_decode($data, true);

        return ExchangeRate::create(
            fromCurrency: $decoded['from'],
            toCurrency: $decoded['to'],
            rate: $decoded['rate'],
            effectiveDate: new DateTimeImmutable($decoded['date'])
        );
    }

    public function put(CurrencyPair $pair, ExchangeRate $rate, int $ttl = 3600): bool
    {
        $key = $this->buildKey($pair, $rate->getEffectiveDate());
        $data = json_encode([
            'from' => $rate->getFromCurrency(),
            'to' => $rate->getToCurrency(),
            'rate' => $rate->getRate(),
            'date' => $rate->getEffectiveDate()->format('Y-m-d H:i:s'),
        ]);

        return Redis::setex($key, $ttl, $data);
    }

    public function forget(CurrencyPair $pair, ?DateTimeImmutable $asOf = null): bool
    {
        $key = $this->buildKey($pair, $asOf);
        return Redis::del($key) > 0;
    }

    public function flush(): bool
    {
        $keys = Redis::keys(self::PREFIX . '*');
        return count($keys) > 0 ? Redis::del($keys) > 0 : true;
    }

    public function has(CurrencyPair $pair, ?DateTimeImmutable $asOf = null): bool
    {
        $key = $this->buildKey($pair, $asOf);
        return Redis::exists($key) > 0;
    }

    private function buildKey(CurrencyPair $pair, ?DateTimeImmutable $asOf): string
    {
        $dateStr = $asOf ? $asOf->format('Y-m-d') : 'current';
        return self::PREFIX . $pair->toString() . ':' . $dateStr;
    }
}
```

### Step 6: Bind Implementations in Service Provider

[](#step-6-bind-implementations-in-service-provider)

```
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Nexus\Currency\Contracts\CurrencyRepositoryInterface;
use Nexus\Currency\Contracts\ExchangeRateProviderInterface;
use Nexus\Currency\Contracts\RateStorageInterface;
use Nexus\Currency\Contracts\CurrencyManagerInterface;
use Nexus\Currency\Services\CurrencyManager;
use Nexus\Currency\Services\ExchangeRateService;
use App\Repositories\DbCurrencyRepository;
use App\Services\ExchangeRates\EcbExchangeRateProvider;
use App\Services\ExchangeRates\RedisRateStorage;

class CurrencyServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Bind repository
        $this->app->singleton(
            CurrencyRepositoryInterface::class,
            DbCurrencyRepository::class
        );

        // Bind exchange rate provider
        $this->app->singleton(
            ExchangeRateProviderInterface::class,
            EcbExchangeRateProvider::class
        );

        // Bind rate storage
        $this->app->singleton(
            RateStorageInterface::class,
            RedisRateStorage::class
        );

        // Bind currency manager
        $this->app->singleton(
            CurrencyManagerInterface::class,
            CurrencyManager::class
        );

        // Bind exchange rate service
        $this->app->singleton(ExchangeRateService::class);
    }
}
```

Integration with Nexus\\Finance
-------------------------------

[](#integration-with-nexusfinance-1)

### Enhancing Money Validation

[](#enhancing-money-validation)

Update `Nexus\Finance\ValueObjects\Money` to use `CurrencyManager`:

```
// In Nexus\Finance\ValueObjects\Money
use Nexus\Currency\Contracts\CurrencyManagerInterface;

private function validateCurrency(string $currency): void
{
    // Get CurrencyManager from container (or inject if refactoring to service)
    $currencyManager = app(CurrencyManagerInterface::class);

    // Delegate validation to Currency package
    try {
        $currencyManager->validateCode($currency);
    } catch (\Nexus\Currency\Exceptions\InvalidCurrencyCodeException $e) {
        throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
    } catch (\Nexus\Currency\Exceptions\CurrencyNotFoundException $e) {
        throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
    }
}
```

### Currency-Aware Formatting

[](#currency-aware-formatting)

Add a formatting method to `Money` that uses currency metadata:

```
// In Nexus\Finance\ValueObjects\Money
public function formatWithCurrency(CurrencyManagerInterface $currencyManager): string
{
    return $currencyManager->formatAmount(
        amount: $this->amount,
        currencyCode: $this->currency,
        includeSymbol: true,
        includeCode: false
    );
}
```

Usage:

```
$money = Money::of(1234.5678, 'USD');
$currencyManager = app(CurrencyManagerInterface::class);

echo $money->formatWithCurrency($currencyManager); // "$ 1,234.57"

$jpy = Money::of(1234.5678, 'JPY');
echo $jpy->formatWithCurrency($currencyManager); // "¥ 1,235" (zero decimals)
```

Decimal Precision Strategy
--------------------------

[](#decimal-precision-strategy)

### The 4-Decimal Internal Standard

[](#the-4-decimal-internal-standard)

`Nexus\Finance\ValueObjects\Money` uses **4 decimal places** for all internal BCMath calculations:

```
// In Money VO
private const PRECISION = 4;
```

**Why 4 decimals internally?**

- Prevents rounding errors in complex calculations (tax, interest, multi-step conversions)
- Allows accurate intermediate results for financial operations
- Industry standard for accounting systems

### Currency-Specific Display Precision

[](#currency-specific-display-precision)

`Nexus\Currency` provides the **correct display precision** per ISO 4217:

```
$usd = $currencyManager->getCurrency('USD');
$usd->getDecimalPlaces(); // 2

$jpy = $currencyManager->getCurrency('JPY');
$jpy->getDecimalPlaces(); // 0

$bhd = $currencyManager->getCurrency('BHD');
$bhd->getDecimalPlaces(); // 3 (Bahraini Dinar)
```

### Best Practice: Calculate at 4, Display per Currency

[](#best-practice-calculate-at-4-display-per-currency)

```
// Internal calculation (4 decimals)
$subtotal = Money::of(100.00, 'USD');
$tax = $subtotal->multiply(0.06); // 6.0000
$total = $subtotal->add($tax);    // 106.0000

// Display with currency-specific precision (2 decimals for USD)
$formatted = $total->formatWithCurrency($currencyManager);
// Result: "$ 106.00"

// For JPY (0 decimals)
$jpyTotal = Money::of(1234.5678, 'JPY');
$formatted = $jpyTotal->formatWithCurrency($currencyManager);
// Result: "¥ 1,235" (rounded, no decimals)
```

Error Handling
--------------

[](#error-handling)

All exceptions provide static factory methods for contextual error messages:

```
use Nexus\Currency\Exceptions\CurrencyNotFoundException;
use Nexus\Currency\Exceptions\InvalidCurrencyCodeException;
use Nexus\Currency\Exceptions\ExchangeRateNotFoundException;
use Nexus\Currency\Exceptions\ExchangeRateProviderException;

try {
    $currency = $manager->getCurrency('XXX');
} catch (CurrencyNotFoundException $e) {
    // "Currency with code 'XXX' not found. Ensure it exists in the currency repository."
}

try {
    $manager->validateCode('12');
} catch (InvalidCurrencyCodeException $e) {
    // "Currency code must be 3 characters, got 2: '12'"
}

try {
    $rate = $service->getRate(new CurrencyPair('USD', 'FAKE'));
} catch (ExchangeRateNotFoundException $e) {
    // "Exchange rate not found for currency pair USD/FAKE..."
}

try {
    $rate = $service->getRate(new CurrencyPair('USD', 'EUR'));
} catch (ExchangeRateProviderException $e) {
    // "Exchange rate provider 'ECB' API failed. Please try again later."
}
```

Testing
-------

[](#testing)

### Unit Testing with In-Memory Repository

[](#unit-testing-with-in-memory-repository)

For package unit tests, create a simple in-memory implementation:

```
use Nexus\Currency\Contracts\CurrencyRepositoryInterface;
use Nexus\Currency\ValueObjects\Currency;

class InMemoryCurrencyRepository implements CurrencyRepositoryInterface
{
    private array $currencies = [];

    public function __construct()
    {
        // Seed with common currencies for testing
        $this->currencies = [
            'USD' => new Currency('USD', 'US Dollar', '$', 2, '840'),
            'EUR' => new Currency('EUR', 'Euro', '€', 2, '978'),
            'JPY' => new Currency('JPY', 'Japanese Yen', '¥', 0, '392'),
            'MYR' => new Currency('MYR', 'Malaysian Ringgit', 'RM', 2, '458'),
        ];
    }

    public function findByCode(string $code): ?Currency
    {
        return $this->currencies[$code] ?? null;
    }

    public function getAll(): array
    {
        return $this->currencies;
    }

    public function exists(string $code): bool
    {
        return isset($this->currencies[$code]);
    }

    // Implement other methods...
}
```

Documentation
-------------

[](#documentation)

### 📚 Complete Documentation

[](#-complete-documentation)

- **[Getting Started Guide](docs/getting-started.md)** - Quick start, installation, and basic configuration
- **[API Reference](docs/api-reference.md)** - Complete interface and method documentation
- **[Integration Guide](docs/integration-guide.md)** - Laravel, Symfony, and custom framework integration
- **[Code Examples](docs/examples/)** - Practical usage examples
    - [Basic Usage](docs/examples/basic-usage.php) - Currency validation, formatting, and retrieval
    - [Advanced Usage](docs/examples/advanced-usage.php) - Exchange rates, conversion, and caching
- **[Requirements](REQUIREMENTS.md)** - Detailed functional and architectural requirements
- **[Implementation Summary](IMPLEMENTATION_SUMMARY.md)** - Development progress and metrics
- **[Test Suite Summary](TEST_SUITE_SUMMARY.md)** - Testing strategy and coverage
- **[Valuation Matrix](VALUATION_MATRIX.md)** - Package valuation and ROI metrics

### 🔗 Quick Links

[](#-quick-links)

DocumentationDescription[Prerequisites](docs/getting-started.md#prerequisites)System requirements and dependencies[Core Concepts](docs/getting-started.md#core-concepts)ISO 4217, non-breaking augmentation, caching[Configuration Steps](docs/getting-started.md#configuration-steps)Complete setup guide[Laravel Integration](docs/integration-guide.md#laravel-integration)Laravel migrations, repositories, service providers[Symfony Integration](docs/integration-guide.md#symfony-integration)Symfony entities, services, configuration[Testing Examples](docs/integration-guide.md#testing-your-integration)Unit and integration test examples[Troubleshooting](docs/getting-started.md#troubleshooting)Common issues and solutions---

License
-------

[](#license)

MIT License - see [LICENSE](LICENSE) file for details.

Credits
-------

[](#credits)

Developed by the Nexus Development Team for the Nexus ERP System.

Related Packages
----------------

[](#related-packages)

- **[Nexus\\Finance](../Finance/)** - Core financial value objects (Money, ExchangeRate)
- **[Nexus\\Accounting](../Accounting/)** - Multi-currency financial statements
- **[Nexus\\Connector](../Connector/)** - Resilient API integration with circuit breaker
- **[Nexus\\Tenant](../Tenant/)** - Multi-tenancy with per-tenant base currency

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance93

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor1

Top contributor holds 76.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

36d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/117408?v=4)[Azahari Zaman](/maintainers/azaharizaman)[@azaharizaman](https://github.com/azaharizaman)

---

Top Contributors

[![azaharizaman](https://avatars.githubusercontent.com/u/117408?v=4)](https://github.com/azaharizaman "azaharizaman (462 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (139 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/azaharizaman-nexus-currency/health.svg)

```
[![Health](https://phpackages.com/badges/azaharizaman-nexus-currency/health.svg)](https://phpackages.com/packages/azaharizaman-nexus-currency)
```

###  Alternatives

[symfony/lock

Creates and manages locks, a mechanism to provide exclusive access to a shared resource

515135.1M619](/packages/symfony-lock)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.6k38.2k](/packages/matomo-matomo)[phpro/soap-client

A general purpose SoapClient library

8955.9M52](/packages/phpro-soap-client)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

562565.8k41](/packages/ecotone-ecotone)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

744284.3k34](/packages/civicrm-civicrm-core)[illuminate/broadcasting

The Illuminate Broadcasting package.

7126.9M199](/packages/illuminate-broadcasting)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
