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

Filament Billing Console Laravel Package

amjadiqbal/filament-billing-console

A Filament billing console for Laravel that adds an admin interface for managing plans, subscriptions, invoices, and payments. It integrates billing dashboards and actions directly into Filament panels, making it easier to oversee customer billing from one place.

View on GitHub
Deep Wiki
Context7

Getting Started

Billing Console is a read-only Filament plugin that surfaces Laravel Cashier (Stripe) customers, subscriptions, invoices, payments, MRR, and churn inside an internal Filament panel. It is aimed at support, ops, and developers who need to answer billing questions without opening the Stripe dashboard.

Minimum setup:

  1. Confirm your app meets the supported matrix: PHP 8.2–8.5, Laravel 12/13, Filament 4/5, and Cashier Stripe with its migrations and Stripe keys configured.

  2. Install the package:

    composer require amjadiqbal/filament-billing-console
    
  3. Register the plugin on an internal/admin panel, not a customer-facing panel:

    use AmjadIqbal\FilamentBillingConsole\BillingConsolePlugin;
    
    public function panel(Panel $panel): Panel
    {
        return $panel
            // ...
            ->plugin(BillingConsolePlugin::make());
    }
    
  4. Configure authorization before exposing the panel. The package supports authorizeUsing(), a viewBillingConsole gate, and model policies. A simple gate-based setup:

    // AuthServiceProvider
    Gate::define('viewBillingConsole', fn (User $user) => $user->hasRole('billing-admin'));
    
    BillingConsolePlugin::make()
        ->authorizeUsing(fn (User $user) => $user->can('viewBillingConsole'));
    

    Verify the default behaviour in your panel: do not assume the console is locked down until you have tested it with a non-admin user.

Where to look first: the Billing Console menu item in your panel. The customer list is the main entry point; the subscriptions page and dashboard widgets surface MRR, churn, and past-due/unpaid counts.

First practical use case: support triage. Search for a customer by email, open their customer page, confirm their active subscription and latest invoice/payment, and send a receipt or invoice PDF link. If the customer needs an actual change—refund, cancellation, plan swap—use the “Open in Stripe” link, because the console never writes to Stripe.

Implementation Patterns

Keep it on an internal panel. Register the plugin on your admin/staff panel only. Do not enable it on a customer or tenant-facing panel unless you have verified tenancy scoping and authorization thoroughly.

Use gates for broad access, policies for narrower rules. The viewBillingConsole gate is a good fit for “billing staff can see the console.” Use authorizeUsing() when access depends on panel context or custom business rules. If you expose customer records to tenant admins, verify whether model policies are applied to the customer/user model and to Cashier subscriptions; test cross-tenant access explicitly.

Support workflow: search → customer → subscription → invoice. The common day-to-day path is:

  • Search by name, email, or Stripe ID.
  • Open the customer page and check payment method, active subscriptions, invoices, and payments.
  • Use the “Open in Stripe” link when you need to perform a write action.
  • Use invoice PDF and receipt links for customer communications.

Use the filters for retention and dunning work. The subscriptions table filters by status and by “cancelled, still in grace period.” Pair that with the past-due/unpaid counters to prioritise outreach. The dashboard MRR and 30-day churn widgets are useful for a quick operational read, not for finance-grade reporting.

Use “Sync from Stripe” as a recovery tool, not the primary webhook path. If a webhook was missed or a local Cashier record drifted, the sync action stores Stripe’s copy in your Cashier tables using Cashier’s own webhook code. A sensible pattern is:

  • Investigate the customer in the console.
  • If local state looks stale, run “Sync from Stripe” for that customer or object.
  • Re-check your application’s entitlement logic after the sync.
  • Fix the underlying webhook/queue issue rather than relying on manual sync.

Treat invoice/payment data as cached API data. Invoices and payments come from the Stripe API and are cached. For support workflows this is usually fine; for “did this charge just succeed?” checks, use “Open in Stripe” or refresh/sync rather than trusting a stale cached list.

Match the Stripe mode to the environment. “Open in Stripe” links use test or live mode based on your configured Stripe key. Ensure staging uses test keys and production uses live keys; otherwise support staff will open the wrong dashboard.

Extend through Filament and the documented authorization hooks. The package is a Filament plugin, so navigation labels, panel placement, and widget/resource customisation should follow Filament plugin/resource conventions. The stable extension points called out in the release are authorizeUsing(), the viewBillingConsole gate, and model policies. Avoid relying on undocumented internals without pinning the package version.

Gotchas and Tips

  • Read-only does not mean no writes anywhere. The console never changes Stripe, but “Sync from Stripe” writes to your local Cashier tables. Treat it as a data-mutating admin action and restrict who can trigger it.
  • Authorization must fail closed. Test the console with a regular user, a tenant admin, and a super-admin. Confirm whether access is denied by default when no gate, authorizeUsing() callback, or policy is configured.
  • Sync depends on Cashier’s webhook code and configuration. It reuses Cashier’s handlers, so webhook signing secrets, Cashier version compatibility, and model configuration matter. Run it in staging first and verify which objects it actually updates in your Cashier setup.
  • Invoice PDF and receipt links can expire. Stripe-hosted document URLs are not permanent bookmarks. Generate or open them at send time; do not store them in CRM notes or long-lived emails without checking expiry.
  • Cached invoice/payment lists can lag. If a customer says they just paid, the console may show the previous state until cache refresh or sync. Use “Open in Stripe” for real-time confirmation.
  • MRR is list-price MRR. It is useful for a rough operational view but does not account for discounts, coupons, tax, credits, or proration. Do not present it as finance-certified revenue.
  • Watch query cost on large Cashier tables. The menu counters and subscription filters query subscriptions. Index stripe_id, stripe_status, and relevant date columns in your Cashier migrations if you have high volume.
  • The secret key is never shown, but it is used server-side. Keep the panel behind authentication and HTTPS, and avoid exposing it to non-staff panels. Review your Filament panel middleware and tenancy setup.
  • Stripe-mock coverage is not a guarantee against live API quirks. The package’s contract tests use stripe-mock; real Stripe accounts can have API version differences, rate limits, or object shapes that trigger empty states. Check Laravel logs when a customer page looks incomplete.
  • Supported versions are narrow. Laravel 12/13, Filament 4/5, PHP 8.2–8.5. Check composer constraints before upgrading your app or panel stack; this is a v1.0.0 package, so pin it in production and review changelogs.
  • Grace-period filtering relies on Cashier’s ends_at semantics. If your app uses custom cancellation or pause logic, verify that “cancelled, still in grace period” matches your actual entitlement rules.
  • Do not use the console as your entitlement source of truth. It is an observability and support tool. Keep your application’s own subscription checks and webhook-driven state as the authority for feature access.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core