- Introducción
- Actualizando cashier
- Instalación
- Configuración
- Clientes
- Formas de pago
- Suscripciones
- Períodos de prueba de suscripción
- Manejando webhooks de Stripe
- Cargos únicos
- Facturas
- Autenticación reforzada de cliente (SCA)
- SDK de Stripe
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\WebhookReceived
cuando 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