Skip to content

Laravel Billing

Billing and payments for Laravel: pluggable payment gateways, one-off payments, saved cards, subscriptions with trials and dunning, usage-based pricing, refunds, invoices and receipts, and a webhook pipeline that is the only thing allowed to change a payment’s status.

Built-in gateways: Monobank Acquiring, LiqPay, WayForPay, Hutko, Stripe, Paddle, plus a fake gateway for local development. A gateway of your own is one Billing::extend() call.

  • One payment model for every gateway — a Payment row is created by your app, charged through any gateway, and settled by the gateway’s webhook (or by status polling when the webhook is lost)
  • A plain checkout link everywhere — payment_url is always a redirectable URL, even for form-only gateways; a permanent pay link re-issues an expired checkout on the fly
  • Saved cards — the card is tokenized as a side effect of the first charge, then charged off-session
  • Subscriptions — flat, per-seat and metered prices, minute-to-year intervals, trials, pause/resume, plan swaps, quotas, a retry ladder with a grace window; renewals run by the package or by the provider (Stripe Billing, Paddle)
  • Refunds — full and partial through the API, plus refunds issued from a gateway’s dashboard, recorded from webhooks
  • Invoices and receipts — numbered PDF documents, snapshots of seller, buyer and items at issue time
  • Webhook guarantees — fail-closed signatures, amount verification, per-outcome dedup, a paid payment never reverted
  • Multi-tenant — per-tenant credentials for outgoing calls and incoming webhooks, several accounts of one gateway
  • No dependencies beyond illuminate/*, Octane-safe, MySQL/MariaDB, PostgreSQL and SQLite
use Fomvasss\Billing\DTO\ChargeOptions;
use Fomvasss\Billing\Facades\Billing;
use Fomvasss\Billing\Models\Payment;
$payment = Payment::create([
'gateway' => 'monobank',
'amount' => 129900, // minor units — 1 299.00
'currency' => 'UAH',
'payable_type' => $order->getMorphClass(),
'payable_id' => $order->id,
'billable_type' => $user->getMorphClass(),
'billable_id' => $user->id,
]);
Billing::charge($payment, new ChargeOptions(description: "Order #{$order->number}"));
return redirect($payment->payment_url);
use Fomvasss\Billing\Events\PaymentSucceeded;
use Illuminate\Support\Facades\Event;
Event::listen(function (PaymentSucceeded $event) {
$event->payment->payable->markAsPaid(); // your Order
});

The browser coming back from the gateway proves nothing — fulfil orders from PaymentSucceeded, which only the verified webhook pipeline fires.

Getting started

  1. Installation
  2. Configuration

Usage

  1. Payments
  2. Return pages and the pay link
  3. Fiscal receipt items
  4. Refunds
  5. Saved cards
  6. Subscriptions
  7. Trials
  8. Renewals and dunning
  9. Usage and quotas
  10. Provider-managed subscriptions
  11. Invoices and receipts
  12. Webhooks
  13. Scheduled commands
  14. Tenants and multiple accounts
  15. Money and currencies
  16. Testing

Gateways

  1. Gateways overview
  2. Monobank
  3. LiqPay
  4. WayForPay
  5. Hutko
  6. Stripe
  7. Paddle
  8. Fake gateway

Guides

  1. Production checklist
  2. Real-world use cases
  3. Architecture
  4. Writing a gateway
  5. Testing webhooks by hand

Reference

  1. Billing facade
  2. Models
  3. Database tables
  4. Events
  5. Artisan commands
  6. Contracts
  7. DTOs and enums
  8. Routes

Upgrading