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

> Explicamos el flujo básico para implementar suscripciones, pagos únicos, facturas y webhooks usando Laravel Cashier (Stripe).

## Visión general

Laravel Cashier (Stripe) es el paquete oficial para gestionar las funciones de facturación de Stripe desde Laravel. Puedes implementar con una API unificada la creación, consulta de estado y cancelación de suscripciones, los pagos únicos, la descarga de facturas y el procesamiento de webhooks.

## Instalación y configuración

Primero, instala Cashier y crea las tablas necesarias.

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

Después, añade el trait `Billable` al modelo facturable, como `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;
}
```

Configura las claves de Stripe en `.env`.

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

## Gestión de clientes

Cuando no puedes asegurar si el cliente de Stripe ya existe, usa `createOrGetStripeCustomer()`.

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

Si quieres crearlo explícitamente, usa `createAsStripeCustomer()`.

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

## Suscripciones

### Nueva creación

Comienza una suscripción con `newSubscription()` y `create()`.
En `$paymentMethodId` pasa el Payment Method ID obtenido con Stripe.js o similar.

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

`price_monthly` es un ejemplo. Al implementarlo indica el Price ID real que hayas creado en el dashboard de Stripe.

### Comprobación de estado

Con `subscribed()` puedes comprobar si la suscripción está activa.

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

### Cancelación y reanudación

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

## Pago único (Charge)

Con `charge()` puedes cobrar una única vez. Pasa el importe en la unidad mínima de la moneda (por ejemplo, en USD `100` es `$1.00`).
Aquí también en `$paymentMethodId` se usa el Payment Method ID creado en Stripe.

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

Si el cobro falla, `charge()` lanza una excepción.

## Payment Element

Con el [Payment Element](https://stripe.com/docs/payments/payment-element) de Stripe puedes manejar de forma unificada varios métodos de pago: tarjeta, Apple Pay, Google Pay, iDEAL, etc.

### Uso en suscripciones

Crea un Setup Intent y pásaselo a la vista.

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

Monta el Payment Element en la vista Blade y pásale el `client_secret` del Setup Intent.

```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) {
            // Mostrar el mensaje de error al usuario...
        }
    });
</script>
```

La URL de redirección de Stripe recibe el parámetro `setup_intent`. Úsalo para obtener el Payment Method ID y crear la suscripción.

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

### Uso en pago único

Para un pago único crea un Payment Intent con `pay()`. El modelo Order debe tener las columnas `user_id`, `amount`, `status` y `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 el Payment Element y confirma el pago.

```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) {
            // Mostrar el mensaje de error al usuario...
        }
    });
</script>
```

Tras la redirección busca el pedido por el parámetro `payment_intent`, comprueba que el Payment Intent pertenece al usuario autenticado y está en estado `succeeded`, y confirma el pedido.

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

        // Procesamiento para confirmar el pedido...
    }

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

```mermaid theme={null}
sequenceDiagram
    participant User as Usuario
    participant App as Aplicación<br>Laravel
    participant Stripe

    User->>App: POST /pay
    App->>Stripe: Crea PaymentIntent con pay()
    Stripe-->>App: client_secret
    App-->>User: Muestra la vista de checkout
    User->>Stripe: confirmPayment()
    Stripe-->>User: Redirección a return_url
    User->>App: GET /payment/complete
    App->>Stripe: Obtiene y verifica el PaymentIntent
    App->>App: order.status = paid
    App-->>User: Redirección al dashboard
```

## Facturas

Obtén la lista de facturas con `invoices()`.

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

Para descargarlas en PDF, instala `dompdf/dompdf` y usa `downloadInvoice()`. En `$invoiceId` pasa el ID de una factura obtenido con `invoices()`.

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

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

## Configuración de webhooks

Cashier registra automáticamente la ruta del webhook de Stripe, que por defecto usa `/stripe/webhook`. Configura esta URL en el dashboard de Stripe.

Puedes crear el webhook con `cashier:webhook`.

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

Excluye `stripe/*` de la protección CSRF.

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

Si configuras `STRIPE_WEBHOOK_SECRET` en `.env`, el middleware de verificación de firmas de Cashier validará las solicitudes de webhook.


## Related topics

- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Actualización de Laravel — Junio de 2026](/es/blog/changelog/202606.md)
- [Protección CSRF](/es/csrf.md)
- [Estructura de aplicación en Laravel 11 y posteriores](/es/advanced/app-structure.md)
- [Contenedor de servicios](/es/service-container.md)
