Introducción

Laravel Cashier proporciona una expresiva interfaz fluida para los servicios de pagos en línea por suscripción de Stripe. Maneja casi todo el código de facturación de suscripción que estás teniendo pavor de escribir. Además de la gestión de suscripción, Cashier puede manejar cupones, cambio de suscripciones, «cantidades» de suscripción, cancelación de períodos de gracia e incluso generar PDFs de facturas.

Actualizando Cashier

Al actualizar a una versión nueva de Cashier, es importante que revises cuidadosamente la guía de actualización.

Para prevenir errores por cambios de actualización, Cashier usa una versión API de Stripe fija. Cashier 10.1 utiliza la versión 2019-08-14 de la API para Stripe. La versión API para Stripe será actualizada con los cambios menores en los lanzamientos de software (releases) con el propósito de asegurar la incorporación de las mejoras y nuevas funcionalidades de Stripe.

Instalación

Primero, instala el paquete de Cashier para Stripe Con Composer:

composer require laravel/cashier

Para asegurar que Cashier maneje todos los eventos de Stripe apropiadamente, recuerda configurar el manejo de webhook de Cashier.

Migraciones de bases de datos

El proveedor de servicio de Cashier registra su propio directorio de migración de base de datos, así que recuerda migrar tu base de datos después de instalar el paquete. Las migraciones de Cashier añadirán varias columnas a tu tabla users al igual que crearán una nueva tabla subscriptions para manejar todas las suscripciones de tus clientes:

php artisan migrate

Si necesitas sobrescribir las migraciones que vienen con el paquete de Cashier, puedes publicarlas usando el comando Artisan vendor:publish:

php artisan vendor:publish --tag="cashier-migrations"

Si prefieres prevenir que las migraciones de Cashier se ejecuten completamente, puedes usar el método ignoreMigrations proporcionado por Cashier. Típicamente, este método debería ser ejecutado en el método register de tu AppServiceProvider:

use Laravel\Cashier\Cashier;

Cashier::ignoreMigrations();

Stripe recomienda que cualquier columna usada para almacenar los identificadores de Stripe debería ser sensible a mayúsculas. Como consecuencia, deberías asegurarte que el ordenamiento de columna para la columna stripe_id sea puesto a, por ejemplo, utf8_bin en MySQL. Puedes buscar más información en la documentación de Stripe.

Configuración

Modelo Billable

Antes de usar Cashier, agrega el trait Billable a tu definición de modelo. Este trait proporciona varios métodos para permitirte realizar tareas comunes de facturación, tales como crear suscripciones, aplicar cupones y actualizar la información de las formas de pago:

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

Cashier asume que tu modelo Billable será la clase App\User que viene con Laravel. Si deseas cambiar esto, puedes especificar un modelo distinto en tu archivo .env:

CASHIER_MODEL=App\User

Si estás usando un modelo distinto al modelo App\User proporcionado por Laravel, necesitarás publicar y modificar las migraciones proporcionadas para que coincidan con el nombre de la tabla de tu modelo alternativo.

Claves de API

Seguidamente, deberías configurar tu clave de Stripe en tu archivo .env. Puedes encontrar tus claves de API de Stripe desde el panel de control de Stripe.

STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret

Configuración de moneda

La moneda predeterminada de Cashier es Dólares estadounidenses (USD). Puedes cambiar la moneda predeterminada colocándola en la variable de entorno CASHIER_CURRENCY:

CASHIER_CURRENCY=eur

Además de configurar la moneda de Cashier, también puedes especificar una configuración regional que se utilizará al momento de dar formato a los valores de moneda que se muestran en las facturas. Internamente, Cashier utiliza la clase NumberFormatter de PHP para establecer la configuración regional de la moneda:

CASHIER_CURRENCY_LOCALE=nl_BE

Para usar configuraciones regionales distintas de en, asegúrate que tienes instalada y configurada la extensión de PHP ext-intl en tu servidor.

Logging

Cashier permite que especifiques el canal de log para registrar todas las excepciones relacionadas con Stripe. Puedes especificar el canal de log al usar la variable de entorno CASHIER_LOGGER:

CASHIER_LOGGER=stack

Clientes

Buscar clientes

Puedes buscar los datos de un cliente por su ID de Stripe usando el método Cashier::findBillable. Este devolverá una instancia del modelo Billable:

use Laravel\Cashier\Cashier;

$user = Cashier::findBillable($stripeId);

Crear clientes

Ocasionalmente, puedes desear crear un cliente de Stripe sin empezar una suscripción. Puedes completar esta tarea usando el método createAsStripeCustomer:

$stripeCustomer = $user->createAsStripeCustomer();

Una vez que el cliente ha sido creado en Stripe, puedes empezar una suscripción en una momento posterior. También puedes usar un arreglo opcional $options para pasar parámetros adicionales que sean soportados por la API de Stripe:

$stripeCustomer = $user->createAsStripeCustomer($options);

