Skip to main content

Choosing your setup

Six decisions shape how the package sits in your app. Make them before billing:install, because two of them — the billable model and who owns billing — decide which table the generated migration adds columns to.

Each has a safe default you can revisit later, except the billable model: it has no useful default, and it is the one setting a real payment depends on.

1. Which model is the customer?

billing.customer.model is the Eloquent model that owns the provider's customer record, and billing.customer.column is where that reference is stored (stripe_id, Cashier's default).

'customer' => ['model' => App\Models\User::class, 'column' => 'stripe_id'],

There is no default. A clone still boots without it, but no subscription webhook can resolve its owner, so a paying customer's plan is never synced and they keep seeing the free tier.

If your billable model is not App\Models\User, set BILLING_CUSTOMER_MODEL before you run billing:install — otherwise the generated migration adds the billing columns to users.

Installation

2. Does a user pay, or does a team?

billing.owner is user (each user is their own billing owner) or team (the user's team owns the subscription and pays for seats). With team, billing.team_relation names the relation on the acting user that returns the paying team, and the billing.seats.* keys tell the package how to count active members and which of your membership events re-sync the billed quantity.

A user-owner app ignores the seats block entirely. Nothing fires until you list your own events under billing.seats.membership_events — the package does not own your membership table.

Subscriptions

3. Are you the seller, or is someone else?

By default your app sells the subscription: checkout, invoices and payment methods all live in your app.

If an external merchant of record owns billing instead — an app store's subscription management, or an external portal — set billing.link_out to that portal's URL. The account hub then links out to it rather than offering in-app checkout it is not the merchant of record for. The value is scheme-restricted (an absolute http/https URL with a host), so a malformed value simply leaves link-out off.

Related, and easy to miss: billing.runtime. Set it to native in a mobile webview and the navigation items flagged web_only — account deletion, the link out to an external portal — are hidden, because an app store forbids completing those flows in-app.

The account hub

4. How is tax computed?

billing.tax picks one of three. It is a driver-capability decision, not a per-checkout option:

ValueWhat happens
none (default)Tax is never added. Fine while you sell into one country and price gross.
providerThe provider computes and remits tax on the invoice (Stripe Tax).
eu_ossThe bundled static EU-OSS VAT table computes it locally.

The TaxCalculator contract is the seam for anything else — a local rate table, an external tax service — without touching the checkout path.

Separately, billing.company.* carries your own details as the seller party. They are required for EN 16931 e-invoices (XRechnung / ZUGFeRD) and unused if you never emit one.

Taxes · Invoices and e-invoicing

5. Whose screens?

The package ships a publishable account hub — overview, subscription, change plan, payment methods, invoices, usage, payment recovery, danger zone — mounted at account.prefix behind account.middleware. billing.navigation decides which sections exist and in what order; removing an entry removes the section.

Point account.layout at your own Blade layout to frame the hub in your chrome, and leave the scoped Content-Security-Policy on (account.csp) so the payment element loads on the billing screens only.

You can also skip the hub and call the same contracts from your own screens. Nothing in the package requires its own UI.

The account hub

6. Do tiers unlock features, or only cost money?

config/billing.php is what a customer pays. config/license.php is what a tier unlocks — boolean feature grants and numeric limits. They are two orthogonal files on purpose: billing code never reads license.* config (an architecture test enforces it), and neither ever blocks a public or marketing surface.

If your tiers differ only in price, leave license.php empty. If they gate features, fill it in and ask the License contract rather than comparing tier keys by hand.

Configuration · Tiers and pricing


With these decided, go to Installation and read the guide in order.


← Back to the documentation index