Financial transactions are rarely simple. A single payment can involve gateway fees, taxes, refunds, and chargebacks — each with its own rules and dependencies. Cashflow gives you a structured, type-safe way to manage all of this in Laravel.
The Problem
Most apps start with a simple transactions table. But as the business grows, you need:
- Multiple line items per transaction (payment, fee, tax, refund)
- Derived amounts (gateway fee is 2.9% of the payment)
- Dependency rules (can't refund without a payment)
- Uniqueness constraints (only one gateway fee per order)
- Atomic batches (all items saved together or not at all)
Cashflow solves all of these with a clean, PHP-native approach.
Define Your Balance Items
Instead of storing raw numbers in a generic table, you define typed PHP classes for each balance component:
use Laratusk\Cashflow\Contracts\BalanceItem;
use Laratusk\Cashflow\Attributes\Unique;
use Laratusk\Cashflow\Attributes\Rule;
use Laratusk\Cashflow\Enums\Direction;
#[Unique]
final class GatewayFee extends BalanceItem
{
public function __construct(
#[Rule('nullable', 'integer', 'min:1')]
private readonly ?int $amount = null,
#[Rule('nullable', 'numeric', 'gt:0', 'max:100')]
private readonly ?float $percentage = null,
) {}
public function amount(): int
{
if ($this->amount !== null) {
return $this->amount;
}
return (int) ceil(
$this->balance()->amountOf(Payment::class) * $this->percentage / 100
);
}
public function direction(): Direction
{
return Direction::Debit;
}
}Fluent Balance API
Building a transaction is straightforward:
use Laratusk\Cashflow\Balance;
$balance = Balance::for($account)
->reference($order)
->currency('USD');
$balance->insert(new Payment(amount: 10000));
$balance->insert(new GatewayFee(percentage: 2.9));
$balance->insert(new SalesTax(taxRate: 8.0));
$balance->save();All items are saved in a single database transaction with a shared batch_id UUID for easy auditing.
Querying Balances
Retrieve and inspect balances with a clean API:
$balance = Balance::for($account)->reference($order)->get();
$balance->amountOf(Payment::class); // 10000
$balance->amountOf(GatewayFee::class); // 290
$balance->batchId(); // "9b1deb4d-..."PHP Attributes for Business Rules
Cashflow uses PHP attributes to enforce your business logic at the framework level:
#[Unique]— Only one instance per reference (no duplicate fees)#[Requires(Payment::class)]— Can't add a refund without a payment#[Rule(...)]— Laravel validation on constructor parameters
Built For Production
Cashflow was designed for real-world payment systems — SaaS platforms, marketplaces, and fintech apps where getting the numbers right matters. Every insert is validated, every batch is atomic, and every dependency is enforced before data hits the database.
Check out the full documentation on GitHub.