laravel/cashier
Laravel Cashier adds a fluent Stripe subscription billing layer to Laravel. Manage plans, trials, coupons, quantities, swaps, cancellations with grace periods, and invoicing, including PDF invoice generation, with minimal boilerplate.
Installation:
composer require laravel/cashier stripe/stripe-php
Publish the migration and config:
php artisan vendor:publish --provider="Laravel\Cashier\CashierServiceProvider"
php artisan migrate
Configure Stripe:
Add your Stripe API keys to .env:
STRIPE_KEY=your_stripe_secret_key
STRIPE_ENDPOINT_SECRET=your_webhook_signing_secret
First Use Case: Attach a Stripe customer to a user model:
use Laravel\Cashier\Billable;
class User extends Authenticatable implements Billable
{
use Billable;
}
Create a subscription in a controller:
$user->newSubscription('monthly', 'price_123')->create($paymentMethodId);
Stripe\Webhook middleware in routes/web.php:
Route::post('/stripe/webhook', [WebhookController::class, 'handle'])->middleware('signed');
Stripe::fake() in tests:
Stripe::fake();
$user->newSubscription('basic')->create('token_123');
Create/Update:
// Create a new subscription
$user->newSubscription('premium', 'price_123')
->withEvent('premium_upgrade')
->create($paymentMethodId);
// Switch plans
$user->subscription('premium')->swap('price_456');
// Pause/resume
$user->subscription('premium')->pause();
$user->subscription('premium')->resume();
Cancellation:
// Cancel immediately
$user->subscription('premium')->cancel();
// Cancel at end of period
$user->subscription('premium')->cancelNow(); // Immediate
$user->subscription('premium')->cancelAtPeriodEnd(); // Grace period
One-time Payments:
$user->charge(1000); // $10.00
$user->charge(1000, ['description' => 'Premium Access']);
Payment Methods:
// Add a payment method
$user->addPaymentMethod($paymentMethodId);
// Set default
$user->setDefaultPaymentMethod($paymentMethodId);
// Update payment method
$user->updateDefaultPaymentMethod($newPaymentMethodId);
Generate Invoices:
$invoice = $user->invoices()->latest()->first();
$invoice->download(); // Download PDF
$invoice->send(); // Email PDF
Upcoming Invoices:
$upcomingInvoice = $user->upcomingInvoice();
Apply Coupons:
$user->newSubscription('basic', 'price_123')
->withCoupon('SUMMER20')
->create($paymentMethodId);
Trials:
$user->newSubscription('basic', 'price_123')
->trialDays(7)
->create($paymentMethodId);
Stripe Checkout:
$session = $user->createSetupIntent();
// Or for subscriptions:
$session = $user->createSubscription('premium', 'price_123');
return redirect()->away($session->url);
Custom Checkout:
$session = $user->createCheckoutSession([
'mode' => 'subscription',
'line_items' => [
['price' => 'price_123', 'quantity' => 1],
],
'success_url' => route('checkout.success'),
'cancel_url' => route('checkout.cancel'),
]);
Middleware: Use EnsureUserHasValidSubscription to protect routes:
Route::get('/premium', function () {
// ...
})->middleware('subscribed:premium');
Events: Listen to Cashier events (e.g., SubscriptionCreated):
event(new SubscriptionCreated($subscription, $user));
Testing:
Stripe::fake();
$user->newSubscription('basic')->create('token_123');
$this->assertTrue($user->subscribed('basic'));
Webhooks: Handle Stripe events in WebhookController:
public function handle(Request $request)
{
$payload = $request->getContent();
\Stripe\Webhook::constructEvent(
$payload, $request->header('Stripe-Signature'),
config('cashier.webhook_secret')
);
// Handle event (e.g., invoice.paid)
}
Webhook Signing:
STRIPE_ENDPOINT_SECRET matches your Stripe webhook signing secret..env and regenerate if needed in Stripe Dashboard.Payment Method Handling:
defaultPaymentMethod() may return null if no default is set.null before using:
if ($user->hasPaymentMethod()) {
$user->defaultPaymentMethod();
}
Subscription Swaps:
prorate: true in the swap method:
$user->subscription('premium')->swap('price_456', ['prorate' => true]);
Invoice Generation:
dompdf or wkhtmltopdf. Install via:
composer require barryvdh/laravel-dompdf
tax_rates in Stripe Dashboard.Testing:
Stripe::fake() must be called before any Stripe interaction in tests.Stripe::testTokens() for test payment methods:
Stripe::fake(['tokens' => ['test_token_123']]);
Cancellation Grace Periods:
cancelAtPeriodEnd() does not immediately cancel; the subscription remains active until the end of the billing cycle.cancelNow() for immediate cancellation.Stripe API Limits:
\Laravel\Cashier\Cashier::flushWebhookQueue();
Log Webhook Events:
Add logging in WebhookController to debug events:
\Log::info('Webhook event:', ['event' => $event->type, 'data' => $event->data]);
Stripe CLI: Use the Stripe CLI to test webhooks locally:
stripe listen --forward-to localhost:8000/stripe/webhook
Cashier Logs:
Enable debug mode in config/cashier.php:
'debug' => env('CASHIER_DEBUG', false),
Custom Webhook Logic:
Extend WebhookController to handle custom events:
public function handleCustomerSubscriptionDeleted()
{
// Custom logic for subscription deletions
}
Custom Invoice Views:
Override the invoice PDF template in resources/views/vendor/cashier/invoice.blade.php.
Custom Subscription Logic: Use model observers or accessors to add custom fields:
public function getCustomSubscriptionAttribute()
{
return $this->subscription('premium')->customField;
}
Multi-Currency Support:
Cashier supports multiple currencies via Stripe. Configure in config/cashier.php:
'currency' => 'usd', // Default currency
Basil (Connect) Support: For Stripe Connect (payments to connected accounts), use:
$user->newSubscription('premium', 'price_123')
->asStripeCustomer()
->create($paymentMethodId);
How can I help you explore Laravel packages today?