También puedes usar el método createOrGetStripeCustomer si quieres devolver el objeto del cliente si la entidad billable ya existe como un cliente dentro de Stripe.

$stripeCustomer = $user->createOrGetStripeCustomer();

Actualizar clientes

Ocasionalmente, podrías querer actualizar el cliente de Stripe directamente con información adicional. Puedes completar esto usando el método updateStripeCustomer:

$stripeCustomer = $user->updateStripeCustomer($options);

Personalizar la dirección de correo electrónico

De forma predeterminada, Cashier usará el atributo email de tu modelo Billable para crear los clientes dentro de Stripe. Puedes sobrescribir esto usando el método stripeEmail:

/**
 * Get the email address used to create the customer in Stripe.
 *
 * @return string|null
 */
public function stripeEmail()
{
    return $this->email;
}

También puedes elegir que devuelva null ya que una dirección de correo electrónico no es obligatoria para crear un cliente dentro de Stripe. Si no proporcionas una dirección de correo electrónico, las funcionalidades dentro de Stripe como correos de cobro, recordatorios de pagos fallidos, y otras funcionalidades relacionadas con el correo electrónico no estarán disponibles.

Formas de pago

Almacenar formas de pago

Con el propósito de crear las suscripciones o efectuar cargos de «pago único» con Stripe, necesitarás almacenar una forma de pago y buscar su identificador desde Stripe. El enfoque utilizado para lograrlo difiere basado en si planeas usar la forma de pago para suscripciones o cargos únicos, así que vamos a examinar ambos a continuación.

Formas de pago para suscripciones

Al momento de almacenar las tarjetas de crédito a un cliente para uso futuro, debe usarse la API de Intentos de Configuración de Stripe (Stripe Setup Intents API) para asignar de forma segura los detalles de la forma de pago del cliente. Un «Intento de Configuración» (Setup Intent) indica a Stripe la intención de cargar una forma de pago del cliente. El trait Billable de Cashier incluye el método createSetupIntent para crear fácilmente un nuevo Intento de Configuración (Setup Intent). Deberías ejecutar este método desde la ruta o controlador que renderizará el formulario que asigna los detalles de la forma de pago del cliente:

return view('update-payment-method', [
    'intent' => $user->createSetupIntent()
]);

Después de crear el Intento de Configuración y pasarlo a la vista, deberías adjuntar su secreto al elemento que asignará la forma de pago. Por ejemplo, considera este formulario para «actualizar la forma de pago»:

<input id="card-holder-name" type="text">

<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>

<button id="card-button" data-secret="{{ $intent->client_secret }}">
    Update Payment Method
</button>

Seguidamente, la librería Stripe.js puede ser usada para adjuntar un Elemento de Stripe al formulario y asignar de forma segura los detalles de pago del cliente:

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

Luego, la tarjeta puede ser verificada y puede buscarse un «identificador de forma de pago» seguro desde Stripe usando el método confirmCardSetup de Stripe:

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

cardButton.addEventListener('click', async (e) => {
    const { setupIntent, error } = await stripe.confirmCardSetup(
        clientSecret, {
            payment_method: {
                card: cardElement,
                billing_details: { name: cardHolderName.value }
            }
        }
    );

    if (error) {
        // Display "error.message" to the user...
    } else {
        // The card has been verified successfully...
    }
});

Después que la tarjeta ha sido verificada por Stripe, puedes pasar el identificador setupIntent.payment_method que resulta a tu aplicación de Laravel, donde puede ser adjuntado al cliente. La forma de pago puede o ser agregada como una nueva forma de pago o usada para actualizar la forma de pago predeterminada. También puedes usar inmediatamente el identificador de la forma de pago para crear una nueva suscripción.

Si quieres más información sobre los Intentos de Configuración (Setup Intents) y los detalles de pago de cliente por favor revisa este resumen proporcionado por Stripe.

Formas de pago para cargos únicos

Ciertamente, al momento de hacer un cargo único contra una forma de pago del cliente solamente necesitaremos un identificador de forma de pago una sola vez. Debido a las limitaciones de Stripe, no puedes usar la forma de pago almacenada de forma predeterminada a un cliente para cargos únicos. Debes permitirle al cliente que introduzca los detalles de su forma de pago usando la librería Stripe.js . Por ejemplo, considera el siguiente formulario:

<input id="card-holder-name" type="text">

<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>

<button id="card-button">
    Process Payment
</button>

Luego, la librería Stripe.js puede usarse para adjuntar un Elemento de Stripe al formulario y asignar de forma segura los detalles de pago del cliente:

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

Seguidamente, la tarjeta puede ser verificada y un «identificador de forma de pago» seguro puede ser buscado desde Stripe usando el método createPaymentMethod de Stripe:

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');

cardButton.addEventListener('click', async (e) => {
    const { paymentMethod, error } = await stripe.createPaymentMethod(
        'card', cardElement, {
            billing_details: { name: cardHolderName.value }
        }
    );

    if (error) {
        // Display "error.message" to the user...
    } else {
        // The card has been verified successfully...
    }
});

