> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Cashier (Stripe)

> Use Laravel Cashier (Stripe) to implement subscription billing, one-time charges, invoice downloads, and Stripe webhook handling.

## Overview

Laravel Cashier (Stripe) is Laravel's official package for Stripe billing. You can use it to create and manage subscriptions, run one-time charges, retrieve invoices, and handle Stripe webhooks through a fluent API.

## Installation and configuration

Install Cashier and run its database migrations.

```shell theme={null}
composer require laravel/cashier
php artisan vendor:publish --tag="cashier-migrations"
php artisan migrate
```

Add the `Billable` trait to your billable model.

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}
```

Set Stripe keys in `.env`.

```ini theme={null}
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
```

## Customer management

Use `createOrGetStripeCustomer()` when you want to safely fetch or create the Stripe customer record.

```php theme={null}
$stripeCustomer = $user->createOrGetStripeCustomer();
```

Use `createAsStripeCustomer()` when you explicitly want to create a customer first.

```php theme={null}
$stripeCustomer = $user->createAsStripeCustomer();
```

## Subscriptions

### Create a subscription

Create a subscription with `newSubscription()` and `create()`.
Pass a Payment Method ID (for example, from Stripe.js) as `$paymentMethodId`.

```php theme={null}
$user->newSubscription('default', 'price_monthly')
    ->create($paymentMethodId);
```

`price_monthly` is an example. Use the actual Stripe Price ID from your Stripe dashboard.

### Check status

Use `subscribed()` to check whether a user currently has an active subscription.

```php theme={null}
if ($user->subscribed('default')) {
    // Active subscription...
}
```

### Cancel and resume

```php theme={null}
$user->subscription('default')->cancel();

if ($user->subscription('default')->onGracePeriod()) {
    // The user is on the grace period...
}

$user->subscription('default')->resume();
```

```mermaid theme={null}
stateDiagram-v2
    [*] --> Active
    Active --> GracePeriod: cancel()
    GracePeriod --> Active: resume()
    GracePeriod --> Canceled: grace period ends
    Active --> Canceled: cancelNow()
```

## One-time charges

Use `charge()` for one-time billing. Pass the amount in the lowest currency denomination (for example, `100` means `$1.00` in USD). Use a Stripe Payment Method ID as `$paymentMethodId`.

```php theme={null}
$payment = $user->charge(100, $paymentMethodId);
```

If the charge fails, `charge()` throws an exception.

## Payment Element

Stripe's [Payment Element](https://stripe.com/docs/payments/payment-element) supports multiple payment methods in a single UI component, including cards, Apple Pay, Google Pay, and iDEAL.

### Payment Element for Subscriptions

Create a Setup Intent and pass it to your view.

```php theme={null}
return view('subscribe', [
    'intent' => $user->createSetupIntent()
]);
```

Mount the Payment Element using the Setup Intent's `client_secret`.

```html theme={null}
<div id="payment-element"></div>
<button id="submit">Subscribe</button>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements({
        clientSecret: '{{ $intent->client_secret }}'
    });

    const paymentElement = elements.create('payment');

    paymentElement.mount('#payment-element');

    document.getElementById('submit').addEventListener('click', async () => {
        const { error } = await stripe.confirmSetup({
            elements,
            confirmParams: {
                return_url: '{{ route("subscription.complete") }}',
            },
        });

        if (error) {
            // Display "error.message" to the user...
        }
    });
</script>
```

After Stripe redirects to your `return_url`, use the `setup_intent` query string parameter to retrieve the payment method and create the subscription.

```php theme={null}
use Illuminate\Http\Request;

Route::get('/subscription/complete', function (Request $request) {
    $setupIntent = $request->user()->findSetupIntent(
        $request->setup_intent
    );

    $paymentMethod = $setupIntent->payment_method;

    $request->user()
        ->newSubscription('default', 'price_xxx')
        ->create($paymentMethod);

    return redirect('/dashboard');
})->name('subscription.complete');
```

### Payment Element for Single Charges

For one-off payments, create a Payment Intent with the `pay()` method. Store the Payment Intent ID on an order model so you can retrieve it after Stripe redirects back. The example below assumes an `Order` model with `user_id`, `amount`, `status`, and `stripe_payment_intent_id` columns.

```php theme={null}
use App\Models\Order;
use Illuminate\Http\Request;

Route::post('/pay', function (Request $request) {
    $amount = 1000;

    $payment = $request->user()->pay($amount);

    $order = Order::create([
        'user_id' => $request->user()->id,
        'amount' => $amount,
        'status' => 'pending',
        'stripe_payment_intent_id' => $payment->id,
    ]);

    return view('checkout', [
        'clientSecret' => $payment->client_secret,
        'order' => $order,
    ]);
});
```

Mount the Payment Element and confirm the payment.

```html theme={null}
<div id="payment-element"></div>
<button id="submit">Pay Now</button>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements({
        clientSecret: '{{ $clientSecret }}'
    });

    const paymentElement = elements.create('payment');

    paymentElement.mount('#payment-element');

    document.getElementById('submit').addEventListener('click', async () => {
        const { error } = await stripe.confirmPayment({
            elements,
            confirmParams: {
                return_url: '{{ route("payment.complete") }}',
            },
        });

        if (error) {
            // Display "error.message" to the user...
        }
    });
</script>
```

After the redirect, retrieve the order using the `payment_intent` query parameter. Always verify that the Payment Intent belongs to the authenticated customer and that its status is `succeeded` before fulfilling the order.

```php theme={null}
use App\Models\Order;
use Illuminate\Http\Request;

Route::get('/payment/complete', function (Request $request) {
    $order = Order::where('user_id', $request->user()->id)
        ->where('stripe_payment_intent_id', $request->payment_intent)
        ->firstOrFail();

    $paymentIntent = $request->user()
        ->stripe()
        ->paymentIntents
        ->retrieve($request->payment_intent);

    if ($paymentIntent->customer === $request->user()->stripe_id &&
        $paymentIntent->status === 'succeeded') {
        $order->update(['status' => 'paid']);

        // Fulfill the order...
    }

    return redirect('/dashboard');
})->name('payment.complete');
```

```mermaid theme={null}
sequenceDiagram
    participant User
    participant App as Laravel App
    participant Stripe

    User->>App: POST /pay
    App->>Stripe: pay() creates PaymentIntent
    Stripe-->>App: client_secret
    App-->>User: Render checkout view
    User->>Stripe: confirmPayment()
    Stripe-->>User: Redirect to return_url
    User->>App: GET /payment/complete
    App->>Stripe: Retrieve & verify PaymentIntent
    App->>App: order.status = paid
    App-->>User: Redirect to dashboard
```

## Invoices

Retrieve invoices with `invoices()`.

```php theme={null}
$invoices = $user->invoices();
```

To generate downloadable PDFs, install `dompdf/dompdf` and call `downloadInvoice()`. Pass an invoice ID from the `invoices()` collection as `$invoiceId`.

```shell theme={null}
composer require dompdf/dompdf
```

```php theme={null}
return $user->downloadInvoice($invoiceId);
```

## Webhook setup

Cashier automatically registers a Stripe webhook route and uses `/stripe/webhook` by default. Configure this URL in your Stripe dashboard.

You can create the webhook through Artisan.

```shell theme={null}
php artisan cashier:webhook
```

Exclude `stripe/*` from CSRF protection.

```php theme={null}
->withMiddleware(function (Middleware $middleware): void {
    $middleware->preventRequestForgery(except: [
        'stripe/*',
    ]);
})
```

Set `STRIPE_WEBHOOK_SECRET` in `.env` so Cashier can validate webhook signatures.
