laravel/cashier
Laravel Cashier offers a fluent Laravel interface for Stripe subscription billing. Manage subscriptions, coupons, plan swaps, quantities, and cancellation grace periods, with support for generating invoice PDFs—all while handling the boilerplate billing code.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require laravel/cashier
Publish the migration and config:
php artisan vendor:publish --provider="Laravel\Cashier\CashierServiceProvider"
php artisan migrate
Configure Stripe:
Add your Stripe secret key to .env:
STRIPE_KEY=your_stripe_secret_key
User Model Setup:
Use the HasApiTokens and HasBillingInfo traits in your User model:
use Laravel\Cashier\HasBillingInfo;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasBillingInfo;
}
First Use Case: Create a subscription for a user:
$user = User::find(1);
$user->newSubscription('monthly', 'price_123')->create($paymentMethodId);
$user->newSubscription('premium', 'price_123')
->withQuantity(2)
->create($paymentMethodId);
$user->subscription('premium')->swap('price_456');
$user->subscription('premium')->cancel();
// With grace period
$user->subscription('premium')->cancelNow();
$user->charge(1000); // $10.00
$user->updateDefaultPaymentMethod($newPaymentMethodId);
$session = $user->createCheckoutSession([
'success_url' => route('checkout.success'),
'cancel_url' => route('checkout.cancel'),
'line_items' => [
[
'price' => 'price_123',
'quantity' => 1,
],
],
]);
use Laravel\Cashier\Http\Controllers\WebhookController;
Route::post('/stripe/webhook', [WebhookController::class, 'handleWebhook']);
$invoice = $user->invoices()->latest()->first();
$pdf = $invoice->download();
$user->invoices()->get();
$user->newSubscription('premium', 'price_123')
->withCoupon('SUMMER20')
->create($paymentMethodId);
$user->newSubscription('premium', 'price_123')
->trialDays(7)
->create($paymentMethodId);
Use observers to log subscription events:
class UserObserver
{
public function created(User $user)
{
if ($user->subscribed('premium')) {
// Send welcome email
}
}
}
Protect routes based on subscription status:
public function handle(Request $request, Closure $next)
{
if (!$request->user()->subscribed('premium')) {
abort(403);
}
return $next($request);
}
Use Cashier facade for testing:
$stripe = new \Laravel\Cashier\Stripe\StripeService();
$stripe->stub()->subscription()->create();
Extend the WebhookController for custom logic:
class CustomWebhookController extends WebhookController
{
public function handleWebhook(Request $request)
{
if ($request->type === 'invoice.paid') {
// Custom logic
}
parent::handleWebhook($request);
}
}
STRIPE_WEBHOOK_SECRET..env:
STRIPE_WEBHOOK_SECRET=whsec_...
subscribed() returns false even though Stripe shows an active subscription.$user->subscription('premium')->refresh();
Or use sync() to force a refresh:
$user->sync();
charge() or create() fails silently.try {
$user->charge(1000);
} catch (\Exception $e) {
\Log::error('Stripe charge failed: ' . $e->getMessage());
}
updateQuantity() with caution:
$user->subscription('premium')->updateQuantity(3);
Ensure the plan supports quantity adjustments in Stripe.download() fails with "Invoice not found."$invoice = $user->invoices()->where('status', 'paid')->latest()->first();
if ($invoice) {
$pdf = $invoice->download();
}
Enable Stripe logging in config/cashier.php:
'logging' => [
'enabled' => true,
'log_level' => 'debug',
],
Use dd() to inspect Stripe objects:
dd($user->subscription('premium')->asStripeSubscription());
Use stripe-cli to test webhooks:
stripe listen --forward-to localhost:8000/stripe/webhook
Override default invoice settings in config/cashier.php:
'invoice_settings' => [
'custom_fields' => [
[
'name' => 'Order ID',
'value' => 'ORD123',
],
],
],
Ensure tax settings are configured in Stripe and Cashier:
$user->newSubscription('premium', 'price_123')
->withTaxRate('txr_123')
->create($paymentMethodId);
For usage-based billing, configure billing_cycle_anchor:
$user->newSubscription('flexible', 'price_123')
->withBillingCycleAnchor(now())
->create($paymentMethodId);
Extend the create() method for custom logic:
$subscription = $user->newSubscription('premium', 'price_123');
$subscription->create($paymentMethodId, [
'metadata' => ['custom_key' => 'custom_value'],
]);
Use invoiceCustomFields in config/cashier.php:
'invoice_custom_fields' => [
'Order ID' => 'order_id',
],
Extend the WebhookController for custom logic:
class CustomWebhookController extends WebhookController
{
public function handleCustomerSubscriptionCreated(Request $request)
{
// Custom logic
parent::handleCustomerSubscriptionCreated($request);
}
}
For custom payment methods (e.g., SEPA, iDEAL):
$source = $user->createAsStripeCustomer([
'source' => $sourceToken,
]);
refresh() sparingly.$user->invoices()->chunk(1
How can I help you explore Laravel packages today?