Si la tarjeta es verificada exitosamente, puedes pasar el paymentMethod.id a tu aplicación de Laravel y procesar un cargo único.

Buscar formas de pago

El método paymentMethods en la instancia de modelo Billable devuelve una colección de instancias Laravel\Cashier\PaymentMethod:

$paymentMethods = $user->paymentMethods();

Para buscar la forma de pago predeterminada, puede usarse el método defaultPaymentMethod:

$paymentMethod = $user->defaultPaymentMethod();

También puedes buscar una forma de pago específica que se posea en el modelo Billable usando el método findPaymentMethod:

$paymentMethod = $user->findPaymentMethod($paymentMethodId);

Determinar si un usuario tiene una forma de pago

Para determinar si un modelo Billable tiene una forma de pago adjuntada a su cuenta, usa el método hasPaymentMethod:

if ($user->hasPaymentMethod()) {
    //
}

Actualizar la forma de pago predeterminada

El método updateDefaultPaymentMethod puede usarse para actualizar la información de la forma de pago predeterminada del cliente. Este método acepta un identificador de forma de pago de Stripe y asignará la nueva forma de pago como forma de pago predeterminada de facturación.

$user->updateDefaultPaymentMethod($paymentMethod);

Para sincronizar tu información de forma de pago predeterminada con la información de forma de pago predeterminada del cliente en Stripe, puedes usar el método updateDefaultPaymentMethodFromStripe:

$user->updateDefaultPaymentMethodFromStripe();

La forma de pago predeterminada de un cliente solamente puede ser usada para facturación y creación de suscripciones nuevas. Dado las limitaciones de Stripe, no pueden ser usadas para cargos únicos.

Agregar formas de pago

Para agregar una nueva forma de pago, puedes ejecutar el método addPaymentMethod en el usuario billable, pasando el identificador de forma de pago:

$user->addPaymentMethod($paymentMethod);

Para aprender cómo buscar un identificador de forma de pago, por favor revisa la documentación sobre almacenamiento de forma de pago.

Eliminando formas de pago

Para eliminar una forma de pago, puedes ejecutar el método delete en la instancia de Laravel\Cashier\PaymentMethod que deseas eliminar:

$paymentMethod->delete();

El método deletePaymentMethods eliminará toda la información de forma de pago para el modelo Billable:

$user->deletePaymentMethods();

Si un usuario tiene una suscripción activa, deberías prevenirlo de eliminar su forma de pago predeterminada.

Suscripciones

Creando suscripciones

Para crear una suscripción, primero obtén una instancia de tu modelo billable o de facturación, el cual será típicamente una instancia de App\User. Una vez que has obtenido la instancia de modelo, puedes usar el método newSubscription para crear la suscripción del modelo:

$user = User::find(1);

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

El primer argumento pasado al método newSubscription debería ser el nombre de la suscripción. Si tu aplicación sólo ofrece una única suscripción, puedes llamarla default o primary. El segundo argumento es el plan específico al que el usuario se está suscribiendo. Este valor debería corresponder al identificador del plan en Stripe.

El método create, el cual acepta un identificador de forma de pago de Stripe o un objeto PaymentMethod de Stripe, comenzará la suscripción al igual que actualizará tu base de datos con el ID del cliente y otra información de facturación relevante.

Pasar un identificador de forma de pago directamente al método para crear una suscripción create(), también lo agregará automáticamente a las formas de pago almacenadas del usuario.

Detalles de usuario adicionales

Si prefieres especificar detalles de cliente adicionales, puedes hacerlo pasándolos como segundo argumento del método create:

$user->newSubscription('default', 'monthly')->create($paymentMethod, [
    'email' => $email,
]);

Para aprender más sobre los campos adicionales soportados por Stripe, revisa la documentación sobre la creación de clientes.

Cupones

Si prefieres aplicar un cupón al momento de crear la suscripción, puedes usar el método withCoupon:

$user->newSubscription('default', 'monthly')
     ->withCoupon('code')
     ->create($paymentMethod);

Verificando el estado de la suscripción

Una vez que un usuario está suscrito a tu aplicación, puedes verificar fácilmente su estado de suscripción usando una variedad conveniente de métodos. Primero, el método subscribed devuelve true si el usuario tiene una suscripción activa, incluso si la suscripción está actualmente en su período de prueba:

if ($user->subscribed('default')) {
    //
}

El método subscribed también constituye un gran candidato para un middleware de ruta, permitiéndote filtrar el acceso a rutas y controladores basados en el estado de suscripción del usuario:

public function handle($request, Closure $next)
{
    if ($request->user() && ! $request->user()->subscribed('default')) {
        // This user is not a paying customer...
        return redirect('billing');
    }

    return $next($request);
}

