laravel-billing maintained by gusmanwidodo
Laravel Billing
Generic, multi-purpose billing & invoicing for Laravel. Attach invoices to any model (user, organization, tenant, project — anything), store money safely as integer minor units (no floating-point errors), build invoices with line items, tax, and discounts, and record payments. Payment-gateway agnostic by design.
Why
- Multi-purpose: invoices are polymorphic — bill a
User, anOrganization, aTenant, aProject, or nothing at all. - Money done right: all amounts are integer minor units (cents) plus a
currency code, so
0.1 + 0.2is exactly0.30— never a float rounding bug. - Computed totals: subtotal, tax, and total are derived from line items and persisted; recompute any time.
- Gateway-agnostic: the package records payments; it does not talk to Stripe,
Midtrans, etc. Bring your own gateway and call
recordPayment().
Requirements
- PHP
^8.3 - Laravel 12
Installation
composer require gusmanwidodo/laravel-billing
php artisan migrate
# optionally:
php artisan vendor:publish --tag=billing-config
php artisan vendor:publish --tag=billing-migrations
Quick start
use Gusmanwidodo\Billing\Facades\Billing;
$invoice = Billing::newInvoice('USD')
->addItem('Pro plan', unitPrice: 5000, quantity: 2, taxRate: 0.10) // $50.00 x2, 10% tax
->addItem('Setup fee', unitPrice: 1500, discount: 500) // $15.00 - $5.00
->discount(1000) // invoice-level discount: $10.00
->dueInDays(14)
->create();
$invoice->total; // integer minor units, e.g. 11000
$invoice->total()->format(); // "110.00 USD"
All prices are integer minor units in the invoice currency (e.g. 5000 =
$50.00). Use the Money helper to convert:
use Gusmanwidodo\Billing\Support\Money;
Money::fromMajor(50.00, 'USD')->amount; // 5000
Money::of(5000, 'USD')->format(); // "50.00 USD"
Billing any model
Add the HasInvoices trait to whatever you bill:
use Gusmanwidodo\Billing\Concerns\HasInvoices;
class Organization extends Model
{
use HasInvoices;
}
$org->invoice('USD')
->addItem('Enterprise seat', 100000, quantity: 5)
->create();
$org->invoices; // all invoices billed to this org
The invoice's billable is a polymorphic relation, so the same package bills
users, orgs, tenants, or anything else — no per-type tables.
Totals
For each line item:
line subtotal = quantity * unit_price - discount
line tax = round(line subtotal * tax_rate)
For the invoice:
subtotal = Σ line subtotals
tax_total = Σ line taxes
total = subtotal - invoice_discount + tax_total
All integer arithmetic. Call Billing::recompute($invoice) after changing items.
Payments
// Record a payment (your gateway/cash/transfer — the package is agnostic):
Billing::recordPayment($invoice, 4000, attributes: [
'method' => 'transfer',
'reference' => 'TRX-123',
]);
$invoice->refresh();
$invoice->status; // 'partially_paid'
$invoice->amountDue()->format(); // remaining balance
Billing::recordPayment($invoice, 7000);
$invoice->refresh();
$invoice->status; // 'paid' (amount_paid >= total)
$invoice->paid_at; // set automatically
amount_paid is always the sum of recorded payments; status is derived
(partially_paid / paid). Payments must match the invoice currency and be
positive.
Void
Billing::void($invoice); // status = 'void'; further payments are rejected
Status
Status is free-form — your app owns the workflow. Convenience constants are
provided, and the package sets partially_paid / paid automatically as
payments come in (and void when you void):
use Gusmanwidodo\Billing\Support\InvoiceStatus;
InvoiceStatus::DRAFT; // 'draft'
InvoiceStatus::SENT; // 'sent'
InvoiceStatus::PARTIALLY_PAID; // 'partially_paid'
InvoiceStatus::PAID; // 'paid'
InvoiceStatus::VOID; // 'void'
Money & currencies
- Amounts are stored as
bigIntegerminor units; never floats. Money::fromMajor(10.50, 'USD')→1050. Passdecimals:for zero-decimal currencies (e.g. JPY):Money::fromMajor(1000, 'JPY', decimals: 0).Moneysupportsadd,subtract,multiply,percentage,format, and refuses cross-currency arithmetic.
Config
config/billing.php:
'default_currency' => 'USD',
'number_prefix' => 'INV-', // auto invoice numbers: INV-20260101-AB12CD
'decimals' => 2,
Testing
composer test # 17 tests, incl. money math, polymorphic billable, payments
Roadmap
- Payment-gateway adapters (Stripe / Midtrans) as separate packages.
- Recurring/subscription invoices.
- PDF rendering.
License
MIT © Gusman Widodo. See LICENSE.