PHPackages                             cleaniquecoders/laravel-billing - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. cleaniquecoders/laravel-billing

ActiveLibrary[Payment Processing](/categories/payments)

cleaniquecoders/laravel-billing
===============================

A gateway-agnostic subscription &amp; invoicing engine for Laravel, with an optional Livewire + Flux billing UI. One package to maintain. Payment gateways are an extension point (a contract), not separate packages. Ships a built-in local gateway so any app has working billing on day one — ideal for demo, development, UAT, and testing.

1.1.0(1mo ago)4554[1 PRs](https://github.com/cleaniquecoders/laravel-billing/pulls)MITPHPPHP ^8.4CI passing

Since Jun 1Pushed 1mo agoCompare

[ Source](https://github.com/cleaniquecoders/laravel-billing)[ Packagist](https://packagist.org/packages/cleaniquecoders/laravel-billing)[ Docs](https://github.com/cleaniquecoders/laravel-billing)[ GitHub Sponsors](https://github.com/CleaniqueCoders)[ RSS](/packages/cleaniquecoders-laravel-billing/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (19)Versions (20)Used By (0)

Laravel Billing
===============

[](#laravel-billing)

A gateway-agnostic subscription &amp; invoicing engine for Laravel — with an **optional Livewire + Flux billing UI**. **One package to maintain** — payment gateways are an extension point (a contract), not separate packages. Ships a built-in **local** gateway so any app has working billing on day one, plus optional, publishable customer-facing pages (plans, subscription management, invoices, receipts) — ideal for demo, development, UAT, and testing.

[![Latest Version on Packagist](https://camo.githubusercontent.com/56d0e8e93d52e93945b97e5b66b44e24dc5c503432f55097ba3c233eb4b5ea4a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636c65616e69717565636f646572732f6c61726176656c2d62696c6c696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/cleaniquecoders/laravel-billing) [![GitHub Tests Action Status](https://camo.githubusercontent.com/fb58a26a2414dd018737aa0a56117265048581db728edec4da73965dc8e659c0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f636c65616e69717565636f646572732f6c61726176656c2d62696c6c696e672f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/cleaniquecoders/laravel-billing/actions?query=workflow%3Arun-tests+branch%3Amain) [![Total Downloads](https://camo.githubusercontent.com/b33f02d17082ae85a3d459bf3ed7e274b1daa0a8c8924f9c2036fd332d4d85b8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636c65616e69717565636f646572732f6c61726176656c2d62696c6c696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/cleaniquecoders/laravel-billing)

Why this design
---------------

[](#why-this-design)

- **One package, one repo.** No per-gateway sub-packages. BayarCash, ToyyibPay, Chip, senangPay, Stripe… each app writes a small driver class implementing the `PaymentGateway` contract. The package never references a real gateway by name.
- **Batteries included.** A first-class `LocalGateway` driver (no real money) is the default, so a fresh app can run the full subscribe → activate → invoice flow immediately.
- **Headless core, optional UI.** Models, services, contract, events, manager — fully usable with no UI at all. Need billing pages fast? Enable the bundled **Livewire + Flux** UI (browse plans, subscribe, manage the subscription, list/inspect invoices, download invoice &amp; receipt PDFs) behind your own `auth` middleware. Either way your app keeps control of routes and access.
- **Tenancy optional.** The bill target is polymorphic: attach `Billable` to `User` (single-tenant) or `Team`/`Workspace`/`Organization` (multi-tenant). The package does not care.
- **Malaysia-friendly invoicing.** MYR default, SST/SSM-aware invoice template, atomic sequential numbering — all configurable and neutral by default.

Installation
------------

[](#installation)

```
composer require cleaniquecoders/laravel-billing
```

Publish the config and (if using the database plan store) the migrations:

```
php artisan vendor:publish --tag="laravel-billing-config"
php artisan vendor:publish --tag="laravel-billing-migrations"
php artisan migrate
```

Optionally publish the invoice/checkout views and the plan seeder:

```
php artisan vendor:publish --tag="laravel-billing-views"
php artisan vendor:publish --tag="billing-seeders"
```

> A fresh install defaults to `BILLING_GATEWAY=local`, so demo/UAT works **before** any merchant account exists.

To use the optional billing UI, install Livewire and Flux in your app (the package guards on their presence, so this is only needed if you enable the UI):

```
composer require livewire/livewire livewire/flux
```

Make a model billable
---------------------

[](#make-a-model-billable)

Any model becomes billable by implementing the `Billable` contract and using the `HasSubscriptions` trait.

```
use CleaniqueCoders\LaravelBilling\Contracts\Billable;
use CleaniqueCoders\LaravelBilling\Concerns\HasSubscriptions;

class User extends Authenticatable implements Billable
{
    use HasSubscriptions;

    public function billingAddress(): array
    {
        return ['No. 1, Jalan Contoh', '50000 Kuala Lumpur'];
    }
}
```

`HasSubscriptions` provides `billingEmail()`, `billingName()` and `billingAddress()` defaults (override as needed) plus:

```
$user->subscription();              // current access-granting subscription, or null
$user->subscribedTo('pro');         // bool
$user->onTrial();                   // bool
$user->onGracePeriod();             // bool (access until current_period_end)
$user->plan();                      // active Plan, or the configured default/free
$user->invoices();                  // MorphMany
$user->canConsume('seats', 1);      // gated by plan limits
$user->recordUsage('seats', 1);
```

Plans — config and/or database
------------------------------

[](#plans--config-andor-database)

`config('billing.plans')` is always the canonical source. The `store` setting decides where reads come from:

- `store = 'config'` — `Plan` instances are built on the fly from the array. **No `plans` table needed.**
- `store = 'database'` — plans are read from the `plans` table; the publishable `PlanSeeder` hydrates it idempotently from config (`updateOrCreate` on `tier`).

```
use CleaniqueCoders\LaravelBilling\Services\PlanRepository;

$plans = app(PlanRepository::class)->all();          // Collection
$plan  = app(PlanRepository::class)->find('pro');     // ?Plan
$free  = app(PlanRepository::class)->default();        // Plan
```

Limits are an open map (`seats`, `messages_per_month`, `api_calls`, …) — your app declares which meters exist. A `null` limit means **unlimited**.

Subscribe flow
--------------

[](#subscribe-flow)

```
use CleaniqueCoders\LaravelBilling\Facades\Billing;
use CleaniqueCoders\LaravelBilling\Enums\PlanInterval;

$plan = app(PlanRepository::class)->find('pro');

$intent = Billing::checkout($user, $plan, PlanInterval::Monthly, route('billing.done'));

return redirect($intent->redirectUrl);
```

`checkout()` creates a pending (`Incomplete`) subscription, delegates to the active driver, and correlates the eventual webhook by `externalId`. With the local gateway:

- **Approve** on the dev-checkout page → a `SubscriptionActivated` event flows through the same code path a real gateway uses → subscription activates and an invoice is issued.
- **Decline** → the subscription stays `Incomplete`.
- Set `BILLING_LOCAL_AUTO=true` to auto-approve (great for CI/tests).

Webhooks (your app wires the route, the package does the work)
--------------------------------------------------------------

[](#webhooks-your-app-wires-the-route-the-package-does-the-work)

```
use CleaniqueCoders\LaravelBilling\Facades\Billing;

Route::post('/webhooks/{gateway}', function (Request $request, string $gateway) {
    $event = Billing::gateway($gateway)->parseWebhook($request);
    abort_if($event === null, 401);

    Billing::handle($event);   // dedups on providerEventId, transitions state, issues invoices, fires events
    return response()->noContent();
});
```

`Billing::handle()` replay-guards on `providerEventId`, locates the subscription by `gateway_subscription_id`/`externalId`, transitions status, calls `IssueInvoice` on activate/renew, and fires the matching event.

Write your own gateway
----------------------

[](#write-your-own-gateway)

Implement the single extension point and register the driver class in config. That's the entire surface.

```
namespace App\Billing;

use CleaniqueCoders\LaravelBilling\Contracts\{Billable, PaymentGateway};
use CleaniqueCoders\LaravelBilling\DataTransferObjects\{CheckoutIntent, WebhookEvent};
use CleaniqueCoders\LaravelBilling\Enums\{PlanInterval, WebhookEventType};
use CleaniqueCoders\LaravelBilling\Models\{Plan, Subscription};
use Illuminate\Http\Request;

class BayarCashGateway implements PaymentGateway
{
    public function createCheckout(Billable $billable, Plan $plan, PlanInterval $interval, string $returnUrl): CheckoutIntent
    {
        // call your SDK / HTTP here
        return new CheckoutIntent(redirectUrl: $url, externalId: $reference);
    }

    public function cancel(Subscription $subscription): void { /* … */ }

    public function parseWebhook(Request $request): ?WebhookEvent
    {
        // verify signature; return null if invalid
        return new WebhookEvent(
            type: WebhookEventType::SubscriptionActivated,
            externalId: $request->input('reference'),
            amountCents: (int) $request->input('amount') * 100,
            providerEventId: $request->input('event_id'),
            rawPayload: $request->all(),
        );
    }
}
```

```
// config/billing.php
'default'  => env('BILLING_GATEWAY', 'bayarcash'),
'gateways' => [
    'local'     => ['driver' => 'local', 'enabled' => env('BILLING_LOCAL_ENABLED', true)],
    'bayarcash' => ['driver' => App\Billing\BayarCashGateway::class],
],
```

The manager resolves the class through the container, so constructor injection works. You can also register a driver at runtime:

```
Billing::extend('toyyibpay', fn ($app) => new ToyyibPayGateway($app['config']['services.toyyibpay']));
```

Events
------

[](#events)

Listen to drive your own side effects (provision access, dunning, Slack notifications). The package itself only updates subscription/invoice state and issues invoices.

`SubscriptionActivated` · `SubscriptionRenewed` · `SubscriptionCanceled` · `PaymentSucceeded` · `PaymentFailed` · `InvoiceIssued`

Invoicing
---------

[](#invoicing)

Invoices use atomic, row-locked sequential numbering (`INV-2026-000001`) and are rendered to PDF from a brandless, SST/SSM-aware Blade template, stored via Laravel's `Filesystem` (disk configurable). Set seller details under `config('billing.company')`. Publish and override `resources/views/vendor/billing/invoice-pdf.blade.php` to brand it.

Billing UI (optional)
---------------------

[](#billing-ui-optional)

The package ships a Livewire + Flux UI that closes the full **subscribe → pay → invoice → receipt** loop. It is opt-in and scoped to the authenticated billable.

PlansBilling portal[![Plans](docs/assets/ui-plans-monthly.png)](docs/assets/ui-plans-monthly.png)[![Billing portal](docs/assets/ui-portal-overview.png)](docs/assets/ui-portal-overview.png)> More screenshots and the full walkthrough: [Billing UI docs](docs/03-billing-ui/01-overview.md).

Requires `livewire/livewire` and `livewire/flux` in your app. Enable and scope it in `config('billing.routes')`:

```
// config/billing.php
'routes' => [
    'enabled'    => true,
    'prefix'     => 'billing',
    'middleware' => ['web', 'auth'],
],
```

That registers:

RouteNameWhat it shows`GET /billing/plans``billing.plans`Plan cards with monthly/annual toggle; **Subscribe** runs `Billing::checkout()``GET /billing``billing.portal`**Overview** (current subscription, cancel/resume) + **Invoices** tab with a detail modal`GET /billing/success``billing.success`Payment-success receipt card (amount, number, **Download invoice / receipt**)`GET /billing/invoices/{invoice:uuid}/download``billing.invoices.download`Streams the stored invoice PDF`GET /billing/invoices/{invoice:uuid}/receipt``billing.invoices.receipt`Streams a receipt PDF derived from the paid invoiceThe billable is resolved via `config('billing.billable_resolver')` (defaults to `request()->user()`); set it to scope billing to a `Team`/`Workspace`. Every query is constrained to that billable, and the download/receipt routes 403 on a foreign invoice. Publish `laravel-billing-views` to override the Flux markup.

Component ownership — package vs your app
-----------------------------------------

[](#component-ownership--package-vs-your-app)

Lives in the packageLives in your app`Contracts\PaymentGateway`, `Contracts\Billable`Concrete gateway drivers (`BayarCashGateway`, …)`Gateways\LocalGateway` (bundled default)The real gateway SDK dependencyModels, migrations, enums, DTOs, eventsThe `Billable` model + `use HasSubscriptions``IssueInvoice`, `PlanRepository`, `BillingManager`, facadeCustom routes/UI beyond the bundled pages, access-control middlewareWebhook dispatcher `Billing::handle()`The webhook route + any environment/tenancy gatingBrandless invoice PDF template + `InvoiceIssuedMail`Company/tax config values, branded template overrideOptional Livewire + Flux billing UI (publishable, overridable)Enabling/scoping the routes, `livewire/flux` install, branding> **UI:** the package ships an **optional** Livewire + Flux billing UI (plans, subscription, invoices, receipts) plus the dev-only local checkout page. It is opt-in — disable `billing.routes.enabled` (or skip installing `livewire/flux`) and the package stays fully headless. Publish and override the views to brand them, or build your own pages against the same models and facade.

Documentation
-------------

[](#documentation)

Full documentation lives in [`docs/`](docs/README.md):

- [Getting Started](docs/01-getting-started/README.md) — install, make a model billable, define plans
- [Architecture](docs/02-architecture/README.md) — domain models, gateways, webhooks, events
- [Billing UI](docs/03-billing-ui/README.md) — pages, routes, invoices &amp; receipts (with screenshots)
- [Configuration](docs/04-configuration/README.md) — every `config/billing.php` key
- [Development](docs/05-development/README.md) — workbench preview &amp; testing
- [Examples](docs/06-examples/README.md) — full cycle &amp; custom gateway

Testing
-------

[](#testing)

```
composer test          # Pest (Livewire components tested headlessly via Livewire::test)
composer analyse       # PHPStan / Larastan
vendor/bin/pint        # code style
```

### Preview the UI locally (Testbench workbench)

[](#preview-the-ui-locally-testbench-workbench)

The package ships a Testbench workbench so you can click through the full **subscribe → pay → invoice → receipt** flow with the real Flux UI:

```
npm install
npm run dev                          # Vite + Tailwind (compiles Flux styles) — keep running
php vendor/bin/testbench workbench:build   # once: create sqlite db + run migrations
php vendor/bin/testbench serve             # http://127.0.0.1:8000
```

Open the root URL — it auto-logs in a demo billable and lands on `/billing/plans`. The workbench (`workbench/`, `testbench.yaml`) configures a demo plan matrix (`config` store), the local gateway with the dev "Approve" checkout page, and SST tax — mirroring what a host app sets in `config/billing.php`.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [Nasrul Hazim Bin Mohamad](https://github.com/nasrulhazim)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance92

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

53d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b57069d0f4b634f65eccc6e5d5848990e25968d45ec2cf46d626c6a4658f944b?d=identicon)[nasrulhazim.m](/maintainers/nasrulhazim.m)

---

Top Contributors

[![nasrulhazim](https://avatars.githubusercontent.com/u/10341422?v=4)](https://github.com/nasrulhazim "nasrulhazim (43 commits)")

---

Tags

billinglaravelpayment-gatewaylaravelcleaniquecoderslaravel billing

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/cleaniquecoders-laravel-billing/health.svg)

```
[![Health](https://phpackages.com/badges/cleaniquecoders-laravel-billing/health.svg)](https://phpackages.com/packages/cleaniquecoders-laravel-billing)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M48](/packages/spatie-laravel-pdf)[elegantly/laravel-invoices

Store invoices safely in your Laravel application

23546.8k2](/packages/elegantly-laravel-invoices)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[danestves/laravel-polar

A package to easily integrate your Laravel application with Polar.sh

8320.4k](/packages/danestves-laravel-polar)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