Si prefieres determinar si un usuario está aún dentro de su período de prueba, puedes usar el método onTrial. Este método puede ser útil para mostrar una advertencia al usuario que todavía está en su período de prueba:

if ($user->subscription('default')->onTrial()) {
    //
}

El método subscribedToPlan puede ser usado para determinar si el usuario está suscrito a un plan dado basado en un ID de plan Stripe proporcionado. En este ejemplo, determinaremos si la suscripción default del usuario está activa para el plan monthly:

if ($user->subscribedToPlan('monthly', 'default')) {
    //
}

Al pasar un arreglo al método subscribedToPlan, puedes determinar si la suscripción default del usuario está activa para el plan monthly o yearly

if ($user->subscribedToPlan(['monthly', 'yearly'], 'default')) {
    //
}

El método recurring puede ser usado para determinar si el usuario está actualmente suscrito y ya no está dentro de su periodo de prueba:

if ($user->subscription('default')->recurring()) {
    //
}

Estado de suscripción cancelada

Para determinar si el usuario fue alguna vez un suscriptor activo, pero que ha cancelado su suscripción, puedes usar el método cancelled:

if ($user->subscription('default')->cancelled()) {
    //
}

También puedes determinar si un usuario ha cancelado su suscripción, pero todavía está en su «período de gracia» hasta que la suscripción caduque totalmente. Por ejemplo, si un usuario cancela una suscripción el 5 de Marzo que fue planificada para expirar originalmente el 10 de Marzo, el usuario está en su «período de gracia» hasta el 10 de Marzo. Nota que el método subscribed aún devuelve true durante este tiempo:

if ($user->subscription('default')->onGracePeriod()) {
    //
}

Para determinar si el usuario que ha cancelado su suscripción ya no está dentro del «periodo de gracia», puedes usar el método ended:

if ($user->subscription('default')->ended()) {
    //
}

Estado de deuda vencida o incompleta

Si una suscripción requiere una acción de pago secundaria después de la creación de la suscripción será marcada como incompleta incomplete. Los estados de suscripción son almacenados en la columna stripe_status en la tabla de bases de datos subscriptions de Cashier.

De forma similar, si una acción de pago secundaria es requerida al momento de intercambiar los planes, la suscripción será marcada como past_due o deuda vencida. Cuando tu suscripción esté en alguno de estos estados, no estará activa hasta que el cliente haya confirmado su pago. La comprobación de si una suscripción tiene un pago incompleto se puede hacer usando el método hasIncompletePayment en el modelo Billable o una instancia de suscripción:

if ($user->hasIncompletePayment('default')) {
    //
}

if ($user->subscription('default')->hasIncompletePayment()) {
    //
}

Cuando una suscripción tenga un pago incompleto, deberías dirigir al usuario a la página de confirmación de pago de Cashier, pasando el identificador de latestPayment. Puedes usar el método latestPayment disponible en una instancia de suscripción para buscar su identificador:

<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">
    Please confirm your payment.
</a>

Si prefieres que la suscripción aún permanezca activa cuando esté en un estado past_due, puedes usar el método keepPastDueSubscriptionsActive proporcionado por Cashier. Típicamente, este método debería ejecutarse en el método register de tu AppServiceProvider:

use Laravel\Cashier\Cashier;

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    Cashier::keepPastDueSubscriptionsActive();
}

Cuando una suscripción esté en un estado incomplete no puede ser cambiada hasta que el pago sea confirmado. Por lo tanto, los métodos swap y updateQuantity arrojarán una excepción cuando la suscripción esté en un estado incomplete.

Cambiando planes

Después que un usuario esté suscrito en tu aplicación, ocasionalmente puede querer cambiar a un nuevo plan de suscripción. Para cambiar un usuario a una nueva suscripción, pasa el identificador de plan al método swap:

$user = App\User::find(1);

$user->subscription('default')->swap('provider-plan-id');

Si el usuario está en período de prueba, se mantendrá el período de prueba. También, si una «cantidad» existe para la suscripción, esa cantidad también será conservada.

Si prefieres cambiar planes y cancelar cualquier período de prueba en donde esté el usuario actualmente, puedes usar el método skipTrial:

$user->subscription('default')
        ->skipTrial()
        ->swap('provider-plan-id');

Si prefieres cambiar de plan y facturar inmediatamente al usuario en lugar de esperar por su próximo período de facturación, puedes usar el método swapAndInvoice:

$user = App\User::find(1);

$user->subscription('default')->swapAndInvoice('provider-plan-id');

Prorrateo

De forma predeterminada, Stripe prorratea los cargos (calcula parcialmente los cargos) al momento de un intercambio de planes. El método noProrate puede ser usado para actualizar la suscripción sin prorratear los cargos:

$user->subscription('default')->noProrate()->swap('provider-plan-id');

Para más información sobre el prorrateo de suscripción, consulta la documentación de Stripe.

Cantidad de la suscripción

