3neti / cash
A package to enable assigning cash to Eloquent Models
Requires
- php: ^8.3
- 3neti/wallet: ^1.0 || ^2.0
- brick/money: ^0.10.1
- lorisleiva/laravel-actions: ^2.9
- propaganistas/laravel-phone: ^6.0
- spatie/laravel-data: ^4.16
- spatie/laravel-model-status: ^1.18
- spatie/laravel-tags: ^4.10
Requires (Dev)
- orchestra/testbench: ^10.3 || ^11.0
- pestphp/pest: ^3.8 || ^4.0
README
A lightweight Laravel package for representing monetary value as a first-class model, with support for:
- precise money handling (via Brick\Money)
- wallet integration (via Bavix Wallet)
- status lifecycle (via Spatie Model Status)
- tagging (via Spatie Tags)
- metadata and expiration handling
This package is designed as a supporting domain layer for the x-change ecosystem, particularly for voucher issuance, redemption, and disbursement workflows.
Supported platforms
- PHP 8.3 and 8.4
- Laravel 12 and 13
3neti/wallet1.x and 2.x
Laravel 12 and Laravel 13 are tested as separate compatibility lanes.
✨ Core Concept
Cash is a value container:
- it represents an amount of money
- it can be tagged, status-tracked, and expired
- it can be linked to any model via
reference - it integrates with wallet systems for transfer and settlement
It is not a ledger
It is not a wallet
It is a portable monetary unit used across workflows.
📦 Installation
composer require 3neti/cash:^1.3
⚙️ Configuration
Publish config (optional):
php artisan vendor:publish --tag=config
🧱 Database Migrations
This package uses:
loadMigrationsFrom()
So migrations are auto-loaded.
Run:
php artisan migrate
⚠️ Migration Prerequisites
This package depends on the following schema:
spatie/laravel-model-status→statusestablespatie/laravel-tags→tagstables3neti/wallet/bavix/laravel-wallet→ wallet tables
👉 These must be installed and migrated by the host application.
This package does NOT publish or manage those migrations.
🧠 Usage
Creating Cash
use LBHurtado\Cash\Models\Cash; $cash = Cash::create([ 'amount' => 1500.00, 'currency' => 'PHP', 'meta' => ['note' => 'Transport support'], ]);
Using Money Object
use Brick\Money\Money; $cash = Cash::create([ 'amount' => Money::of(1500, 'PHP'), 'currency' => 'PHP', ]);
Stored internally as minor units.
Accessing Amount
$cash->amount; // Brick\Money\Money instance $cash->amount->getAmount()->toFloat(); // 1500.00 $cash->amount->getMinorAmount()->toInt(); // 150000
Metadata
$cash->meta->note; $cash->meta['note'];
Expiration
$cash->expired = true; $cash->save(); $cash->expired; // true
Status Management
use LBHurtado\Cash\Enums\CashStatus; $cash->setStatus(CashStatus::DISBURSED); $cash->hasStatus(CashStatus::DISBURSED); $cash->getCurrentStatus();
Secret Protection
$cash->secret = '1234'; $cash->save(); $cash->verifySecret('1234'); // true
Redemption Check
$cash->canRedeem('1234');
Tags
$cash->attachTag('transport'); $cash->tags;
Wallet Integration
$cash->depositFloat(1000); $cash->withdrawFloat(500);
🧾 Data Transformation
Use CashData for API responses:
use LBHurtado\Cash\Data\CashData; CashData::fromModel($cash);
🧱 Schema
cash
- id
- amount (minor units)
- currency
- reference_type
- reference_id
- meta (json)
- secret (hashed)
- expires_on
- timestamps
🧩 Relationships
- morphTo:
reference - morphOne:
withdrawTransaction - statuses (Spatie)
- tags (Spatie)
- wallet (Bavix)
🧪 Testing
This package uses:
- Testbench
- in-memory SQLite
- test-only migrations under
tests/database/migrations
No vendor migrations are required for tests.
🧭 Architecture Role
In the x-change ecosystem:
cash= monetary payloadvoucher= contract/instructionwallet= balance + ledgerx-change= orchestration
🔒 Design Principles
- Money is always stored in minor units
- Currency is explicit
- Status is first-class
- Expiration is built-in
- Metadata is flexible
- Secrets are hashed
Withdrawal authorization
Cash withdrawal policy is exposed through container-resolved contracts for:
- amount bounds;
- withdrawal intervals;
- claimant authorization;
- vendor mandates;
- approval policy; and
- typed authorization decisions.
The default decision service reports whether a withdrawal is allowed or requires an independent approval. It does not dispatch a provider transfer or manufacture approval evidence. The integrating settlement system remains responsible for authenticating the officer, persisting authorization evidence, and invoking execution.
Resolve these services through Laravel's container so applications may replace policy implementations without changing Cash models.
🚀 Future Enhancements
- reconciliation support
- audit integration
- stricter immutability enforcement
- event hooks
🧾 License
Proprietary