Laravel Cashier provides an expressive interface for Stripe subscription billing. It handles the complexity of webhooks, proration, and subscription management. At ZIRA Software, Cashier powers billing for our SaaS products.
Installation and Setup
composer require laravel/cashier
php artisan migrate
// config/services.php
'stripe' => [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
// User model
use Laravel\Cashier\Billable;
class User extends Model
{
use Billable;
}
Creating Subscriptions
// Basic subscription
$user->newSubscription('main', 'monthly-plan')->create($paymentMethod);
// With trial period
$user->newSubscription('main', 'monthly-plan')
->trialDays(14)
->create($paymentMethod);
// Apply coupon
$user->newSubscription('main', 'monthly-plan')
->withCoupon('launch-special')
->create($paymentMethod);
// Multiple subscriptions
$user->newSubscription('main', 'monthly-plan')->create($paymentMethod);
$user->newSubscription('addons', 'storage-addon')->create($paymentMethod);
Checking Subscription Status
// Check if subscribed
if ($user->subscribed('main')) {
// Has active subscription
}
// Check specific plan
if ($user->subscribedToPlan('premium-plan', 'main')) {
// On premium plan
}
// Check trial status
if ($user->onTrial('main')) {
// Currently on trial
}
// Check grace period (cancelled but not expired)
if ($user->subscription('main')->onGracePeriod()) {
// Cancelled but still active
}
// Middleware usage
Route::middleware('subscribed:main')->group(function () {
Route::get('/premium-feature', 'PremiumController@index');
});
Changing Plans
// Swap to different plan
$user->subscription('main')->swap('annual-plan');
// Swap without prorating
$user->subscription('main')->noProrate()->swap('annual-plan');
// Add quantity (per-seat pricing)
$user->subscription('main')->incrementQuantity();
$user->subscription('main')->updateQuantity(5);
Cancelling Subscriptions
// Cancel at end of billing period
$user->subscription('main')->cancel();
// Cancel immediately
$user->subscription('main')->cancelNow();
// Resume cancelled subscription (during grace period)
$user->subscription('main')->resume();
Handling Webhooks
// routes/web.php
Route::post('stripe/webhook', 'WebhookController@handleWebhook');
// app/Http/Controllers/WebhookController.php
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
class WebhookController extends CashierController
{
public function handleInvoicePaymentSucceeded($payload)
{
// Custom logic for successful payment
$user = User::where('stripe_id', $payload['data']['object']['customer'])->first();
Mail::to($user)->send(new PaymentReceived($payload));
}
public function handleCustomerSubscriptionDeleted($payload)
{
// Custom logic for subscription cancellation
parent::handleCustomerSubscriptionDeleted($payload);
$user = User::where('stripe_id', $payload['data']['object']['customer'])->first();
Mail::to($user)->send(new SubscriptionCancelled());
}
}
Invoices
// Get all invoices
$invoices = $user->invoices();
// Get specific invoice
$invoice = $user->findInvoice($invoiceId);
// Download invoice PDF
return $user->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
// Display invoices in view
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->date()->toFormattedDateString() }}</td>
<td>{{ $invoice->total() }}</td>
<td>
<a href="/user/invoice/{{ $invoice->id }}">Download</a>
</td>
</tr>
@endforeach
Subscription Controller Example
class SubscriptionController extends Controller
{
public function store(Request $request)
{
$request->validate([
'plan' => 'required|in:basic,premium,enterprise',
'payment_method' => 'required|string',
]);
$user = auth()->user();
$user->newSubscription('main', $request->plan)
->create($request->payment_method);
return redirect('/dashboard')
->with('success', 'Subscription created successfully!');
}
public function update(Request $request)
{
$request->validate([
'plan' => 'required|in:basic,premium,enterprise',
]);
auth()->user()
->subscription('main')
->swap($request->plan);
return back()->with('success', 'Plan updated!');
}
public function destroy()
{
auth()->user()->subscription('main')->cancel();
return back()->with('success', 'Subscription cancelled.');
}
}
Conclusion
Laravel Cashier eliminates subscription billing complexity. Focus on your product while Cashier handles Stripe integration, webhooks, and invoice generation.
Building a SaaS? Contact ZIRA Software for subscription billing implementation.