Algunas veces las suscripciones son afectadas por la «cantidad». Por ejemplo, tu aplicación podría cargar $10 por mes por usuario en una cuenta. Para incrementar o disminuir fácilmente tu cantidad en la suscripción, usa los métodos incrementQuantity y decrementQuantity:

$user = User::find(1);

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

// Add five to the subscription's current quantity...
$user->subscription('default')->incrementQuantity(5);

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

// Subtract five to the subscription's current quantity...
$user->subscription('default')->decrementQuantity(5);

Alternativamente, puedes establecer una cantidad específica usando el método updateQuantity:

$user->subscription('default')->updateQuantity(10);

El método noProrate puede ser usado para actualizar la cantidad de la suscripción sin prorratear los cargos:

$user->subscription('default')->noProrate()->updateQuantity(10);

Para más información sobre cantidades de suscripción, consulta la documentación de Stripe.

Impuestos de suscripción

Para especificar el porcentaje de impuesto que un usuario paga en una suscripción, implementa el método taxPercentage en tu modelo billable (facturable) y devuelve un valor numérico entre 0 y 100, sin más de 2 posiciones decimales.

public function taxPercentage()
{
    return 20;
}

El método taxPercentage te permite aplicar una tasa de impuesto modelo por modelo, lo que puede ser útil para una base de usuarios que abarca varios países y tasas de impuestos.

El método taxPercentage solamente aplica para cargos por suscripción. Si usas Cashier para hacer cargos de «pago único», necesitarás especificar manualmente la tasa de impuesto en ese momento.

Sincronizando los porcentajes del impuesto

Al cambiar el valor retornado por el método taxPercentage, las configuraciones de impuesto en cualquier suscripción existente del usuario permanecerán igual. Si deseas actualizar el valor del impuesto para un suscripción existente con el valor taxPercentage retornado, debes llamar al método syncTaxPercentage en la instancia de suscripción del usuario:

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

Fecha de inicio de la suscripción

Por defecto, el inicio del período de facturación es la fecha en que se creó la suscripción o, si se usa un período de prueba, la fecha en que finaliza la prueba. Si deseas modificar la fecha de inicio de facturación, puedes usar el método anchorBillingCycleOn:

use App\User;
use Carbon\Carbon;

$user = User::find(1);

$anchor = Carbon::parse('first day of next month');

$user->newSubscription('default', 'premium')
            ->anchorBillingCycleOn($anchor->startOfDay())
            ->create($paymentMethod);

Para más información sobre administrar períodos de facturación, consulta la documentación del período de facturación de Stripe.

Cancelando suscripciones

Para cancelar una suscripción, ejecuta el método cancel en la suscripción del usuario:

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

Cuando una suscripción es cancelada, Cashier establecerá automáticamente la columna ends_at en tu base de datos. Esta columna es usada para conocer cuando el método subscribed debería empezar a devolver el valor de false. Por ejemplo, si un cliente cancela una suscripción el 1 de Marzo, pero la suscripción no estaba planificada para finalizar sino para el 5 de Marzo, el método subscribed continuará devolviendo true hasta el 5 de Marzo.

Puedes determinar si un usuario ha cancelado su suscripción pero aún está en su «período de gracia» usando el método onGracePeriod:

if ($user->subscription('default')->onGracePeriod()) {
    //
}

Si deseas cancelar una suscripción inmediatamente, ejecuta el método cancelNow en la suscripción del usuario:

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

Reanudando suscripciones

Si un usuario ha cancelado su suscripción y deseas reanudarla, usa el método resume. El usuario debe estar aún en su período de gracia para reanudar una suscripción:

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

Si el usuario cancela una suscripción y después reanuda esa suscripción antes que la suscripción haya expirado completamente, no será facturada inmediatamente. En lugar de eso, su suscripción será reactivada y será facturada en el período de facturación original.

Períodos de prueba de suscripción

Cashier maneja las fechas de períodos de prueba por suscripciones y no las deriva desde los planes de Stripe. Por lo tanto, deberías configurar tu plan en Stripe para que tenga un período de prueba de cero días de modo que Cashier pueda manejar los períodos de prueba en su lugar.

Con información anticipada de la forma de pago

Si prefieres ofrecer períodos de prueba a tus clientes mientras continuas coleccionando información anticipada de la forma de pago, deberías usar el método trialDays al momento de crear tus suscripciones:

$user = User::find(1);

$user->newSubscription('default', 'monthly')
            ->trialDays(10)
            ->create($paymentMethod);

Este método establecerá la fecha de finalización del período de prueba en el registro de suscripción dentro de la base de datos, al igual que le indicará a Stripe a no empezar a facturar al cliente hasta después de esta fecha. Al usar el método trialDays, Cashier sobrescribirá cualquier período de prueba por defecto configurado para el plan en Stripe.

Si la suscripción del cliente no es cancelada antes de la fecha de finalización del período de prueba, será cargada tan pronto como expire el período de prueba, así que deberías asegurarte de notificar a tus usuarios de la fecha de finalización de su período de prueba.

