> ## 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)

> Een uitleg van de basisflow voor het implementeren van subscription-facturering, eenmalige betalingen, facturen en webhooks met Laravel Cashier (Stripe).

## Overzicht

Laravel Cashier (Stripe) is het officiële package om de betaalfunctionaliteit van Stripe vanuit Laravel te gebruiken. Je kunt met een uniforme API subscriptions aanmaken, de status controleren en opzeggen, eenmalige betalingen uitvoeren, facturen downloaden en webhooks verwerken.

## Installatie en configuratie

Installeer eerst Cashier en maak de benodigde tabellen aan.

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

Voeg vervolgens de `Billable`-trait toe aan het model waarvoor gefactureerd wordt, zoals `App\Models\User`.

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

namespace App\Models;

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

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

Stel je Stripe-sleutels in in `.env`.

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

## Klantbeheer

Als er mogelijk nog geen Stripe Customer is aangemaakt, gebruik je `createOrGetStripeCustomer()`.

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

Wil je expliciet een klant aanmaken, gebruik dan `createAsStripeCustomer()`.

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

## Subscriptions

### Nieuwe subscription aanmaken

Start een subscription met `newSubscription()` en `create()`.
Aan `$paymentMethodId` geef je een Payment Method ID door die je bijvoorbeeld via Stripe.js hebt opgehaald.

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

`price_monthly` is een voorbeeld. Geef bij de implementatie het daadwerkelijke Price ID op dat je in het Stripe-dashboard hebt aangemaakt.

### Status controleren

Met `subscribed()` controleer je of er een actieve subscription is.

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

### Opzeggen en hervatten

```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()
```

## Eenmalige betaling (Charge)

Met `charge()` breng je eenmalig een bedrag in rekening. Geef het bedrag door in de kleinste eenheid van de valuta (bijvoorbeeld: bij USD is `100` gelijk aan `$1.00`).
Ook hier gebruik je voor `$paymentMethodId` een Payment Method ID dat in Stripe is aangemaakt.

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

Als de betaling mislukt, gooit `charge()` een exception.

## Payment Element

Met Stripe's [Payment Element](https://stripe.com/docs/payments/payment-element) kun je meerdere betaalmethoden zoals kaarten, Apple Pay, Google Pay en iDEAL op een uniforme manier afhandelen.

### Gebruik met subscriptions

Maak een Setup Intent aan en geef die door aan de view.

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

Mount het Payment Element in de Blade-view en geef de `client_secret` van de Setup Intent door.

```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) {
            // Toon de foutmelding aan de gebruiker...
        }
    });
</script>
```

Aan de redirect-URL van Stripe wordt een `setup_intent`-parameter toegevoegd. Hiermee haal je het Payment Method ID op en maak je de subscription aan.

```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');
```

### Gebruik bij eenmalige betalingen

Bij een eenmalige betaling maak je met `pay()` een Payment Intent aan. Het Order-model heeft de kolommen `user_id`, `amount`, `status` en `stripe_payment_intent_id` nodig.

```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 het Payment Element en bevestig de betaling.

```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) {
            // Toon de foutmelding aan de gebruiker...
        }
    });
</script>
```

Na de redirect zoek je de Order op via de `payment_intent`-parameter, controleer je dat de Payment Intent van de geauthenticeerde gebruiker is en de status `succeeded` heeft, en bevestig je daarna de bestelling.

```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']);

        // Verwerking om de bestelling te bevestigen...
    }

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

```mermaid theme={null}
sequenceDiagram
    participant User as Gebruiker
    participant App as Laravel-<br>app
    participant Stripe

    User->>App: POST /pay
    App->>Stripe: PaymentIntent aanmaken met pay()
    Stripe-->>App: client_secret
    App-->>User: checkout-view tonen
    User->>Stripe: confirmPayment()
    Stripe-->>User: Redirect naar return_url
    User->>App: GET /payment/complete
    App->>Stripe: PaymentIntent ophalen en verifiëren
    App->>App: order.status = paid
    App-->>User: Redirect naar dashboard
```

## Facturen

Een lijst van facturen haal je op met `invoices()`.

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

Voor het downloaden als PDF installeer je `dompdf/dompdf` en gebruik je `downloadInvoice()`. Geef aan `$invoiceId` een factuur-ID door dat je met `invoices()` hebt opgehaald.

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

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

## Webhooks instellen

Cashier registreert automatisch een route voor Stripe-webhooks en gebruikt standaard `/stripe/webhook`. Stel deze URL in in het Stripe-dashboard.

Je kunt de webhook aanmaken met `cashier:webhook`.

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

Sluit `stripe/*` uit van de CSRF-beveiliging.

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

Als je `STRIPE_WEBHOOK_SECRET` in `.env` instelt, kan de handtekeningverificatie-middleware van Cashier de webhook-requests verifiëren.


## Related topics

- [Upgraden van Laravel 10 naar 11](/nl/blog/upgrade-10-to-11.md)
- [Laravel-updates van juni 2026](/nl/blog/changelog/202606.md)
- [CSRF-bescherming](/nl/csrf.md)
- [Applicatiestructuur van Laravel 11 en later](/nl/advanced/app-structure.md)
- [Service container](/nl/service-container.md)
