chargebee/cashier
Laravel Cashier for Chargebee provides an expressive, fluent interface for subscription billing with Chargebee. It handles subscriptions, invoices, payment methods, and recurring payments, letting Laravel developers integrate Chargebee billing quickly.
Minimum viable setup
composer require chargebee/cashier-chargebee
php artisan vendor:publish --tag=cashier-config
php artisan vendor:publish --tag=cashier-migrations
php artisan migrate
CHARGEBEE_SITE, CHARGEBEE_API_KEY, and an environment switch (test/live) in config/cashier.php. There is no equivalent of a single STRIPE_SECRET string — the site name is part of every endpoint URL.Billable trait to your User (or a Team model) and run the published migration, which adds the Chargebee customer reference and trial_ends_at to the table. Confirm the actual column names in the published migration rather than assuming chargebee_id.php artisan tinker, create a Chargebee customer for a test user and read it back. If this fails, it's config (wrong site, test key against live site) — not code.Where to look first
src/Billable.php and src/Concerns/ — this is the real API reference. The README is thin, and the package deliberately mirrors laravel/cashier's surface, so treat the official Stripe Cashier docs as the semantic guide and the source as the authority on what's actually implemented.routes/ or the service provider — to see whether the webhook route is registered for you or must be added manually.First use case to target: a single-plan SaaS with a 14-day trial, card capture via Chargebee's hosted checkout/portal, and a "manage billing" link. Don't start with proration, multi-plan upgrades, or usage billing — those are where Chargebee's model diverges most from Cashier's Stripe-shaped assumptions.
Keep card data off your server. Tokenize with Chargebee.js / hosted pages in the browser, then create the subscription server-side from the resulting payment source. Your Laravel code should never see a PAN.
Gate features with middleware, not view logic. Cashier's subscribed / subscription:default route middleware aliases (if registered by this package's provider — verify) keep controllers clean:
Route::middleware(['auth', 'subscribed'])->group(fn () => Route::get('/app', Dashboard::class));
Webhook-first state management. Any change made inside the Chargebee dashboard or self-service portal — dunning failure, cancellation, plan swap, card update — reaches your app only via webhook. Treat your subscriptions table as a cache. Register the webhook URL in the Chargebee dashboard, enable its basic-auth/webhook authentication, and make sure the route is CSRF-exempt.
Reconcile nightly. Because the local table is a cache, schedule a job that re-fetches active subscriptions from Chargebee for recently-active users and repairs drift. This one job will save you more support tickets than any other code you write.
Trials: pick one source of truth. trial_ends_at exists both locally and in Chargebee. Decide that Chargebee's trial wins for billing purposes and that onTrial() is only a UX hint; otherwise expired trials keep working after a failed webhook.
Portal over custom UI. Point "manage billing" at the Chargebee self-service portal URL rather than building card-update and cancel screens yourself — it's the fastest path to a compliant billing page.
Testing. Provision a dedicated Chargebee test site for CI. Note that the Chargebee PHP SDK historically performs its own HTTP calls, so Laravel's Http::fake() will not intercept them — plan on sandbox integration tests, or wrap the client behind your own interface so you can fake at that seam.
laravel/cashier; check whether it reuses the Laravel\Cashier namespace before installing. If it does, you cannot run it alongside the official Stripe Cashier in the same app.src/ for the method before designing a flow around it.cancel() vs cancelNow(). cancel() leaves the user in a grace period (onGracePeriod()) until period end; cancelNow() ends access immediately. Most "user still has access after cancelling" bugs are this distinction.config('cashier.*') is read at boot. After changing CHARGEBEE_* env values in production, run php artisan config:cache again — a stale cached config against a live site is a painful half-hour.queue:restart).composer.json, read the diff on every upgrade, and budget an afternoon to read src/ end to end before committing the architecture — that afternoon is cheaper than discovering a missing method three sprints in.How can I help you explore Laravel packages today?