El método trialUntil te permite proporcionar una instancia DateTime para especificar cuando el periodo de prueba debería terminar:

use Carbon\Carbon;

$user->newSubscription('default', 'monthly')
            ->trialUntil(Carbon::now()->addDays(10))
            ->create($paymentMethod);

Puedes determinar si el usuario está dentro de su período de prueba utilizando el método onTrial de la instancia del usuario o el método onTrial de la instancia de suscripción. Los dos ejemplos que siguen son idénticos:

if ($user->onTrial('default')) {
    //
}

if ($user->subscription('default')->onTrial()) {
    //
}

Sin información anticipada de la forma de pago

Si prefieres ofrecer períodos de prueba sin coleccionar la información anticipada del método de pago del usuario, puedes establecer la columna trial_ends_at en el registro del usuario con la fecha de finalización del período de prueba deseado. Esto es hecho típicamente durante el proceso de registro del usuario:

$user = User::create([
    // Populate other user properties...
    'trial_ends_at' => now()->addDays(10),
]);

Asegúrate de agregar un mutador de fecha para trial_ends_at en tu definición de modelo.

Cashier se refiere a este tipo de período de prueba como un «período de prueba genérico», debido a que no está conectado a ninguna suscripción existente. El método onTrial en la instancia User devolverá true si la fecha actual no es mayor al valor de trial_ends_at:

if ($user->onTrial()) {
    // User is within their trial period...
}

También puedes usar el método onGenericTrial si deseas conocer específicamente que el usuario está dentro de su período de prueba «genérico» y no ha creado una suscripción real todavía:

if ($user->onGenericTrial()) {
    // User is within their "generic" trial period...
}

Una vez que estés listo para crear una suscripción real para el usuario, puedes usar el método newSubscription como es usual:

$user = User::find(1);

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

Extender el período de prueba

El método extendTrial permite que extiendas el período de prueba de una suscripción después que haya sido creada:

// End the trial 7 days from now...
$subscription->extendTrial(
    now()->addDays(7)
);

// Add an additional 5 days to the trial...
$subscription->extendTrial(
    $subscription->trial_ends_at->addDays(5)
);

Si el período de prueba ya ha expirado y el cliente ya ha recibido el cobro por la suscripción, todavía puedes ofrecerle un período de prueba extendido. El tiempo que gastó dentro del período de prueba será descontado de la próxima factura del cliente.

Manejando webhooks de Stripe

Puedes usar la interfaz de línea de comandos o CLI de Stripe para ayudar a realizar pruebas con webhooks durante el desarrollo local.

Stripe puede notificar a tu aplicación de una variedad de eventos por medio de webhooks. De forma predeterminada, una ruta que apunta al controlador de webhook de Cashier es configurada a través del proveedor de servicio de Cashier. Este controlador manejará todas las solicitudes de webhook entrantes.

De forma predeterminada, este controlador manejará automáticamente la cancelación de suscripciones que tengan demasiados cargos fallados (como sea definido por tu configuración de Stripe), las actualizaciones de clientes, las eliminaciones de clientes, las actualizaciones de suscripción y los cambios de forma de pago; sin embargo, como vamos a descubrir pronto, puedes extender este controlador para que maneje cualquier evento de webhook que desees.

Para asegurar que tu aplicación pueda manejar los webhooks de Stripe, asegúrate de configurar la URL de webhook en el panel de control de Stripe. La lista completa de todos los webhooks que deberías configurar en el panel de control de Stripe son:

  • customer.subscription.updated
  • customer.subscription.deleted
  • customer.updated
  • customer.deleted
  • invoice.payment_action_required

Asegúrate de proteger las solicitudes entrantes con el middleware webhook de verificación de firmas incluido en Cashier.

Webhooks & Protección CSRF

Ya que los webhooks de Stripe necesitan pasar por alto la protección CSRF de Laravel, asegúrate de listar la URI como una excepción en tu middleware VerifyCsrfToken o lista la ruta fuera del grupo de middleware web:

protected $except = [
    'stripe/*',
];

Definiendo manejadores de eventos de webhooks

Cashier maneja automáticamente la cancelación de suscripción por cargos fallidos, pero si tienes eventos de webhook adicionales que te gustaría manejar, extiende el controlador de Webhook. Tus nombres de métodos deberían corresponder con la convención esperada por Cashier, específicamente, los métodos deberían tener como prefijo handle y el nombre «camel case» del webhook que deseas manejar. Por ejemplo, si deseas manejar el webhook invoice.payment_succeeded, deberías agregar un método handleInvoicePaymentSucceeded al controlador:

<?php

namespace App\Http\Controllers;

use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
    * Handle invoice payment succeeded.
    *
    * @param  array  $payload
    * @return \Symfony\Component\HttpFoundation\Response
    */
    public function handleInvoicePaymentSucceeded($payload)
    {
        // Handle The Event
    }
}

