Documentation for larsasub v0.38.2.
On an older version? Your release notes ship with the package (CHANGELOG.md).
Setup
Everything between composer require and a working billing installation:
running the installer, making a model billable, connecting Mollie, declaring
your license tier, and publishing the assets you want to customize. For a
shorter walk-through to a first subscriber, see Quick start.
- Requirements
- Installing the package
- Making a model billable
- Connecting Mollie
- Declaring your license tier
- Running the scheduler and a queue worker
- Publishing assets
- Dashboard assets and theming
- Customizing the PDF templates
- Verifying the installation
- AI coding agents (Laravel Boost)
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.3 |
| Laravel | ^12.11 | ^13.0, a full application (the package uses framework services beyond the split Illuminate components) |
| Payment provider | A Mollie account and API key |
| Optional | barryvdh/laravel-dompdf ^3.1, for the built-in invoice PDFs |
A live installation also needs a public HTTPS URL for the webhooks, cron and a queue worker; the full list is in Before you install.
Installing the package
LarsaSub is not on Packagist.
composer requireonly resolves once you have registered the private repository on larsasub.eu and authenticated with your license token — without that, composer reports "package not found". See the installation guide on larsasub.eu/docs for thecomposer configlines.
composer require peters-development/larsasub
php artisan larsasub:install
larsasub:install publishes the configuration, publishes the built dashboard
assets, runs the migrations and seeds the payment methods (all inactive). It
also prints the license tier you declared, and warns when Mollie Connect is
enabled on a tier that does not cover it.
It is safe to run again. Re-running is the supported way to publish the
assets that ship with a new package version and to apply its migrations. Every
step is idempotent: payment methods are seeded with firstOrCreate, and a
published config/larsasub.php is kept — the installer never overwrites
your customizations. Pass --force-config when you deliberately want the
package default back.
The dashboard assets (--tag=larsasub-assets) are force-published, because
they are build artifacts that must match the installed version; a stale bundle
against a newer payload shape is a broken UI, not a customization.
| Option | Effect |
|---|---|
--seed |
Seed two example subscription plans (fresh install only) |
--active |
Seed active subscriptions and charges for existing billables (test data, fresh install only) |
--active-payment-methods |
Activate every seeded payment method (fresh install only) |
--vue-components |
Publish the Vue SFC sources, with the modules, translations and stylesheet they use, for your own SPA build (never overwritten once published) |
--force-config |
Overwrite an existing config/larsasub.php with the package default |
--skip-migrations |
Do not run migrations; run php artisan migrate yourself |
The three demo-data options are the exception to "safe to run again": they only
act on a fresh install, one where the LarsaSub tables do not exist yet. On
an existing installation --seed, --active and --active-payment-methods
are skipped with an explanation, because they would duplicate purchasable
plans, re-enable payment methods an operator switched off, and attach demo
subscriptions to existing billables. Reseed through your own seeder instead.
When --skip-migrations leaves a fresh installation without its tables, the
installer seeds nothing and tells you to finish with
php artisan migrate && php artisan larsasub:install.
Migrations.
larsasub:installcallsphp artisan migrate, which applies all pending migrations in your application — not only this package's. An interactive run asks before doing so; a non-interactive run (--no-interaction, deploy scripts) proceeds and says so. Use--skip-migrationsto keep the two steps apart.
Making a model billable
Any Eloquent model can hold subscriptions and payments. Implement
Contracts\BillableModel and add the IsBillableModel trait, plus
IsMollieBillableModel, which the Mollie provider needs, and HasSubscriptions
for the subscription shortcuts:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use PetersDevelopment\LarsaSub\Contracts\BillableModel;
use PetersDevelopment\LarsaSub\PaymentProviders\Mollie\Traits\IsMollieBillableModel;
use PetersDevelopment\LarsaSub\Traits\HasSubscriptions;
use PetersDevelopment\LarsaSub\Traits\IsBillableModel;
class User extends Authenticatable implements BillableModel
{
use HasSubscriptions;
use IsBillableModel;
use IsMollieBillableModel;
public function getBillableNameAttribute(): string
{
return $this->name;
}
public function getBillableEmailAttribute(): string
{
return $this->email;
}
public function getBillableIdentifierAttribute(): string
{
return (string) $this->id;
}
}
billable.model defaults to App\Models\User. When another model pays, point
the config at it:
// config/larsasub.php
'billable' => [
'model' => \App\Models\Team::class,
'relation' => 'team', // how to get from the signed-in user to the payer
],
Things worth knowing before you go further:
- LarsaSub stores billing state against a
Billableintermediary record, not against your model directly. It is created automatically on the first billing operation; you never manage it by hand. See Billable models. - The contract also requires
getNotifiableUsers().IsBillableModelimplements it: aNotifiablemodel receives its own billing mail, any other model is mailed at itsbillable_email. Override it to send to other people. IsMollieBillableModelis not optional with the Mollie provider: the checkout callsgetMollieCustomer()and the mandate methods on your model, andlarsasub:doctorreports a model without it.HasSubscriptionsaddsactiveSubscription,hasFeature()andfeatureValue();IsBillableModeldeliberately carries only the core billing relations. See Traits and helpers.
Connecting Mollie
MOLLIE_KEY=test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A test_ key puts every provider call in test mode; that detection is what the
package keys its own test-mode handling on, including OAuth access tokens in
Connect mode.
Payment methods are seeded inactive. Activate the ones your Mollie account actually supports before you sell anything; the Quick start shows how, and Payment methods and mandates explains each flag.
Webhooks must be reachable from the internet. For local development, point LarsaSub at an ngrok tunnel:
LARSASUB_NGROK_DOMAIN=https://your-subdomain.ngrok.io
Schemeless values get https:// prepended.
Declaring your license tier
LarsaSub is sold in three tiers, and you declare yours in the configuration:
LARSASUB_LICENSE_TIER=single # single (default), unlimited or platform
| Tier | Covers |
|---|---|
single |
One production application |
unlimited |
Any number of applications, with payments settling to your own Mollie account |
platform |
Collecting payments on behalf of others: required for Mollie Connect |
The declaration is a local value: nothing is verified, signed or sent
anywhere, and the package never contacts larsasub.eu. The one check it makes
is against Connect: with connect.enabled on and a tier other than
platform, the package refuses to boot. Only a short list of recovery
commands still runs then (larsasub:doctor, larsasub:install,
larsasub:license, config:clear, vendor:publish and a few others), so you
can repair the configuration.
php artisan larsasub:license
shows the declared tier and whether the enabled features fit it.
Running the scheduler and a queue worker
Planning, collecting, renewing and ending subscriptions all happen in
scheduled commands that the package registers itself, so cron must run
schedule:run every minute; without it nothing is ever charged.
Notifications and several listeners are queued, so run a queue worker too:
the sync queue runs mail inside the webhook request, and larsasub:doctor
warns about it outside local and testing. See
Scheduled commands for frequencies and overlap protection.
Publishing assets
larsasub:install publishes what a standard installation needs. To publish
individual groups:
# Configuration
php artisan vendor:publish --tag=larsasub-config
# Database migrations. Optional: the package loads its migrations itself,
# so `php artisan migrate` runs them without publishing
php artisan vendor:publish --tag=larsasub-migrations
# Blade views (published to resources/views/vendor/LarsaSub — the path the
# view finder reads LarsaSub:: overrides from; edits there win over the package)
#
# All-or-nothing: this copies the ENTIRE view tree, and every file you keep is
# then frozen at the version you published it. Prefer one of the two narrow
# tags below if that is what you actually want.
php artisan vendor:publish --tag=larsasub-views
# The mail layout alone (emails/layout.blade.php) — the mail chrome: colours,
# wordmark, footer, dark-mode block. Nearly every mail customization is this
# one file, and taking only it keeps the 49 notification views on the package.
php artisan vendor:publish --tag=larsasub-email-layout
# The invoice/credit-note PDF views alone
php artisan vendor:publish --tag=larsasub-invoice-views
# Translation files (published to lang/vendor/larsasub — or
# resources/lang/vendor/larsasub when your app keeps its language files there;
# the tag follows langPath(), which is the only path the loader reads)
php artisan vendor:publish --tag=larsasub-translations
# Vue components (SFC sources, for your own SPA build), with the utils,
# types, composables and frontend.json they import and the larsasub.css
# whose tokens their classes use, under resources/vendor/larsasub; see BUILD.md
php artisan vendor:publish --tag=larsasub-vue-components
# Built standalone dashboard assets. Republishing over an older copy needs
# --force; larsasub:install always forces them
php artisan vendor:publish --tag=larsasub-assets --force
Dashboard assets and theming
The standalone dashboard assets in public/vendor/larsasub/ are fully
self-contained:
larsasub.css— a complete compiled Tailwind stylesheet covering every dashboard component, including light/dark theme tokens. The dashboards are fully styled without any host Tailwind or Vite setup.vue.global.prod.js— the bundled Vue 3 runtime. No CDN is contacted at runtime.standalone-*-app.umd.js— the dashboard apps, with component-scoped styles inlined.
Theming by the host app is optional and works in two layers:
-
Theme view —
larsasub.standalone.theme_view(defaultnull) names a Blade view that the dashboard views include afterlarsasub.css. Put your stylesheet markup there, whatever your asset pipeline is:@vite(['resources/css/app.css']),<link href="{{ mix('css/app.css') }}">, or a plain<link>tag. Because the dashboards read shadcn-style tokens (--background,--primary,--card, …), a host stylesheet that defines those tokens on:root/.darkre-themes the dashboards automatically.// resources/views/larsasub-theme.blade.php contains your @vite call 'standalone' => [ 'theme_view' => 'larsasub-theme', ], -
Tailwind source scanning (optional) — only needed if your stylesheet should restyle package markup with its own utilities. Tailwind v4 hosts can add
@source '../../public/vendor/larsasub/*.js';to their CSS entry so package classes are included in the host build. With the bundledlarsasub.cssthis is not required for correct rendering.
See Standalone dashboards for routing, middleware and the per-dashboard switches.
Customizing the PDF templates
The built-in invoice and credit-note PDFs (resources/views/vendor/LarsaSub/pdf/*
once published) are rendered by dompdf, which is safe as long as the template
only receives escaped data. When customizing them:
- Keep all user/customer data inside
{{ }}(Blade escaping). If you need line breaks, escape first:{!! nl2br(e($value)) !!}— never output raw user input through{!! !!}. - Leave dompdf's
isRemoteEnabledat its default (false) — do not load fonts, images or CSS from URLs in PDF templates. Historical dompdf CVEs all involved rendering untrusted HTML or fetching remote assets. - Prefer inline CSS and the bundled DejaVu fonts, as the stock templates do.
dompdf is a suggested dependency, not a required one:
composer require barryvdh/laravel-dompdf to use the built-in PDFs. Consumers
that render invoices through an external service can skip it entirely by binding
their own Contracts\InvoiceRenderer; the built-in renderer then throws a
LarsaSubException naming the missing package rather than a class-not-found
mid-stream. See Invoicing and VAT.
Verifying the installation
php artisan larsasub:doctor
larsasub:doctor checks the configuration for combinations that render fine and
only fail later (a guard nothing authenticates, a tenant-context hook that can
never fire), and it verifies the columns of every shipped table — not just
that the tables exist. Before 0.37.1 the migrations were edited in place, so a
database created from an earlier release can have nothing pending while
migrate reports success, and the next scheduled run dies on Unknown column.
Since 0.37.1 every schema change is a new migration.
AI coding agents (Laravel Boost)
LarsaSub ships guidance for the AI coding agent you work with, in the format Laravel Boost loads from installed packages:
| File | What it is | When the agent sees it |
|---|---|---|
resources/boost/guidelines/core.blade.php |
The rules: billing state goes through the services, payment status comes from the webhook, provider calls stay outside your transactions, the relation-versus-property trap | Up front, in every session |
resources/boost/skills/larsasub-development/SKILL.md |
Task recipes: subscribing, access checks, plan changes, cost lines, one-time payments, credit, refunds, events, Connect, testing | On demand, when the task touches billing |
With Boost installed, run boost:install, and boost:update after upgrading
LarsaSub. Boost includes the guideline automatically, and installs the skill
when you enable skills during installation:
php artisan boost:install
Both files point the agent at the documentation shipped in
vendor/peters-development/larsasub/docs/, so it reads the pages for the
version you have installed. Without Boost, nothing loads them, and nothing
else in the package depends on them.
Next: Quick start for a working end-to-end example, or Configuration for the full list of settings.