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

> Il flusso base per implementare abbonamenti, pagamenti singoli, fatture e webhook con Laravel Cashier (Stripe).

## Panoramica

Laravel Cashier (Stripe) è il pacchetto ufficiale per gestire le funzionalità di fatturazione di Stripe da Laravel. Implementi con un'API unificata creazione/verifica/cancellazione di abbonamenti, pagamenti singoli, download di fatture e gestione dei webhook.

## Installazione e configurazione

Installa Cashier e crea le tabelle necessarie.

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

Aggiungi il trait `Billable` al modello fatturabile (es. `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;
}
```

Imposta le chiavi Stripe in `.env`.

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

## Gestione dei customer

Se il Customer Stripe potrebbe non esistere, usa `createOrGetStripeCustomer()`.

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

Per crearlo esplicitamente usa `createAsStripeCustomer()`.

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

## Subscription

### Creazione

Con `newSubscription()` e `create()`.
Passa a `$paymentMethodId` il Payment Method ID ottenuto con Stripe.js.

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

`price_monthly` è un esempio: usa il vero Price ID definito nella dashboard Stripe.

### Verifica dello stato

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

### Cancellazione e ripresa

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

if ($user->subscription('default')->onGracePeriod()) {
    // L'utente è nel grace period...
}

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

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

## Pagamenti singoli (Charge)

Con `charge()` esegui un singolo addebito. L'importo è nell'unità minima della valuta (per EUR `100` = `1,00 €`).
Anche qui usi `$paymentMethodId`.

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

Se l'addebito fallisce, `charge()` lancia un'eccezione.

## Payment Element

Con il [Payment Element](https://stripe.com/docs/payments/payment-element) di Stripe puoi gestire più metodi (carta, Apple Pay, Google Pay, iDEAL) in modo uniforme.

### Con Subscription

Crea un Setup Intent e passalo alla vista.

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

Nella vista Blade monta il Payment Element passandogli il `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) {
            // Mostra l'errore all'utente...
        }
    });
</script>
```

Nel `return_url` viene appeso il parametro `setup_intent`. Usalo per ottenere il Payment Method ID e creare l'abbonamento.

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

### Con pagamento singolo

Per il pagamento singolo usa `pay()` per creare un Payment Intent. Il modello Order richiede colonne `user_id`, `amount`, `status`, `stripe_payment_intent_id`.

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

Monta il Payment Element per confermare.

```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) {
            // Mostra l'errore all'utente...
        }
    });
</script>
```

Dopo il redirect, con `payment_intent` recuperi l'Order e verifichi che il Payment Intent sia dell'utente autenticato ed in stato `succeeded`, poi conferma l'ordine.

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

        // Conferma ordine...
    }

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

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

    User->>App: POST /pay
    App->>Stripe: pay() crea PaymentIntent
    Stripe-->>App: client_secret
    App-->>User: Mostra la vista checkout
    User->>Stripe: confirmPayment()
    Stripe-->>User: Redirect a return_url
    User->>App: GET /payment/complete
    App->>Stripe: Recupera e verifica il PaymentIntent
    App->>App: order.status = paid
    App-->>User: Redirect alla dashboard
```

## Fatture

Ottieni la lista delle fatture con `invoices()`.

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

Per scaricare il PDF installa `dompdf/dompdf` e usa `downloadInvoice()`. Passa a `$invoiceId` l'ID recuperato da `invoices()`.

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

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

## Configurazione dei webhook

Cashier registra automaticamente la route dei webhook Stripe e usa di default `/stripe/webhook`. Imposta questo URL nella dashboard Stripe.

Puoi creare l'endpoint con `cashier:webhook`.

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

Escludi `stripe/*` dalla protezione CSRF.

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

Impostando `STRIPE_WEBHOOK_SECRET` in `.env`, il middleware di Cashier verifica la firma delle richieste webhook.


## Related topics

- [Aggiornamento da Laravel 10 a 11](/it/blog/upgrade-10-to-11.md)
- [Aggiornamenti Laravel di giugno 2026](/it/blog/changelog/202606.md)
- [Protezione CSRF](/it/csrf.md)
- [Struttura dell'applicazione da Laravel 11](/it/advanced/app-structure.md)
- [Service Container](/it/service-container.md)