Luego, define una ruta a tu controlador de Cashier dentro de tu archivo routes/web.php. Esto sobrescribirá la ruta predeterminada de entrega:

Route::post(
    'stripe/webhook',
    '\App\Http\Controllers\WebhookController@handleWebhook'
);

Cashier emite un evento Laravel\Cashier\Events\WebhookReceivedcuando se recibe un webhook, y un evento Laravel\Cashier\Events\WebhookHandled cuando un webhook fue manejado por Cashier. Ambos eventos contienen la porción de los datos completa (payload) del webhook de Stripe.

Suscripciones fallidas

¿Qué sucedería si una tarjeta de crédito expira? No importa – el controlador Webhook de Cashier cancelará la suscripción del cliente por ti. Los pagos fallidos serán capturados y manejados por el controlador automáticamente. El controlador cancelará la suscripción del cliente cuando Stripe determina que la suscripción ha fallado (normalmente después de tres intentos de pagos fallidos).

Verificando las firmas de los webhooks

Para asegurar tus webhooks, puedes usar las firmas de webhook de Stripe. Por conveniencia, Cashier automáticamente incluye un middleware que verifica si la petición del webhook de Stripe entrante es válida.

Para habilitar la verificación de webhook, asegúrate de que la variable de entorno STRIPE_WEBHOOK_SECRET está declarada en tu archivo .env. El valor secret de webhook puede ser buscado desde el panel de control de tu cuenta de Stripe.

Cargos únicos

Cargo simple

El método charge acepta la cantidad que prefieras cargar en el denominador más bajo de la moneda usada por tu aplicación.

Si desea realizar un «cargo único» a la forma de pago de un cliente suscrito, puedes usar el método charge en una instancia de modelo billable (facturable). Deberás proporcionar un identificador de forma de pago como segundo argumento:

// Stripe Accepts Charges In Cents...
$stripeCharge = $user->charge(100, $paymentMethod);

El método charge acepta un arreglo como tercer argumento, permitiendo que pases algunas opciones que desees para la creación de cargo de Stripe subyacente. Consulta la documentación de Stripe sobre las opciones disponibles al crear cargos:

$user->charge(100, $paymentMethod, [
    'custom_option' => $value,
]);

El método charge arrojará una excepción si el cargo falla. Si el cargo es exitoso, una instancia de Laravel\Cashier\Payment será devuelta por el método:

try {
    $payment = $user->charge(100, $paymentMethod);
} catch (Exception $e) {
    //
}

Cargo con factura

Algunas veces puedes necesitar hacer un cargo único pero también generar una factura por el cargo de modo que puedas ofrecer un recibo PDF a tu cliente. El método invoiceFor permite que hagas justamente eso. Por ejemplo, vamos a facturar al cliente $5.00 por una «cuota única»:

// Stripe Accepts Charges In Cents...
$user->invoiceFor('One Time Fee', 500);

La factura será cargada inmediatamente contra la forma de pago predeterminada del usuario. El método invoiceFor también acepta un arreglo como su tercer argumento. Este arreglo contiene las opciones de facturación para el elemento de la factura. El cuarto argumento aceptado por el método es también un arreglo. Este argumento final acepta las opciones de facturación de la factura en sí:

$user->invoiceFor('Stickers', 500, [
    'quantity' => 50,
], [
    'tax_percent' => 21,
]);

El método invoiceFor creará una factura de Stripe la cual reintentará intentos de facturación fallidos. Si no quieres que las facturas reintenten cargos fallidos, necesitarás cerrarlas usando la API de Stripe después del primer cargo fallido.

Reembolsando cargos

Si necesitas reembolsar un cargo de Stripe, puedes usar el método refund. Este método acepta el ID del Intento de Pago de Stripe como su primer argumento:

$payment = $user->charge(100, $paymentMethod);

$user->refund($payment->id);

Facturas

Puedes obtener fácilmente un arreglo de facturas de modelo billable usando el método invoices:

$invoices = $user->invoices();

// Include pending invoices in the results...
$invoices = $user->invoicesIncludingPending();

Al momento de listar las facturas para el cliente, puedes usar los métodos helper de factura para mostrar la información de factura relevante. Por ejemplo, puedes querer listar todas las facturas en una tabla, permitiendo que el usuario descargue fácilmente algunas de ellas:

<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td>{{ $invoice->date()->toFormattedDateString() }}</td>
            <td>{{ $invoice->total() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
        </tr>
    @endforeach
</table>

Generando PDFs de facturas

Dentro de una ruta o controlador, usa el método downloadInvoice para generar una descarga en PDF de la factura. Este método generará automáticamente la respuesta HTTP apropiada para enviar la descarga al navegador:

use Illuminate\Http\Request;

Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
    return $request->user()->downloadInvoice($invoiceId, [
        'vendor' => 'Your Company',
        'product' => 'Your Product',
    ]);
});

Autenticación reforzada de cliente

