Laravel Cashier + Stripe setup for SaaS billing (complete guide)

A practical guide to setting up Laravel Cashier with Stripe for SaaS billing, including products, prices, checkout, webhooks, trials, and production reliability.

R
Raşit Apalak
·
·
Updated
·
6 min read

Quick Answer

Quick answer: Laravel Cashier + Stripe setup for SaaS billing (complete guide)

A practical guide to setting up Laravel Cashier with Stripe for SaaS billing, including products, prices, checkout, webhooks, trials, and production reliability.

See supporting documentation

Laravel Cashier + Stripe setup for SaaS billing (complete guide)

If you are building a Laravel SaaS, Stripe + Laravel Cashier is one of the fastest paths to production billing. The key is not only getting checkout working, but also making subscription lifecycle events reliable in production.

This guide walks through the full setup: plans, checkout, webhooks, trials, portal access, and common failure points.

Quick answer: how do you set up Stripe billing in Laravel?

At a high level:

  1. Configure Stripe keys and install Cashier
  2. Create products and prices in Stripe
  3. Map plan IDs in your app config
  4. Create checkout/subscription flows
  5. Process Stripe webhooks reliably
  6. Gate product access by subscription state

If each of these is implemented cleanly, you can run SaaS billing with predictable behavior and fewer support incidents.

What Laravel Cashier handles well

Laravel Cashier gives you a practical abstraction for:

  • creating and managing subscriptions
  • handling trial periods
  • swapping plans
  • canceling/reactivating subscriptions
  • opening Stripe customer portal sessions

Use it as your billing workflow layer, while Stripe remains the source of truth for charges and invoices.

Step 1: Install and configure Cashier

Use this as your baseline setup sequence.

1) Install the package and run migrations

composer require laravel/cashier
php artisan vendor:publish --tag="cashier-migrations"
php artisan migrate

This creates Cashier billing tables (subscriptions, subscription items, etc.).

2) Add Stripe and billing env variables

STRIPE_KEY=pk_test_xxx
STRIPE_SECRET=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
 
# optional but common
CASHIER_CURRENCY=usd
CASHIER_CURRENCY_LOCALE=en

Keep test and live keys strictly separated by environment.

3) Make your billable model use Cashier

In most Laravel SaaS apps, User is the billable model. Add Billable:

<?php
 
namespace App\Models;
 
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Cashier\Billable;
 
class User extends Authenticatable
{
    use Billable;
}

If your billing entity is a workspace/team model, add Billable there instead and keep subscription checks workspace-scoped.

4) Ensure webhook endpoint is reachable and CSRF-safe

Cashier expects Stripe webhooks at:

/stripe/webhook

Make sure your app allows Stripe to post to this endpoint without CSRF blocking, then configure the same endpoint URL in Stripe Dashboard for each environment.

5) Sanity-check with a test subscription

Before building full UI flows:

  • create one Stripe test product/price
  • create a test checkout/subscription in app code
  • confirm subscription rows are created/updated locally
  • send a Stripe test webhook and confirm processing succeeds

Implementation notes:

  • keep billing config centralized (config file or billing service)
  • ensure your billable model relationship is stable and tested
  • avoid scattering Stripe price IDs across controllers

Step 2: Create Stripe products and prices intentionally

In Stripe, create products and the exact price models you need:

  • monthly and yearly prices
  • optional trials
  • seat-based price variants (if team billing)

Keep IDs explicit and environment-specific (test vs live).

Recommended pattern:

  • define plan keys in app config (for example starter_monthly, pro_yearly)
  • map plan keys to Stripe price IDs in env/config
  • use plan keys in your application logic instead of hardcoding price IDs directly

Step 3: Build checkout and subscription creation flows

A reliable SaaS flow usually looks like this:

  1. user/workspace selects a plan
  2. app creates Stripe checkout session (or direct subscription flow)
  3. user completes payment in Stripe
  4. webhook confirms subscription state
  5. app enables paid access based on confirmed state

Important rule: do not trust browser redirect alone as billing truth.
Use webhooks as the system of record for final subscription state.

Step 4: Webhooks are where billing reliability is won

Your webhook handler should be:

  • signature-verified
  • idempotent
  • order-tolerant
  • observable (logs + alerts)

Core events to handle for SaaS lifecycle:

  • subscription created/updated/canceled
  • invoice payment succeeded/failed
  • trial ending (if used)

Operational checklist:

  • store processed event IDs to prevent duplicate side effects
  • make access updates retry-safe
  • alert on repeated webhook failures
  • keep manual replay runbook ready

Step 5: Gate access with subscription state, not assumptions

Your app should check subscription status in one centralized layer (policy/middleware/service), then enforce feature access consistently.

At minimum, define behavior for:

  • active subscription
  • trialing
  • past_due
  • canceled but still in grace period
  • fully ended access

This prevents inconsistent behavior between dashboard pages, APIs, and background jobs.

Step 6: Add trials, upgrades, downgrades, and cancellation paths

For production-ready SaaS billing, implement lifecycle flows clearly:

  • start trial from selected plan
  • upgrade immediately when user picks higher tier
  • downgrade with predictable effective date
  • cancel with clear access end date and reactivation option

User confusion here creates support load. Keep billing status and next charge date visible in your UI.

Step 7: Support team and seat-based billing (if applicable)

If your product is workspace/team-based:

  • map Stripe quantity to seat count
  • sync quantity when seats change
  • avoid race conditions during invite/remove member events

A robust approach is to centralize seat sync in one domain service and queue updates safely.

For a deeper implementation model, see: How to build a SaaS app with Laravel in 2026

Step 8: Test mode and production cutover

Before going live:

  • test all checkout outcomes in Stripe test mode
  • simulate webhook retries and duplicate delivery
  • test failed payment and recovery paths
  • validate cancellation and grace-period behavior
  • verify billing portal round-trips

For live cutover, use a checklist and keep rollback options documented.

Common mistakes to avoid

  • relying on redirect success pages as payment truth
  • no idempotency in webhook processing
  • hardcoding Stripe price IDs in multiple places
  • mixing billing logic into unrelated controllers
  • no support visibility for current subscription state

Practical architecture recommendation

Use this split:

  • Stripe: payment execution and invoice source of truth
  • Cashier: Laravel-native billing workflow abstraction
  • Your app: feature gating and product-specific access rules

This keeps the system understandable as you scale plan complexity.

Final takeaway

Laravel Cashier + Stripe is a strong default for SaaS billing if you prioritize webhook reliability, centralized access gating, and clean plan mapping from day one.

If you want to skip most of the setup and start from a production-ready baseline, explore:

Share this post:

Ready to ship faster?

Build your SaaS with a production-ready foundation

Launch with authentication, billing, tenancy, and team workflows already in place, then focus on the features that make your product unique.

Related posts