Saltar al contenido principal

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.
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

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.
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().
$stripeCustomer = $user->createOrGetStripeCustomer();
Si quieres crearlo explícitamente, usa createAsStripeCustomer().
$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.
$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.
if ($user->subscribed('default')) {
    // Active subscription...
}

Cancelación y reanudación

$user->subscription('default')->cancel();

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

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

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.
$payment = $user->charge(100, $paymentMethodId);
Si el cobro falla, charge() lanza una excepción.

Payment Element

Con el 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.
return view('subscribe', [
    'intent' => $user->createSetupIntent()
]);
Monta el Payment Element en la vista Blade y pásale el client_secret del Setup Intent.
<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.
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.
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.
<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.
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');

Facturas

Obtén la lista de facturas con invoices().
$invoices = $user->invoices();
Para descargarlas en PDF, instala dompdf/dompdf y usa downloadInvoice(). En $invoiceId pasa el ID de una factura obtenido con invoices().
composer require dompdf/dompdf
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.
php artisan cashier:webhook
Excluye stripe/* de la protección CSRF.
->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.
Última modificación el 13 de julio de 2026