Si tu empresa se encuentra en Europa necesitarás cumplir con las regulaciones de autenticación reforzada de clientes (SCA). Estas regulaciones fueron impuestas en Septiembre de 2019 por la Unión Europea para prevenir el fraude. Afortunadamente, Stripe y Cashier están preparados para construir aplicaciones que cumplen con SCA.

Antes de empezar, revisa la guía de Stripe sobre PSD2 y SCA así como su documentación sobre la nueva guía de API SCA.

Pagos que requieren confirmación adicional

Las regulaciones SCA frecuentemente requieren verificación extra con el propósito de confirmar y procesar un pago. Cuando esto pasa, Cashier arrojará una excepción IncompletePayment que te informa que esta verificación extra es necesaria. Después de capturar esta excepción, tienes dos opciones de cómo proceder.

Primero, podrías redirigir tu cliente a la página dedicada para confirmación de pago que viene incluida con Cashier. Esta página ya tiene una ruta asociada que es registrada por medio del proveedor de servicio de Cashier. Así, puedes capturar la excepción IncompletePayment y redirigir a la página de confirmación de pago:

use Laravel\Cashier\Exceptions\IncompletePayment;

try {
    $subscription = $user->newSubscription('default', $planId)
                            ->create($paymentMethod);
} catch (IncompletePayment $exception) {
    return redirect()->route(
        'cashier.payment',
        [$exception->payment->id, 'redirect' => route('home')]
    );
}

En la página de confirmación de pago, el cliente será informado para que introduzca su información de tarjeta de crédito nuevamente y ejecute algunas acciones adicionales requeridas por Stripe, tales como la confirmación «3D Secure» (Seguridad 3D). Después de confirmar su pago, el usuario será redirigido a la URL proporcionada en el parámetro redirect especificado anteriormente.

Alternativamente, podrías permitir que Stripe maneje la confirmación de pago por ti. En este caso, en lugar de redirigir a la página de confirmación de pago, puedes configurar el envío de correos de facturación automático de Stripe en tu panel de control de Stripe. Sin embargo, si una excepción IncompletePayment es capturada, aún deberías informar al usuario que ellos recibirán un correo con instrucciones de confirmación de pago adicional.

Las excepciones de pago incompletas pueden ser arrojadas por los métodos siguientes: charge, invoiceFor e invoice en el usuario Billable. Al momento de manejar las suscripciones, el método create del SubscriptionBuilder, y los métodos incrementAndInvoice y swapAndInvoice en el modelo Subscription pueden arrojar excepciones.

Estado de deuda incompleta y vencida

Cuando un pago necesita confirmación adicional, la suscripción permanecerá en un estado incomplete o past_due como lo indica su columna de base de datos stripe_status. Cashier activará automáticamente la suscripción del cliente por medio de un webhook tan pronto como la confirmación de pago se complete.

Para más información sobre los estados incomplete e past_due, por favor revisa nuestra documentación adicional.

Notificaciones de pago de fuera-de-sesión

Debido a que las regulaciones SCA requieren que los clientes verifiquen ocasionalmente sus detalles de pago aun cuando su suscripción esté activa, Cashier puede enviar una notificación de pago al cliente al momento que sea requerida la confirmación de pago fuera-de-sesión. Por ejemplo, esto puede ocurrir cuando una suscripción se está renovando. La notificación de pago de Cashier puede ser activada al establecer la variable de entorno CASHIER_PAYMENT_NOTIFICATION a una clase de notificación. De forma predeterminada, esta notificación es desactivada. Ciertamente, Cashier incluye una clase de notificación que puedes usar para este propósito, pero eres libre de proporcionar tu propia clase de notificación si lo deseas:

CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment

Para asegurar que las notificaciones de confirmación de pago fuera-de-sesión sean entregadas, verifica que los webhooks de Stripe estén configurados para tu aplicación y el webhook invoice.payment_action_required esté habilitado en tu panel de control de Stripe. Además, tu modelo Billable también debería usar el trait Illuminate\Notifications\Notifiable de Laravel.

Las notificaciones serán enviadas aún cuando los clientes estén haciendo manualmente un pago que requiera confirmación adicional. Desafortunadamente, no hay manera que Stripe conozca si el pago fue hecho manualmente o «fuera-de-sesión». Por otra parte, un cliente simplemente verá un mensaje de «Pago Exitoso» si visita la página de pago después que haya confirmación de su pago. El cliente no tiene permitido que accidentalmente confirme el mismo pago dos veces e incurra en un segundo cargo accidental.

SDK de Stripe

Muchos de los objetos de Cashier son envoltorios de objetos SDK de Stripe. Si deseas interactuar con los objetos de Stripe directamente, puedes invocarlos convenientemente usando el método asStripe:

$stripeSubscription = $subscription->asStripeSubscription();

$stripeSubscription->update($subscription->stripe_id, ['application_fee_percent' => 5]);

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Mocking - Documentación de Laravel 6 Lección siguiente Laravel Envoy - Documentación de Laravel 6