maartenpaauw/filament-cashier-billing-provider
Integrate Laravel Cashier Stripe subscriptions into Filament multi-tenant panels. Configure plans in cashier.php and make your Billable model the tenant model; supports recurring subscriptions, trials, promo codes, tax IDs, and metered prices.
Architecture fit. This is a thin, opinionated adapter — not a billing system. It slots into an existing Filament panel as a tenant billing provider, wiring Filament's multi-tenancy lifecycle to Laravel Cashier's Stripe implementation. The critical architectural constraint is stated plainly in the README: your Billable model must be the same as your Filament tenant model. That means one Stripe customer per tenant, subscriptions owned at the tenant level, and no per-user billing inside a tenant. For SaaS products where the paying entity is the workspace/organisation, this is a clean, low-friction fit. For products that bill individual users but group them into teams, or that need seat-level proration, this is a hard architectural mismatch and the package should be rejected or forked.
Scope is deliberately narrow: recurring subscriptions only (explicitly flagged as a limitation in the README), Stripe only (no Paddle), and plan definition via a plans array in cashier.php. The package is essentially three things: a BillingProvider registered on the panel, an optional requiresTenantSubscription() gate backed by RedirectIfUserNotSubscribed middleware, and plan-config parsing that maps plan keys to Stripe product_id/price_id pairs with optional trial, promotion-code, tax-ID, and metered-price flags. Feature breadth is modest but the surface area you must support is correspondingly small.
Integration feasibility. High for greenfield, moderate for brownfield. The install is a single composer require plus two lines in the panel provider. Config uses ENV() calls inside cashier.php, which is valid but makes php artisan config:cache a required, ordered deployment step — a stale or missing cache silently yields null product/price IDs. The v3.x line is a layered rewrite (v3.0.0 "Use a layered architecture") that dropped Filament 3 support, so the package now tracks Filament 4 and 5. v3.1.0 additionally requires Laravel 13, PHP 8.5, and Cashier 16, and removed Laravel 11. That aggressive support matrix is the single biggest feasibility gate: you must confirm your Laravel, PHP, Filament, and Cashier versions all land inside one supported package major before committing.
Technical risk. Rate this medium.
maartenpaauw), 104 stars, zero dependents. There is no downstream production base to absorb breakage on your behalf, and no evidence of use at scale.Response instances) means auth/billing gate bugs surface as lockouts or, worse, as free access.Key questions.
Stack fit. The package presumes: a Filament panel with tenant() configured, a Tenant model implementing Billable with the standard Cashier columns (stripe_id, pm_type, pm_last_four, trial_ends_at), a configured Stripe account with products and recurring prices already created, and Cashier installed with webhooks live. If the tenant model is a Team/Workspace and Cashier is currently bolted onto User, expect real migration work — not a package install.
Migration path. Two distinct paths:
Billable from day one, add Cashier columns to the tenants table, publish cashier.php with the plans map, register BillingProvider in the panel and decide whether requiresTenantSubscription() applies. Low effort, roughly 2–5 engineering days including Stripe test-mode validation.stripe_id on tenants, reconcile existing invoices and trial state, and preserve access continuity during cutover. Budget 2–4 weeks with a Stripe-side runbook and a dual-read period. Do not attempt this without confirming how in-flight trials and past-due states should map.Compatibility. Pin explicitly. Filament 3 requires package 1.x/2.x (which in turn caps Laravel 12). Filament 4/5 requires package 3.x (Laravel 12/13, Cashier 15/16). Do not float the constraint — a ^3.0 that silently absorbs a future 3.2.0 dropping Laravel 13 is an avoidable outage. Lock the package version in composer.json and record the coupling in your upgrade documentation.
Sequencing. (1) Feasibility spike: one developer, 1–2 days, proves tenant-as-Billable and a Stripe test-mode subscription end-to-end. (2) Schema and config: Cashier columns on tenants, plans map with product/price IDs sourced from secrets, config-cache step added to the deploy pipeline. (3) Panel wiring: tenantBillingProvider() first without the subscription gate — verify the billing UI renders before gating anything. (4) Gate enablement behind a feature flag per panel. (5) Webhook verification and subscription-state reconciliation. (6) Staging soak with Stripe test clocks for trial expiry and dunning. (7) Production rollout to a single cohort, then general availability.
Maintenance. Low day-to-day, spiky at framework upgrades. The dependency is small and idiomatic; there is no bespoke runtime to operate. The recurring cost is the coupling tax: every Filament, Laravel, Cashier, or PHP major likely forces a package major bump, and the upgrade notes are terse changelog entries rather than migration guides. Allocate a recurring 1–2 days per framework upgrade cycle and treat the package as part of your Filament upgrade epic, not as an independent line item. The repo does carry a test workflow and a changelog, which materially lowers the cost of validating a bump.
Support. Community-only. GitHub issues, no SLA, no commercial backing, no documentation site beyond the README. The README is accurate but assumes Cashier fluency — it will not teach your team Stripe billing. Plan for internal ownership: designate one engineer as the billing subject-matter expert, and expect that person to read package source when edge cases arise. If the revenue path cannot tolerate best-effort upstream support, fork the package into your monorepo; the surface area is small enough that this is a credible option rather than an overreaction.
Scaling. Billing volume is bounded by Stripe, not by this package — no scaling concern on the payment side. The scaling risk is on the read path: requiresTenantSubscription() gates tenant access, which means a subscription check runs on tenant-scoped requests. Confirm how that check resolves state (cached column vs. live Stripe/DB lookup) and cache if necessary. Plan configuration is a static PHP array, so adding or retiring plans is a code change plus a deploy — acceptable, but it means pricing changes are release events, not admin actions. If marketing needs self-service plan changes, that is custom work on top.
Failure modes. Rank by severity:
plans, wrong product ID, or an over-eager gate blocks every tenant from the panel. Mitigation: feature-flag the gate; ship an artisan command or bypass for support staff.ENV() values baked at config:cache time; a deploy that caches before secrets are injected yields null price IDs. Enforce ordering in the pipeline.Ramp-up. Shallow for a Laravel developer familiar with Filament — an afternoon to first working test-mode subscription, roughly a week to production confidence. The real ramp is domain ramp: Stripe products, prices, trials, tax IDs, promotion codes, metered pricing, and webhook semantics. That knowledge lives in Stripe's docs and Cashier's source, not in this package. Budget accordingly and do not mistake the package's simplicity for a simplification of the billing problem.
How can I help you explore Laravel packages today?