Contents
Billing is one of those things that looks straightforward until you're three weeks into building it. Subscriptions are simple. Then you need trials. Then proration when someone upgrades mid-cycle. Then invoice history. Then cancellations that don't immediately cut off access. Then webhooks for failed payments.
Each piece is manageable on its own. Together they're a project. Founders who try to hand-roll Stripe integration routinely spend a month on billing infrastructure before they've written a single line of actual product code.
Laravel Cashier solves this. It's Stripe's official Laravel package, maintained by the Laravel team, and it handles the entire billing lifecycle. I've used it on half a dozen SaaS products. The setup takes an afternoon, not a month.
What Cashier Does Out of the Box
Cashier wraps Stripe's API in a clean, Eloquent-style interface. The Billable trait is the entry point — add it to your User model and you get a full set of billing methods:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
From there, creating a subscription is a single chain:
$user->newSubscription('default', 'price_pro_monthly')
->trialDays(14)
->create($paymentMethodId);
That one call creates the Stripe customer if it doesn't exist, sets up the subscription, applies a 14-day trial, and stores the subscription state in your database. No manual API calls. No parsing raw Stripe responses.
The Common Billing Scenarios, All Covered
Plan upgrades and downgrades with proration. When a user moves from a $29 plan to a $79 plan mid-cycle, Stripe calculates what they owe for the remaining days. Cashier exposes this cleanly:
$user->subscription('default')->swap('price_pro_annual');
Proration happens automatically based on your Stripe settings. You don't write the math.
Cancellations that respect the billing cycle. Most SaaS products cancel at period end — the user keeps access until they've used what they paid for:
$user->subscription('default')->cancel();
The subscription status becomes canceled in your database, but $user->subscribed() returns true until the period ends. Your middleware can check this and keep giving access. When the period ends, the webhook fires, status updates, access revokes. It just works.
Invoice history for your users. Every payment is an invoice. Surfacing them in your app is a few lines:
foreach ($user->invoices() as $invoice) {
echo $invoice->date()->toFormattedDateString();
echo $invoice->total();
echo $invoice->invoice_pdf; // direct link to PDF
}
Users can download their own invoices without emailing you. That alone saves hours of support time per month.
Handling Webhooks (The Part Everyone Skips)
Here's where most DIY Stripe integrations fall apart. Webhooks are how Stripe tells your app about events — failed payments, subscription renewals, disputes. If you ignore them, your app's billing state drifts from Stripe's reality.
Cashier includes a webhook controller that handles all of this:
// routes/web.php
Route::stripeWebhooks('stripe/webhook');
Point your Stripe dashboard at that endpoint, run php artisan cashier:webhook to register it automatically, and Cashier handles invoice.payment_failed, customer.subscription.deleted, and the other critical events. It updates your database. It fires Laravel events you can hook into for things like sending dunning emails.
This is worth spending 20 minutes to set up correctly. It's the part that keeps your billing data accurate over time.
What Cashier Doesn't Do
Cashier is Stripe-specific. If you need to support multiple payment processors, you'll need to abstract above it. It also doesn't build your pricing page, your checkout UI, or your customer portal — though Stripe's hosted portal is one line to redirect to:
return $user->redirectToBillingPortal(route('dashboard'));
That gets you a full Stripe-hosted billing management page — update card, cancel subscription, download invoices — with zero frontend work.
The Honest Timeline
With Cashier, a working billing system — subscriptions, trials, upgrades, cancellations, invoice history, webhook handling — takes one to two days of focused work. That includes reading the docs, wiring up the frontend payment form with Stripe Elements, and testing the edge cases.
Without Cashier, the same system takes weeks. And it'll have bugs in the proration math.
For a SaaS founder, this is one of the clearest cases where reaching for the right tool directly translates to shipping faster and spending more time on what makes your product worth paying for.
Building a SaaS and need billing done right? Let's talk.
Billing is one piece of the SaaS puzzle. See how Pixelworx handles the full build — from architecture to launch — on the SaaS development service page.