Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Spark Billing Provider Laravel Package

filament/spark-billing-provider

Filament plugin that brings Laravel Spark billing into your admin panel, letting you manage plans, subscriptions, and invoices through native Filament resources, widgets, and actions. Install via Composer and start handling customer billing workflows in minutes.

View on GitHub
Deep Wiki
Context7

Getting Started

filament/spark-billing-provider is the bridge between Laravel Spark (Cashier/Stripe subscription scaffolding) and a Filament panel. In practice it lets you serve Spark's billing portal — subscription status, plan switching, payment methods, invoices — as a native Filament page instead of Spark's default front end, while keeping Spark as the source of truth for plans, coupons, and the Billable model.

Because it ships inside the Filament monorepo (packages/spark-billing-provider) rather than as a standalone product, the release cadence tracks Filament itself. Treat the package source as the documentation.

First three steps

  1. Install alongside Spark and match majors:

    composer require filament/spark-billing-provider:"^4.0"
    

    Confirm the constraint against your filament/filament version — the package is versioned with the monorepo, not independently.

  2. Publish and read what the service provider offers before writing any code:

    php artisan vendor:publish --provider="Filament\SparkBillingProvider\SparkBillingProviderServiceProvider"
    

    Inspect packages/spark-billing-provider/src/ in the repo — specifically the Plugin class, the page class, and ...ServiceProvider::publishes(). Publish tags, config keys, and view namespaces are all defined there, and the package docs under packages/spark-billing-provider/docs/ are thinner than the code.

  3. Register the plugin on the customer-facing panel (never the admin panel):

    use Filament\SparkBillingProvider\SparkBillingProvider;
    
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->id('app')
            ->path('app')
            ->login()
            ->plugin(SparkBillingProvider::make());
    }
    

First use case: a subscriber hits /app/billing, sees their current Spark plan, and can swap plans or open the Stripe customer portal without leaving the panel. That single flow is the package's reason for existing — validate it end-to-end against Stripe test keys before layering anything else on.

Implementation Patterns

Let the plugin own the route, not your app. Resist hand-rolling a /billing route that wraps the Filament page. Register the plugin, then point Spark's billing-portal contract at the resulting route (see Spark::billingPortal() / the spark config in your app). Everything else — navigation registration, authorization, layout — comes free from the panel.

Gate the panel on subscription, not on the page. The idiomatic Spark setup is middleware plus canAccessPanel():

public function canAccessPanel(Panel $panel): bool
{
    return $this->hasVerifiedEmail();
}

and on the panel:

->middleware(['web', 'auth', EnsureUserIsSubscribed::class])

Keep the billing page itself reachable for unsubscribed users, otherwise they can never pay you. If your panel is subscription-gated wholesale, grant the billing page an exception route or a separate panel.

Drive upgrades through Filament actions from anywhere in the panel. The billing page shouldn't be the only entry point:

use Filament\Actions\Action;

Action::make('upgrade')
    ->label('Upgrade plan')
    ->visible(fn (): bool => ! auth()->user()->subscribed())
    ->url(fn (): string => Spark::billingPortal())
    ->openUrlInNewTab(false);

Attach it to a dashboard widget, a resource header, or a UserMenu item. Because the portal is now a Filament route, url() keeps users inside your panel shell instead of bouncing them to a separate Spark layout.

Compose with Filament's native primitives for anything Spark doesn't render. Invoices, usage counters, and seat counts are usually better as a Table or Infolist you build yourself against Cashier/Spark data than as custom Blade injected into the package's view. Read the record from $user->invoices(), expose it via TextColumn::make('total')->money(...), and keep your own code in your app — publishing and editing the package's views is a maintenance tax.

Keep plan definitions in one place. Spark's config/spark.php plans and the labels you show in Filament will drift. Wrap plan labels in an enum or a small PlanCatalog service and reference it from both the plugin config and your own action labels:

Select::make('plan')
    ->options(PlanCatalog::labels())
    ->default(fn (): ?string => auth()->user()->sparkPlan()?->name);

Testing follows standard Filament patterns with a Stripe seam. Use Pest + pestphp/pest-plugin-livewire, authenticate the billable model, and fake Stripe HTTP calls:

livewire(BillingPage::class)
    ->actingAs($user)
    ->assertSuccessful()
    ->assertSee($user->sparkPlan()->name);

The package's own tests (under tests/src/) are the best reference for the exact component class name and required panel context — copy their setup rather than guessing.

Gotchas and Tips

The docs are not the API. This package is one directory in a 32k-star monorepo; its public surface is small and under-documented relative to Forms/Tables. When something doesn't work, read src/ and the published config before searching the web — most tutorials you'll find describe Spark's default portal, not this provider.

Register the plugin exactly once. Registering it on two panels collides on route names and navigation. If you genuinely need billing in two panels, register on one and link to it with an absolute URL.

Guard and billable model must agree. Spark assumes auth()->user() implements Billable and has a Stripe customer. A panel using a non-default guard (auth:admin) will produce "no customer" errors that look like provider bugs. Set the panel's ->authGuard() deliberately and confirm the model matches Spark's billable config.

Webhooks are not handled by Filament. Stripe webhooks still hit Cashier's route; if your app's CSRF middleware or a web middleware stack catches them, subscription state silently desyncs from Stripe. Verify with php artisan route:list --name=cashier (and --name=spark) and a stripe listen --forward-to session before shipping.

Cache and assets. After changing plans in config/spark.php, run php artisan config:clear — plan changes appearing "not to apply" is nearly always config cache. After publishing views or upgrading the package, php artisan filament:upgrade and php artisan filament:assets (or your build pipeline) must run, otherwise stale compiled CSS shows unstyled markup.

Don't edit vendor/published views in place. The package is monorepo-versioned and will move under you on composer update. Override via your own theme or a small custom page that composes the package's components.

Route collisions with Spark's own portal. If Spark's default billing route is still registered, you'll get two candidates for Spark::billingPortal() and confusing redirects. Pick one — typically the Filament page — and remove or leave unlinked the other.

Debugging checklist, in order: php artisan route:list --name=billing → php artisan about (confirm the plugin appears in the panel's plugins) → $panel->getPages() in tinker → the package's src/ page class → its tests. Ninety percent of "the page 404s / is blank" reports are an unregistered plugin, a wrong panel path, or a missing auth middleware.

Upgrade deliberately. Because the version tracks Filament ^4.x, a Filament minor bump can move the provider. Pin it in composer.json, and when upgrading, run composer test:sqlite and composer test:phpstan locally if you've forked or extended any package classes — the repo's own commands are the fastest smoke test you have access to.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections