Skip to main content

The account hub

When billing.enabled is on, the hub mounts under config('account.prefix') (default account/billing) behind your web + auth middleware:

RouteScreen
/Overview + tier summary
/subscriptionState, next-invoice preview, cancel / resume, portal link
/planSubscribe (hosted checkout) or in-app swap with a proration preview
/payment-methodsList, set-default, remove, add
/invoicesHistory + ownership-checked PDF download
/usageMetered dimensions
/usage/historyPast periods + add-on top-up timeline
/recoveryDunning recovery surface
/dangerImmediate cancel
/portalRedirect to the provider's hosted billing portal
/checkout/returnReconciles the subscription after a hosted checkout

See Subscriptions for the subscribe/swap flow behind /plan.

Add the shell banner to your layout — it renders nothing for a healthy account:

<x-billing::banner />

Every screen renders inside a publishable app shell (layouts/account.blade.php): a grouped sidebar navigation with the active item marked, a skip link to the main content, a typed document title, and a POST-logout form shown only when your app registers a logout route. It needs no UI-kit dependency; publish billing-views to replace it with your own design system's shell.

If an external merchant of record owns billing (an app-store subscription, an external portal), set billing.link_out (env BILLING_LINK_OUT) to that portal's URL: the plan screen then links out to it and suppresses the in-app checkout it is not the merchant of record for. On a native runtime, set billing.runtime=native (env BILLING_RUNTIME) to hide flows an app store forbids in-app.

Hosting your own screens in the hub

The hub navigation is config-driven, and the hub hosts screens it does not own. To slot one of your own app or auth screens — sessions, connections, set-password, an onboarding step — into the hub, register its route in billing.navigation (an optional group places it in a labeled section, billing::account.nav.group.<group>):

// config/billing.php
'navigation' => [
'sessions' => ['label' => 'account.sessions', 'route' => 'app.sessions', 'group' => 'account', 'order' => 60],
],

The entry appears in the navigation only once that route actually exists, so the same config can name a section your app builds later — an unregistered route (or one that needs parameters the hub cannot supply) is silently dropped, never rendered as a broken link. The package ships no ancillary-screen classes of its own; it only slots your route into the shell.


← Back to the documentation index