moneyphp/money
moneyphp/money is a PHP value-object library for safe money handling without floats. Uses string-based big integers, supports arithmetic, allocation, currencies/ISO repositories, formatting (incl. intl), JSON serialization, and exchange rates. Requires BCMath.
## Technical Evaluation
### **Architecture Fit**
- **Domain-Driven Design (DDD) Alignment**: The `moneyphp/money` package is a near-perfect fit for Laravel applications requiring **domain-driven financial modeling** (e.g., e-commerce, billing, accounting). It enforces **immutable value objects** (Money pattern) and **strong typing** for currencies, aligning with Laravel’s **domain-layer purity** and **type safety** (PHP 8+).
- **Separation of Concerns**: The package’s **decoupled design** (e.g., `Converter`, `Formatter`, `Parser`) allows seamless integration into Laravel’s **service layer**, **repositories**, or **domain entities** without tight coupling.
- **Laravel Ecosystem Synergy**:
- Works natively with **Laravel’s Eloquent** (via `Money` as a **custom attribute type** or **accessor**).
- Compatible with **Laravel Cashier**, **Spatie’s Payment packages**, or **custom billing systems**.
- Supports **JSON serialization** (critical for APIs, caching, or database storage via `json` columns).
### **Integration Feasibility**
- **Low Friction**: Composer install (`composer require moneyphp/money`) + **BCMath extension** (enabled by default in most PHP stacks).
- **Database Compatibility**:
- Store as **string** (e.g., `"EUR 1000"` or `"1000.00 EUR"`) in **JSON fields** or **decimal columns** (with validation).
- Avoid `float`/`double` entirely (prevents rounding errors).
- **API/HTTP Layer**:
- Serializes to/from **JSON** (e.g., `{"amount": "1000", "currency": "EUR"}`).
- Integrates with **Laravel Sanctum/Passport** for secure money transfers.
### **Technical Risk**
| Risk Area | Mitigation Strategy |
|-------------------------|------------------------------------------------------------------------------------|
| **BCMath Dependency** | Verify server has `bcmath` enabled (common in Laravel deployments). Fallback to `gmp` if needed. |
| **PHP Version** | Requires **PHP 8.0+** (Laravel 9+). For older Laravel, use **v3.x** of the package. |
| **Floating-Point Rejection** | Enforce **string-based arithmetic** (e.g., `sprintf('%.2f', $float)` before conversion). |
| **Currency Exchange** | Use **`moneyphp/exchanger`** (optional) or integrate with **third-party APIs** (e.g., Open Exchange Rates). |
| **Performance** | Benchmark **allocation/arithmetic** in high-throughput systems (e.g., bulk payments). |
### **Key Questions for TPM**
1. **Domain Requirements**:
- Are **crypto-currencies** needed? (Supported via `CryptoCurrencies` class.)
- Do we need **multi-currency accounting** (e.g., `Money::allocate`)?
- Are **exchange rates** dynamic (API-driven) or static (hardcoded)?
2. **Data Storage**:
- Will money values be stored in **database columns**? If so, use `decimal` or `json`?
- Need **audit trails** for money changes? (Consider **Laravel Observers** or **Event Sourcing**.)
3. **API Contracts**:
- Should APIs return **raw amounts** (e.g., `"1000"`) or **formatted strings** (e.g., `"€10.00"`)?
- Need **localization** (e.g., `IntlLocalizedDecimalFormatter`)?
4. **Testing**:
- How to test **money arithmetic**? (Use `Money\Comparator` for assertions.)
- Mock **exchange rates** in unit tests (e.g., `FixedExchange`).
5. **Legacy Systems**:
- Any **float-based money** in existing code? (Plan migration to `Money` objects.)
- Need **backward compatibility** with old `Teller` class?
---
## Integration Approach
### **Stack Fit**
- **Laravel Core**:
- **Service Providers**: Register `Money` factories (e.g., `Money::EUR()`) as **app bindings**.
- **Helpers**: Create a `MoneyHelper` facade for common operations (e.g., `MoneyHelper::format($money)`).
- **Database**:
- **Eloquent Accessors/Mutators**:
```php
// Model: Invoice.php
protected $casts = [
'amount' => MoneyCast::class, // Custom cast to Money object
];
```
- **Migrations**: Use `decimal(20, 4)` for legacy systems (but **prefer JSON** for flexibility).
- **APIs**:
- **Request Validation**: Use `moneyphp/money` in **Form Requests** to validate currency/amount.
- **Responses**: Format money with `DecimalMoneyFormatter` or `IntlLocalizedDecimalFormatter`.
### **Migration Path**
1. **Phase 1: Core Integration**
- Replace all `float`/`int` money values with `Money` objects.
- Update **models**, **DTOs**, and **services** to use `Money` methods (e.g., `->add()`, `->allocate()`).
2. **Phase 2: Database Sync**
- Add **JSON columns** for money fields (e.g., `amount -> {"amount": "1000", "currency": "EUR"}`).
- Use **Eloquent casts** to auto-convert between `Money` and DB storage.
3. **Phase 3: API Layer**
- Standardize **request/response formats** (e.g., always return `{"amount": "1000", "currency": "EUR"}`).
- Add **localization middleware** for formatted outputs.
4. **Phase 4: Advanced Features**
- Integrate **exchange rates** (e.g., `moneyphp/exchanger` or custom API).
- Implement **money allocation** for split payments.
### **Compatibility**
| Component | Compatibility Notes |
|-------------------------|------------------------------------------------------------------------------------|
| **Laravel 9/10** | Full support (PHP 8.0+). Use **v4.x** of the package. |
| **Laravel 8** | Use **v3.x** (PHP 7.4+). Avoid PHP 8.0+ BC breaks. |
| **Doctrine ORM** | Works with **embeddables** (but watch for `code` vs. `name` BC in `Currency`). |
| **Redis/Memcached** | Serialize `Money` to JSON for caching. |
| **Queue Jobs** | Pass `Money` objects via **serializable DTOs** (avoid direct serialization). |
### **Sequencing**
1. **Start with a Single Module**:
- Pilot in **billing**, **payments**, or **inventory** (low-risk areas).
2. **Incremental Replacement**:
- Replace **one service/class** at a time (e.g., `OrderService` → use `Money` for totals).
3. **Test Exchange Flows**:
- Verify **currency conversion** and **allocation** in staging.
4. **Deprecate Legacy Code**:
- Add **deprecation warnings** for old `float`-based money methods.
5. **Roll Out to APIs**:
- Update **contracts** (OpenAPI/Swagger) to reflect `Money` types.
---
## Operational Impact
### **Maintenance**
- **Pros**:
- **No floating-point bugs** (e.g., `0.1 + 0.2 !== 0.3`).
- **Self-documenting code** (e.g., `Money::USD(100)` > `100.00`).
- **Built-in validation** (e.g., invalid currency codes throw exceptions).
- **Cons**:
- **Learning curve** for team (immutable objects, string arithmetic).
- **Exchange rate management** requires external systems (if dynamic).
- **Tooling**:
- Use **Psalm/PHPStan** for static analysis of `Money` types.
- **CI checks**: Validate all money operations in tests.
### **Support**
- **Common Issues**:
- **Currency mismatches** (e.g., `Money::EUR(100)->add(Money::USD(50))` → throws exception).
- **Serialization errors** (ensure JSON keys match `amount`/`currency`).
- **Exchange rate failures** (handle API timeouts gracefully).
- **Debugging**:
- Use `Money::getAmount()` and `Money::getCurrency()` for logging.
- **Comparator** for testing equality: `assert($money1->equals($money2))`.
- **Documentation**:
- Link to **[moneyphp.org](http://moneyphp.org)** for team onboarding.
- Create **internal cheat sheets** for common operations (e.g., allocation, formatting).
### **Scaling**
- **Performance**:
- **Arithmetic**: `Money` operations are **O(1)** (no loops). Benchmark in high-load scenarios.
- **Exchange Rates**: Cache rates in **Redis** (e
How can I help you explore Laravel packages today?