1. Paquetes
  2. Laravel Cashier (Stripe)

Introducción

Laravel Cashier Stripe proporciona una interfaz expresiva y fluida para los servicios de facturación de suscripciones de Stripe. Maneja casi todo el código de facturación de suscripciones que estás temiendo escribir. Además de la gestión básica de suscripciones, Cashier puede manejar cupones, cambiar de suscripción, "cantidades" de suscripción, períodos de gracia para la cancelación e incluso generar facturas en PDF.

Actualización de Cashier

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

Advertencia Para evitar cambios que puedan causar problemas, Cashier utiliza una versión fija de la API de Stripe. Cashier 14 utiliza la versión 2022-11-15 de la API de Stripe. La versión de la API de Stripe se actualizará en lanzamientos menores para aprovechar las nuevas funciones y mejoras de Stripe.

Instalación

En primer lugar, instala el paquete Cashier para Stripe utilizando el gestor de paquetes Composer:

composer require laravel/cashier

Advertencia Para asegurar que Cashier maneje adecuadamente todos los eventos de Stripe, recuerda configurar el manejo de webhooks de Cashier.

Migraciones de Base de Datos

El proveedor de servicios de Cashier registra su propio directorio de migración de base de datos, así que recuerde migrar su base de datos después de instalar el paquete. Las migraciones de Cashier agregarán varias columnas a su tabla de users. También creará una nueva tabla de subscriptions para contener todas las suscripciones de su cliente y una tabla de subscription_items para suscripciones con precios múltiples:

php artisan migrate

Si necesita sobrescribir las migraciones que se incluyen con Cashier, puede publicarlas utilizando el comando Artisan vendor:publish:

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

Si desea evitar que se ejecuten las migraciones de Cashier por completo, puede utilizar el método ignoreMigrations proporcionado por Cashier. Por lo general, este método debería llamarse en el método register de su AppServiceProvider:

use Laravel\Cashier\Cashier;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
Cashier::ignoreMigrations();
}

Advertencia Stripe recomienda que cualquier columna utilizada para almacenar identificadores de Stripe sea sensible a mayúsculas y minúsculas. Por lo tanto, debes asegurarte de que la colaboración de la columna stripe_id esté configurada en utf8_bin al usar MySQL. Puedes encontrar más información al respecto en la documentación de Stripe.

Configuración

Modelo Facturable

Antes de usar Cashier, añada el rasgo Billable a la definición de su modelo facturable. Por lo general, esto será el modelo App\Models\User. Este rasgo proporciona varios métodos que le permiten realizar tareas comunes de facturación, como crear suscripciones, aplicar cupones y actualizar la información del método de pago:

use Laravel\Cashier\Billable;
 
class User extends Authenticatable
{
use Billable;
}

Cashier asume que su modelo facturable será la clase App\Models\User que se incluye con Laravel. Si desea cambiar esto, puede especificar un modelo diferente a través del método useCustomerModel. Este método debe llamarse típicamente en el método boot de su clase AppServiceProvider:

use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;
 
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Cashier::useCustomerModel(User::class);
}

Advertencia Si estás utilizando un modelo que no sea el modelo App\Models\User suministrado por Laravel, deberás publicar y modificar las migraciones de Cashier proporcionadas para que coincidan con el nombre de la tabla de tu modelo alternativo.

Claves API

A continuación, debe configurar sus claves de API de Stripe en el archivo .env de su aplicación. Puede recuperar sus claves de API de Stripe desde el panel de control de Stripe:

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

Advertencia Asegúrate de que la variable de entorno STRIPE_WEBHOOK_SECRET esté definida en el archivo .env de tu aplicación, ya que esta variable se utiliza para garantizar que los webhooks entrantes sean realmente de Stripe.

Configuración de Moneda

La moneda predeterminada de Cashier es el dólar estadounidense (USD). Puede cambiar la moneda predeterminada configurando la variable de entorno CASHIER_CURRENCY en el archivo .env de su aplicación:

CASHIER_CURRENCY=eur

Además de configurar la moneda de Cashier, también puede especificar una configuración regional que se utilizará al formatear los valores monetarios para su visualización 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

Advertencia Para usar localidades distintas a en, asegúrate de que la extensión PHP ext-intl esté instalada y configurada en tu servidor.

Configuración de Impuestos

Gracias a Stripe Tax, es posible calcular automáticamente los impuestos para todas las facturas generadas por Stripe. Puedes habilitar el cálculo automático de impuestos invocando el método calculateTaxes en el método boot de la clase App\Providers\AppServiceProvider de tu aplicación:

use Laravel\Cashier\Cashier;
 
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Cashier::calculateTaxes();
}

Una vez que se haya habilitado el cálculo de impuestos, todas las nuevas suscripciones y las facturas únicas generadas recibirán el cálculo automático de impuestos.

Para que esta función funcione correctamente, los detalles de facturación de tu cliente, como el nombre, la dirección y el ID fiscal del cliente, deben sincronizarse con Stripe. Puedes utilizar los métodos de sincronización de datos del cliente y ID fiscal ofrecidos por Cashier para lograr esto.

Advertencia No se calcula ningún impuesto para cargos únicos o pagos únicos con checkout.

Registro

Cashier te permite especificar el canal de registro que se utilizará al registrar errores fatales de Stripe. Puedes especificar el canal de registro definiendo la variable de entorno CASHIER_LOGGER dentro del archivo .env de tu aplicación:

CASHIER_LOGGER=stack

Las excepciones generadas por llamadas al API de Stripe se registrarán a través del canal de registro predeterminado de tu aplicación.

Uso de Modelos Personalizados

Eres libre de extender los modelos utilizados internamente por Cashier definiendo tu propio modelo y extendiendo el modelo correspondiente de Cashier:

use Laravel\Cashier\Subscription as CashierSubscription;
 
class Subscription extends CashierSubscription
{
// ...
}

Después de definir tu modelo, puedes indicarle a Cashier que utilice tu modelo personalizado a través de la clase Laravel\Cashier\Cashier. Por lo general, debes informar a Cashier sobre tus modelos personalizados en el método boot del proveedor de servicios de tu aplicación, en la clase App\Providers\AppServiceProvider:

use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;
 
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}

Clientes

Recuperación de Clientes

Puedes recuperar a un cliente por su ID de Stripe utilizando el método Cashier::findBillable. Este método devolverá una instancia del modelo facturable:

use Laravel\Cashier\Cashier;
 
$user = Cashier::findBillable($stripeId);

Creación de Clientes

Ocasionalmente, es posible que desees crear un cliente de Stripe sin comenzar una suscripción. Puedes lograr esto utilizando el método createAsStripeCustomer:

$stripeCustomer = $user->createAsStripeCustomer();

Una vez que el cliente ha sido creado en Stripe, puedes comenzar una suscripción en una fecha posterior. Puedes proporcionar un array opcional $options para pasar cualquier parámetro de creación de cliente compatible con la API de Stripe adicional:

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

Puedes usar el método asStripeCustomer si deseas devolver el objeto de cliente de Stripe para un modelo facturable:

$stripeCustomer = $user->asStripeCustomer();

El método createOrGetStripeCustomer se puede usar si deseas recuperar el objeto de cliente de Stripe para un modelo facturable dado, pero no estás seguro de si el modelo facturable ya es un cliente en Stripe. Este método creará un nuevo cliente en Stripe si aún no existe:

$stripeCustomer = $user->createOrGetStripeCustomer();

Actualización de Clientes

Ocasionalmente, es posible que desees actualizar directamente el cliente de Stripe con información adicional. Puedes lograr esto utilizando el método updateStripeCustomer. Este método acepta un array de opciones de actualización de cliente admitidas por la API de Stripe:

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

Saldos

Stripe te permite acreditar o debitar el "saldo" de un cliente. Más tarde, este saldo se acreditará o debitará en nuevas facturas. Para verificar el saldo total del cliente, puedes usar el método balance que está disponible en tu modelo facturable. El método balance devolverá una representación de cadena formateada del saldo en la moneda del cliente:

$balance = $user->balance();

Para acreditar el saldo de un cliente, puedes proporcionar un valor al método creditBalance. Si lo deseas, también puedes proporcionar una descripción:

$user->creditBalance(500, 'Premium customer top-up.');

Proporcionar un valor al método debitBalance debitará el saldo del cliente:

$user->debitBalance(300, 'Bad usage penalty.');

El método applyBalance creará nuevas transacciones de saldo para el cliente. Puedes recuperar estos registros de transacciones utilizando el método balanceTransactions, lo que puede ser útil para proporcionar un registro de créditos y débitos para que el cliente lo revise:

// Obtener todas las transacciones...
$transactions = $user->balanceTransactions();
 
foreach ($transactions as $transaction) {
// Monto de la transacción...
$amount = $transaction->amount(); // $2.31
 
// Obtener la factura relacionada cuando esté disponible...
$invoice = $transaction->invoice();
}

Identificadores Fiscales

Cashier ofrece una forma sencilla de gestionar los ID fiscales de un cliente. Por ejemplo, el método taxIds se puede usar para recuperar todos los ID fiscales asignados a un cliente como una colección:

$taxIds = $user->taxIds();

También puedes recuperar un ID fiscal específico para un cliente por su identificador:

$taxId = $user->findTaxId('txi_belgium');

Puedes crear un nuevo ID fiscal proporcionando un tipo y un valor válidos al método createTaxId:

$taxId = $user->createTaxId('eu_vat', 'BE0123456789');

El método createTaxId añadirá inmediatamente el ID de IVA a la cuenta del cliente. La verificación de los ID de IVA también la realiza Stripe; sin embargo, este es un proceso asíncrono. Puedes ser notificado de las actualizaciones de verificación suscribiéndote al evento de webhook customer.tax_id.updated e inspeccionando el parámetro de verification de los ID de IVA. Para obtener más información sobre cómo manejar webhooks, consulta la documentación sobre la definición de manipuladores de webhooks.

Puedes eliminar un ID fiscal utilizando el método deleteTaxId:

$user->deleteTaxId('txi_belgium');

Sincronización de Datos de Cliente con Stripe

Por lo general, cuando los usuarios de tu aplicación actualizan su nombre, dirección de correo electrónico u otra información que también se almacena en Stripe, debes informar a Stripe sobre las actualizaciones. Al hacerlo, la copia de la información de Stripe estará sincronizada con la de tu aplicación.

Para automatizar esto, puedes definir un escucha de eventos en tu modelo facturable que reaccione al evento updated del modelo. Luego, dentro de tu escucha de eventos, puedes invocar el método syncStripeCustomerDetails en el modelo:

use App\Models\User;
use function Illuminate\Events\queueable;
 
/**
* El método "booted" del modelo.
*/
protected static function booted(): void
{
static::updated(queueable(function (User $customer) {
if ($customer->hasStripeId()) {
$customer->syncStripeCustomerDetails();
}
}));
}

Ahora, cada vez que se actualiza tu modelo de cliente, su información se sincronizará con Stripe. Para mayor comodidad, Cashier sincronizará automáticamente la información de tu cliente con Stripe en la creación inicial del cliente.

Puedes personalizar las columnas utilizadas para sincronizar la información del cliente con Stripe mediante la anulación de varios métodos proporcionados por Cashier. Por ejemplo, puedes anular el método stripeName para personalizar el atributo que se debe considerar como el "nombre" del cliente cuando Cashier sincroniza la información del cliente con Stripe:

/**
* Obtener el nombre del cliente que debe sincronizarse con Stripe.
*/
public function stripeName(): string|null
{
return $this->company_name;
}

De manera similar, puedes anular los métodos stripeEmail, stripePhone, stripeAddress y stripePreferredLocales. Estos métodos sincronizarán la información con sus parámetros de cliente correspondientes cuando se actualice el objeto de cliente de Stripe. Si deseas tener un control total sobre el proceso de sincronización de la información del cliente, puedes anular el método syncStripeCustomerDetails.

Portal de Facturación

Stripe ofrece una forma sencilla de configurar un portal de facturación para que tu cliente pueda gestionar su suscripción, métodos de pago y ver su historial de facturación. Puedes redirigir a tus usuarios al portal de facturación invocando el método redirectToBillingPortal en el modelo facturable desde un controlador o una ruta:

use Illuminate\Http\Request;
 
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});

De forma predeterminada, cuando el usuario haya terminado de gestionar su suscripción, podrá regresar a la ruta home de tu aplicación mediante un enlace dentro del portal de facturación de Stripe. Puedes proporcionar una URL personalizada a la que el usuario debe regresar pasando la URL como argumento al método redirectToBillingPortal:

use Illuminate\Http\Request;
 
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('billing'));
});

Si deseas generar la URL al portal de facturación sin generar una respuesta de redirección HTTP, puedes invocar el método billingPortalUrl:

$url = $request->user()->billingPortalUrl(route('billing'));

Métodos de Pago

Almacenamiento de Métodos de Pago

Para crear suscripciones o realizar cargos "puntuales" con Stripe, deberás almacenar un método de pago y recuperar su identificador de Stripe. El enfoque utilizado para lograr esto difiere según si planeas usar el método de pago para suscripciones o cargos únicos, así que los examinaremos ambos a continuación.

Métodos de Pago para Suscripciones

Cuando almacenas la información de la tarjeta de crédito de un cliente para su uso futuro en una suscripción, se debe utilizar la API de "Setup Intents" de Stripe para recopilar de manera segura los detalles del método de pago del cliente. Un "Setup Intent" indica a Stripe la intención de cobrar el método de pago de un cliente. El rasgo Billable de Cashier incluye el método createSetupIntent para crear fácilmente un nuevo Setup Intent. Debes invocar este método desde la ruta o el controlador que renderizará el formulario que recopilará los detalles del método de pago de tu cliente:

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

Después de haber creado el Setup Intent y pasado a la vista, debes adjuntar su secreto al elemento que recopilará el método de pago. Por ejemplo, considera este formulario de "actualización de método 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>

A continuación, la biblioteca Stripe.js se puede utilizar para adjuntar un Elemento de Stripe al formulario y recopilar de manera segura los detalles del 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>

A continuación, la tarjeta se puede verificar y se puede recuperar un identificador de "método de pago" seguro de Stripe utilizando 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) {
// Mostrar "error.message" al usuario...
} else {
// La tarjeta se ha verificado con éxito...
}
});

Después de que la tarjeta haya sido verificada por Stripe, puedes pasar el identificador setupIntent.payment_method resultante a tu aplicación de Laravel, donde se puede adjuntar al cliente. El método de pago se puede agregar como un nuevo método de pago o utilizar para actualizar el método de pago predeterminado. También puedes usar inmediatamente el identificador del método de pago para crear una nueva suscripción.

Nota Si desea obtener más información sobre los Setup Intents y la recopilación de detalles de pago del cliente, consulte esta descripción general proporcionada por Stripe en este enlace.

Métodos de Pago para Cargos Individuales

Por supuesto, al realizar un solo cargo contra el método de pago de un cliente, solo necesitaremos usar un identificador de método de pago una vez. Debido a las limitaciones de Stripe, no puedes usar el método de pago predeterminado almacenado de un cliente para cargos únicos. Debes permitir que el cliente ingrese sus detalles de método de pago utilizando la biblioteca 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>

Después de definir un formulario como este, se puede utilizar la biblioteca Stripe.js para adjuntar un Elemento de Stripe al formulario y recopilar de manera segura los detalles del 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>

A continuación, la tarjeta se puede verificar y se puede recuperar un identificador de "método de pago" seguro de Stripe mediante 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) {
// Mostrar "error.message" al usuario...
} else {
// La tarjeta se ha verificado con éxito...
}
});

Si la tarjeta se verifica correctamente, puedes pasar paymentMethod.id a tu aplicación de Laravel y procesar un cargo único.

Recuperación de Métodos de Pago

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

$paymentMethods = $user->paymentMethods();

De forma predeterminada, este método devolverá métodos de pago del tipo card. Para recuperar métodos de pago de un tipo diferente, puedes pasar el type como argumento al método:

$paymentMethods = $user->paymentMethods('sepa_debit');

Para recuperar el método de pago predeterminado del cliente, se puede usar el método defaultPaymentMethod:

$paymentMethod = $user->defaultPaymentMethod();

Puedes recuperar un método de pago específico que está adjunto al modelo facturable utilizando el método findPaymentMethod:

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

Determinación de si un Usuario Tiene un Método de Pago

Para determinar si un modelo facturable tiene un método de pago predeterminado adjunto a su cuenta, invoca el método hasDefaultPaymentMethod:

if ($user->hasDefaultPaymentMethod()) {
// ...
}

Puedes usar el método hasPaymentMethod para determinar si un modelo facturable tiene al menos un método de pago adjunto a su cuenta:

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

Este método determinará si el modelo facturable tiene métodos de pago del tipo card. Para determinar si existe un método de pago de otro tipo para el modelo, puedes pasar el type como argumento al método:

if ($user->hasPaymentMethod('sepa_debit')) {
// ...
}

Actualización del Método de Pago Predeterminado

El método updateDefaultPaymentMethod se puede utilizar para actualizar la información del método de pago predeterminado de un cliente. Este método acepta un identificador de método de pago de Stripe y asignará el nuevo método de pago como el método de pago predeterminado para la facturación:

$user->updateDefaultPaymentMethod($paymentMethod);

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

$user->updateDefaultPaymentMethodFromStripe();

Advertencia El método de pago predeterminado en un cliente solo se puede usar para facturación y para crear nuevas suscripciones. Debido a las limitaciones impuestas por Stripe, no se puede utilizar para cargos únicos.

Adición de Métodos de Pago

Para agregar un nuevo método de pago, puedes llamar al método addPaymentMethod en el modelo facturable, pasando el identificador del método de pago:

$user->addPaymentMethod($paymentMethod);

Nota Para aprender a recuperar identificadores de métodos de pago, revise la documentación de almacenamiento de métodos de pago.

Eliminación de Métodos de Pago

Para eliminar un método de pago, puedes llamar al método delete en la instancia de Laravel\Cashier\PaymentMethod que deseas eliminar:

$paymentMethod->delete();

El método deletePaymentMethod eliminará un método de pago específico del modelo facturable:

$user->deletePaymentMethod('pm_visa');

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

$user->deletePaymentMethods();

De forma predeterminada, este método eliminará los métodos de pago del tipo card. Para eliminar métodos de pago de un tipo diferente, puedes pasar el type como argumento al método:

$user->deletePaymentMethods('sepa_debit');

Advertencia Si un usuario tiene una suscripción activa, tu aplicación no debería permitirle eliminar su método de pago predeterminado.

Suscripciones

Las suscripciones ofrecen una forma de configurar pagos recurrentes para tus clientes. Las suscripciones de Stripe gestionadas por Cashier admiten múltiples precios de suscripción, cantidades de suscripción, pruebas y más.

Creación de Suscripciones

Para crear una suscripción, primero recupera una instancia de tu modelo facturable, que típicamente será una instancia de App\Models\User. Una vez que hayas recuperado la instancia del modelo, puedes usar el método newSubscription para crear la suscripción del modelo:

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription(
'default', 'price_monthly'
)->create($request->paymentMethodId);
 
// ...
});

El primer argumento pasado al método newSubscription debería ser el nombre interno de la suscripción. Si tu aplicación solo ofrece una única suscripción, podrías llamarla default o primary. Este nombre de suscripción es solo para uso interno de la aplicación y no está destinado a mostrarse a los usuarios. Además, no debe contener espacios y nunca debe cambiarse después de crear la suscripción. El segundo argumento es el precio específico al que el usuario se está suscribiendo. Este valor debe corresponder al identificador del precio en Stripe.

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

Advertencia Al pasar directamente un identificador de método de pago al método de creación de suscripción create, también se añadirá automáticamente a los métodos de pago almacenados del usuario.

Recolección de Pagos Recurrentes a través de Correos Electrónicos de Facturas

En lugar de recopilar automáticamente los pagos recurrentes de un cliente, puedes indicar a Stripe que envíe un correo electrónico con una factura al cliente cada vez que deba realizarse su pago recurrente. Luego, el cliente puede pagar manualmente la factura una vez que la reciba. El cliente no necesita proporcionar un método de pago por adelantado al recopilar pagos recurrentes a través de facturas:

$user->newSubscription('default', 'price_monthly')->createAndSendInvoice();

El tiempo que tiene un cliente para pagar su factura antes de que se cancele su suscripción está determinado por la opción days_until_due. De forma predeterminada, esto son 30 días; sin embargo, puedes proporcionar un valor específico para esta opción si lo deseas:

$user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [
'days_until_due' => 30
]);

Cantidades

Si deseas establecer una cantidad específica para el precio al crear la suscripción, debes invocar el método quantity en el constructor de suscripciones antes de crear la suscripción:

$user->newSubscription('default', 'price_monthly')
->quantity(5)
->create($paymentMethod);

Detalles Adicionales

Si deseas especificar opciones adicionales de cliente o suscripción admitidas por Stripe, puedes hacerlo pasándolas como segundo y tercer argumento al método create:

$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
], [
'metadata' => ['note' => 'Some extra information.'],
]);

Cupones

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

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

O, si deseas aplicar un código de promoción de Stripe, puedes usar el método withPromotionCode:

$user->newSubscription('default', 'price_monthly')
->withPromotionCode('promo_code_id')
->create($paymentMethod);

El ID del código de promoción dado debe ser el ID de la API de Stripe asignado al código de promoción y no el código de promoción orientado al cliente. Si necesitas encontrar el ID de un código de promoción según un código de promoción orientado al cliente dado, puedes usar el método findPromotionCode:

// Encontrar un ID de código de promoción por su código visible para el cliente...
$promotionCode = $user->findPromotionCode('SUMMERSALE');
 
// Encontrar un ID de código de promoción activo por su código visible para el cliente...
$promotionCode = $user->findActivePromotionCode('SUMMERSALE');

En el ejemplo anterior, el objeto $promotionCode devuelto es una instancia de Laravel\Cashier\PromotionCode. Esta clase decora un objeto Stripe\PromotionCode subyacente. Puedes recuperar el cupón relacionado con el código de promoción invocando el método coupon:

$coupon = $user->findPromotionCode('SUMMERSALE')->coupon();

La instancia de cupón te permite determinar el monto del descuento y si el cupón representa un descuento fijo o un descuento basado en un porcentaje:

if ($coupon->isPercentage()) {
return $coupon->percentOff().'%'; // 21.5%
} else {
return $coupon->amountOff(); // $5.99
}

También puedes recuperar los descuentos que se aplican actualmente a un cliente o suscripción:

$discount = $billable->discount();
 
$discount = $subscription->discount();

Las instancias devueltas de Laravel\Cashier\Discount decoran una instancia subyacente de Stripe\Discount. Puedes recuperar el cupón relacionado con este descuento invocando el método coupon:

$coupon = $subscription->discount()->coupon();

Si deseas aplicar un nuevo cupón o código de promoción a un cliente o suscripción, puedes hacerlo a través de los métodos applyCoupon o applyPromotionCode:

$billable->applyCoupon('coupon_id');
$billable->applyPromotionCode('promotion_code_id');
 
$subscription->applyCoupon('coupon_id');
$subscription->applyPromotionCode('promotion_code_id');

Recuerda, debes usar el ID de la API de Stripe asignado al código de promoción y no el código de promoción orientado al cliente. Solo se puede aplicar un cupón o código de promoción a un cliente o suscripción a la vez.

Para obtener más información sobre este tema, consulta la documentación de Stripe sobre cupones y códigos de promoción.

Adición de Suscripciones

Si deseas agregar una suscripción a un cliente que ya tiene un método de pago predeterminado, puedes invocar el método add en el constructor de suscripciones:

use App\Models\User;
 
$user = User::find(1);
 
$user->newSubscription('default', 'price_monthly')->add();

Creación de Suscripciones desde el Panel de Stripe

También puedes crear suscripciones desde el propio panel de Stripe. Al hacerlo, Cashier sincronizará las suscripciones recién agregadas y les asignará un nombre de default. Para personalizar el nombre de suscripción asignado a las suscripciones creadas en el panel, extiende el WebhookController y sobrescribe el método newSubscriptionName.

Además, solo puedes crear un tipo de suscripción a través del panel de Stripe. Si tu aplicación ofrece múltiples suscripciones que utilizan nombres diferentes, solo se puede agregar un tipo de suscripción a través del panel de Stripe.

Finalmente, asegúrate siempre de agregar solo una suscripción activa por tipo de suscripción ofrecida por tu aplicación. Si un cliente tiene dos suscripciones default, solo se usará la suscripción más recientemente agregada, aunque ambas se sincronizarán con la base de datos de tu aplicación.

Verificación del Estado de Suscripción

Una vez que un cliente está suscrito a tu aplicación, puedes verificar fácilmente su estado de suscripción mediante una variedad de métodos convenientes. En primer lugar, el método subscribed devuelve true si el cliente tiene una suscripción activa, incluso si la suscripción está actualmente en su período de prueba. El método subscribed acepta el nombre de la suscripción como su primer argumento:

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

El método subscribed también es un buen candidato para un middleware de ruta, lo que te permite filtrar el acceso a rutas y controladores según el estado de suscripción del usuario:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureUserIsSubscribed
{
/**
* Manejar una solicitud entrante.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->user() && ! $request->user()->subscribed('default')) {
// Este usuario no es un cliente pagador...
return redirect('billing');
}
 
return $next($request);
}
}

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

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

El método subscribedToProduct se puede usar para determinar si el usuario está suscrito a un producto dado basado en el identificador de un producto de Stripe dado. En Stripe, los productos son colecciones de precios. En este ejemplo, determinaremos si la suscripción default del usuario está suscrita activamente al producto "premium" de la aplicación. El identificador de producto de Stripe dado debe corresponder a uno de los identificadores de tus productos en el panel de Stripe:

if ($user->subscribedToProduct('prod_premium', 'default')) {
// ...
}

Al pasar un array al método subscribedToProduct, puedes determinar si la suscripción default del usuario está suscrita activamente a los productos "basic" o "premium" de la aplicación:

if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {
// ...
}

El método subscribedToPrice se puede usar para determinar si la suscripción de un cliente corresponde a un ID de precio dado:

if ($user->subscribedToPrice('price_basic_monthly', 'default')) {
// ...
}

El método recurring se puede usar para determinar si el usuario está actualmente suscrito y ya no está en su período de prueba:

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

Advertencia Si un usuario tiene dos suscripciones con el mismo nombre, el método subscription siempre devolverá la suscripción más reciente. Por ejemplo, un usuario podría tener dos registros de suscripción con el nombre default; sin embargo, una de las suscripciones puede ser una suscripción antigua y vencida, mientras que la otra es la suscripción actual y activa. Siempre se devolverá la suscripción más reciente mientras que las suscripciones más antiguas se mantienen en la base de datos para su revisión histórica.

Estado de Suscripción Cancelada

Para determinar si el usuario alguna vez fue un suscriptor activo pero canceló su suscripción, puedes usar el método canceled:

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

También puedes determinar si un usuario ha cancelado su suscripción pero aún está en su "período de gracia" hasta que la suscripción expire completamente. Por ejemplo, si un usuario cancela una suscripción el 5 de marzo que originalmente estaba programada para vencer el 10 de marzo, el usuario está en su "período de gracia" hasta el 10 de marzo. Ten en cuenta que el método subscribed sigue devolviendo true durante este tiempo:

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

Para determinar si el usuario ha cancelado su suscripción y ya no está en su "período de gracia", puedes usar el método ended:

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

Estado Incompleto y Vencido

Si una suscripción requiere una acción de pago secundaria después de su creación, la suscripción se marcará como incompleta. Los estados de las suscripciones se almacenan en la columna stripe_status de la tabla de base de datos subscriptions de Cashier.

De manera similar, si se requiere una acción de pago secundaria al cambiar de precios, la suscripción se marcará como past_due. Cuando tu suscripción esté en cualquiera de estos estados, no estará activa hasta que el cliente haya confirmado su pago. Puedes determinar si una suscripción tiene un pago incompleto usando el método hasIncompletePayment en el modelo facturable o en una instancia de suscripción:

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

Cuando una suscripción tiene un pago incompleto, debes dirigir al usuario a la página de confirmación de pago de Cashier, pasando el identificador latestPayment. Puedes usar el método latestPayment disponible en la instancia de suscripción para recuperar este identificador:

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

Si deseas que la suscripción aún se considere activa cuando está en un estado past_due o incompleta, puedes usar los métodos keepPastDueSubscriptionsActive y keepIncompleteSubscriptionsActive proporcionados por Cashier. Normalmente, estos métodos deben llamarse en el método register de tu App\Providers\AppServiceProvider:

use Laravel\Cashier\Cashier;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
Cashier::keepIncompleteSubscriptionsActive();
}

Advertencia Cuando una suscripción está en un estado incompleto, no se puede cambiar hasta que se confirme el pago. Por lo tanto, los métodos swap y updateQuantity lanzarán una excepción cuando la suscripción esté en un estado incompleto.

Ámbitos de Suscripción

La mayoría de los estados de suscripción también están disponibles como ámbitos de consulta para que puedas consultar fácilmente tu base de datos en busca de suscripciones que se encuentren en un estado dado:

// Obtener todas las suscripciones activas...
$subscriptions = Subscription::query()->active()->get();
 
// Obtener todas las suscripciones canceladas para un usuario...
$subscriptions = $user->subscriptions()->canceled()->get();

A continuación, se muestra una lista completa de los ámbitos disponibles:

Subscription::query()->active();
Subscription::query()->canceled();
Subscription::query()->ended();
Subscription::query()->incomplete();
Subscription::query()->notCanceled();
Subscription::query()->notOnGracePeriod();
Subscription::query()->notOnTrial();
Subscription::query()->onGracePeriod();
Subscription::query()->onTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();

Cambio de Precios

Después de que un cliente se suscribe a tu aplicación, ocasionalmente puede querer cambiar a un nuevo precio de suscripción. Para cambiar a un cliente a un nuevo precio, pasa el identificador de precio de Stripe al método swap. Al cambiar de precios, se asume que el usuario desea reactivar su suscripción si anteriormente fue cancelada. El identificador de precio dado debe corresponder a un identificador de precio de Stripe disponible en el panel de Stripe:

use App\Models\User;
 
$user = App\Models\User::find(1);
 
$user->subscription('default')->swap('price_yearly');

Si el cliente está en período de prueba, se mantendrá el período de prueba. Además, si existe una "cantidad" para la suscripción, esa cantidad también se mantendrá.

Si deseas cambiar de precios y cancelar cualquier período de prueba en el que el cliente se encuentre actualmente, puedes invocar el método skipTrial:

$user->subscription('default')
->skipTrial()
->swap('price_yearly');

Si deseas cambiar de precios e inmediatamente facturar al cliente en lugar de esperar hasta su próximo ciclo de facturación, puedes usar el método swapAndInvoice:

$user = User::find(1);
 
$user->subscription('default')->swapAndInvoice('price_yearly');

Prorrateos

De forma predeterminada, Stripe prorratea los cargos al cambiar entre precios. El método noProrate se puede usar para actualizar el precio de la suscripción sin prorratear los cargos:

$user->subscription('default')->noProrate()->swap('price_yearly');

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

Advertencia Ejecutar el método noProrate antes del método swapAndInvoice no tendrá efecto en la prorrateo. Siempre se emitirá una factura.

Cantidad de Suscripción

A veces, las suscripciones se ven afectadas por la "cantidad". Por ejemplo, una aplicación de gestión de proyectos podría cobrar $10 por mes por proyecto. Puedes usar los métodos incrementQuantity y decrementQuantity para incrementar o decrementar fácilmente la cantidad de tu suscripción:

use App\Models\User;
 
$user = User::find(1);
 
$user->subscription('default')->incrementQuantity();
 
// Agregar cinco a la cantidad actual de la suscripción...
$user->subscription('default')->incrementQuantity(5);
 
$user->subscription('default')->decrementQuantity();
 
// Restar cinco de la cantidad actual de la suscripción...
$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 se puede usar para actualizar la cantidad de la suscripción sin prorratear los cargos:

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

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

Cantidades para Suscripciones con Múltiples Productos

Si tu suscripción es una suscripción con varios productos, debes pasar el ID del precio cuya cantidad deseas incrementar o decrementar como segundo argumento a los métodos de incremento / decremento:

$user->subscription('default')->incrementQuantity(1, 'price_chat');

Suscripciones con Múltiples Productos

Suscripción con varios productos te permite asignar varios productos de facturación a una sola suscripción. Por ejemplo, imagina que estás construyendo una aplicación de "helpdesk" de servicio al cliente que tiene un precio de suscripción base de $10 por mes pero ofrece un producto adicional de chat en vivo por $15 adicionales por mes. La información para las suscripciones con varios productos se almacena en la tabla de base de datos subscription_items de Cashier.

Puedes especificar varios productos para una suscripción dada pasando un array de precios como segundo argumento al método newSubscription:

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription('default', [
'price_monthly',
'price_chat',
])->create($request->paymentMethodId);
 
// ...
});

En el ejemplo anterior, el cliente tendrá dos precios adjuntos a su suscripción default. Ambos precios se cargarán en sus respectivos intervalos de facturación. Si es necesario, puedes usar el método quantity para indicar una cantidad específica para cada precio:

$user = User::find(1);
 
$user->newSubscription('default', ['price_monthly', 'price_chat'])
->quantity(5, 'price_chat')
->create($paymentMethod);

Si desea agregar otro precio a una suscripción existente, puede invocar el método addPrice de la suscripción:

$user = User::find(1);
 
$user->subscription('default')->addPrice('price_chat');

El ejemplo anterior agregará el nuevo precio y se facturará al cliente en su próximo ciclo de facturación. Si desea facturar al cliente de inmediato, puede usar el método addPriceAndInvoice:

$user->subscription('default')->addPriceAndInvoice('price_chat');

Si desea agregar un precio con una cantidad específica, puede pasar la cantidad como segundo argumento de los métodos addPrice o addPriceAndInvoice:

$user = User::find(1);
 
$user->subscription('default')->addPrice('price_chat', 5);

Puede eliminar precios de suscripciones usando el método removePrice:

$user->subscription('default')->removePrice('price_chat');

Advertencia No puedes eliminar el último precio de una suscripción. En su lugar, simplemente debes cancelar la suscripción.

Intercambio de Precios

También puede cambiar los precios adjuntos a una suscripción con múltiples productos. Por ejemplo, imagine que un cliente tiene una suscripción price_basic con un producto adicional price_chat y desea actualizar al cliente de price_basic a price_pro:

use App\Models\User;
 
$user = User::find(1);
 
$user->subscription('default')->swap(['price_pro', 'price_chat']);

Al ejecutar el ejemplo anterior, el elemento de suscripción subyacente con price_basic se elimina y se conserva el de price_chat. Además, se crea un nuevo elemento de suscripción para price_pro.

También puede especificar opciones de elementos de suscripción pasando un array de pares clave/valor al método swap. Por ejemplo, puede ser necesario especificar las cantidades de precios de suscripción:

$user = User::find(1);
 
$user->subscription('default')->swap([
'price_pro' => ['quantity' => 5],
'price_chat'
]);

Si desea cambiar un solo precio en una suscripción, puede hacerlo usando el método swap en el propio elemento de suscripción. Este enfoque es particularmente útil si desea conservar todos los metadatos existentes en los otros precios de la suscripción:

$user = User::find(1);
 
$user->subscription('default')
->findItemOrFail('price_basic')
->swap('price_pro');

Prorrateo

De forma predeterminada, Stripe prorrateará los cargos al agregar o eliminar precios de una suscripción con múltiples productos. Si desea realizar un ajuste de precio sin prorrateo, debe encadenar el método noProrate en su operación de precio:

$user->subscription('default')->noProrate()->removePrice('price_chat');

Cantidades

Si desea actualizar cantidades en precios individuales de suscripción, puede hacerlo utilizando los métodos de cantidad existentes pasando el nombre del precio como un argumento adicional al método:

$user = User::find(1);
 
$user->subscription('default')->incrementQuantity(5, 'price_chat');
 
$user->subscription('default')->decrementQuantity(3, 'price_chat');
 
$user->subscription('default')->updateQuantity(10, 'price_chat');

Advertencia Cuando una suscripción tiene varios precios, los atributos stripe_price y quantity en el modelo Subscription serán null. Para acceder a los atributos de precio individuales, debes usar la relación items disponible en el modelo Subscription.

Elementos de Suscripción

Cuando una suscripción tiene varios precios, tendrá varios "elementos" de suscripción almacenados en la tabla subscription_items de su base de datos. Puede acceder a ellos a través de la relación items en la suscripción:

use App\Models\User;
 
$user = User::find(1);
 
$subscriptionItem = $user->subscription('default')->items->first();
 
// Obtener el precio y la cantidad de Stripe para un artículo específico...
$stripePrice = $subscriptionItem->stripe_price;
$quantity = $subscriptionItem->quantity;

También puede recuperar un precio específico usando el método findItemOrFail:

$user = User::find(1);
 
$subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat');

Suscripciones Múltiples

Stripe permite que sus clientes tengan múltiples suscripciones simultáneamente. Por ejemplo, puede tener un gimnasio que ofrezca una suscripción para natación y otra para levantamiento de pesas, y cada suscripción puede tener precios diferentes. Por supuesto, los clientes deberían poder suscribirse a uno o ambos planes.

Cuando su aplicación crea suscripciones, puede proporcionar el nombre de la suscripción al método newSubscription. El nombre puede ser cualquier cadena que represente el tipo de suscripción que el usuario está iniciando:

use Illuminate\Http\Request;
 
Route::post('/swimming/subscribe', function (Request $request) {
$request->user()->newSubscription('swimming')
->price('price_swimming_monthly')
->create($request->paymentMethodId);
 
// ...
});

En este ejemplo, iniciamos una suscripción mensual de natación para el cliente. Sin embargo, pueden querer cambiar a una suscripción anual más tarde. Al ajustar la suscripción del cliente, simplemente podemos cambiar el precio en la suscripción de natación:

$user->subscription('swimming')->swap('price_swimming_yearly');

Por supuesto, también puede cancelar la suscripción por completo:

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

Facturación Medida

La facturación medida le permite cobrar a los clientes según el uso de sus productos durante un ciclo de facturación. Por ejemplo, puede cobrar a los clientes según la cantidad de mensajes de texto o correos electrónicos que envíen por mes.

Para empezar a utilizar la facturación medida, primero deberá crear un nuevo producto en su panel de control de Stripe con un precio medido. Luego, utilice el meteredPrice para agregar el ID del precio medido a la suscripción de un cliente:

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription('default')
->meteredPrice('price_metered')
->create($request->paymentMethodId);
 
// ...
});

También puede iniciar una suscripción medida a través de Stripe Checkout:

$checkout = Auth::user()
->newSubscription('default', [])
->meteredPrice('price_metered')
->checkout();
 
return view('your-checkout-view', [
'checkout' => $checkout,
]);

Uso de Informes

A medida que su cliente utilice su aplicación, informará su uso a Stripe para que puedan facturarse con precisión. Para incrementar el uso de una suscripción medida, puede utilizar el método reportUsage:

$user = User::find(1);
 
$user->subscription('default')->reportUsage();

De forma predeterminada, se añade una "cantidad de uso" de 1 al período de facturación. Alternativamente, puede pasar una cantidad específica de "uso" para agregar al uso del cliente durante el período de facturación:

$user = User::find(1);
 
$user->subscription('default')->reportUsage(15);

Si su aplicación ofrece varios precios en una sola suscripción, deberá utilizar el método reportUsageFor para especificar el precio medido para el cual desea informar el uso:

$user = User::find(1);
 
$user->subscription('default')->reportUsageFor('price_metered', 15);

A veces, es posible que necesite actualizar el uso que ha informado previamente. Para lograrlo, puede pasar una marca de tiempo o una instancia de DateTimeInterface como segundo parámetro a reportUsage. Al hacerlo, Stripe actualizará el uso que se informó en ese momento dado. Puede continuar actualizando registros de uso anteriores, ya que la fecha y hora dadas aún se encuentran dentro del período de facturación actual:

$user = User::find(1);
 
$user->subscription('default')->reportUsage(5, $timestamp);

Recuperación de Registros de Uso

Para recuperar el uso pasado de un cliente, puede utilizar el método usageRecords de una instancia de suscripción:

$user = User::find(1);
 
$usageRecords = $user->subscription('default')->usageRecords();

Si su aplicación ofrece varios precios en una sola suscripción, puede utilizar el método usageRecordsFor para especificar el precio medido del cual desea recuperar registros de uso:

$user = User::find(1);
 
$usageRecords = $user->subscription('default')->usageRecordsFor('price_metered');

Los métodos usageRecords y usageRecordsFor devuelven una instancia de Collection que contiene un array asociativo de registros de uso. Puede iterar sobre este array para mostrar el uso total de un cliente:

@foreach ($usageRecords as $usageRecord)
- Period Starting: {{ $usageRecord['period']['start'] }}
- Period Ending: {{ $usageRecord['period']['end'] }}
- Total Usage: {{ $usageRecord['total_usage'] }}
@endforeach

Para obtener una referencia completa de todos los datos de uso devueltos y cómo utilizar la paginación basada en curso de Stripe, consulte la documentación oficial de la API de Stripe.

Impuestos de Suscripción

Advertencia En lugar de calcular manualmente las tasas de impuestos, puedes calcular automáticamente los impuestos utilizando Stripe Tax.

Para especificar las tasas impositivas que un usuario paga en una suscripción, debe implementar el método taxRates en su modelo facturable y devolver un array que contenga los ID de tasas impositivas de Stripe. Puede definir estas tasas impositivas en su panel de control de Stripe:

/**
* Las tasas de impuestos que deben aplicarse a las suscripciones del cliente.
*
* @return array<int, string>
*/
public function taxRates(): array
{
return ['txr_id'];
}

El método taxRates le permite aplicar una tasa impositiva de manera individual para cada cliente, lo que puede ser útil para una base de usuarios que abarque varios países y tasas impositivas.

Si ofrece suscripciones con múltiples productos, puede definir tasas impositivas diferentes para cada precio implementando un método priceTaxRates en su modelo facturable:

/**
* Las tasas de impuestos que deben aplicarse a las suscripciones del cliente.
*
* @return array<string, array<int, string>>
*/
public function priceTaxRates(): array
{
return [
'price_monthly' => ['txr_id'],
];
}

Advertencia El método taxRates solo se aplica a cargos de suscripción. Si utilizas Cashier para realizar "cargos únicos", deberás especificar manualmente la tasa impositiva en ese momento.

Sincronización de Tasas de Impuestos

Al cambiar los ID de tasas impositivas codificados en duro devueltos por el método taxRates, la configuración de impuestos en cualquier suscripción existente para el usuario seguirá siendo la misma. Si desea actualizar el valor del impuesto para las suscripciones existentes con los nuevos valores de taxRates, debe llamar al método syncTaxRates en la instancia de suscripción del usuario:

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

Esto también sincronizará las tasas impositivas de los elementos para una suscripción con múltiples productos. Si su aplicación ofrece suscripciones con múltiples productos, asegúrese de que su modelo facturable implemente el método priceTaxRates discutido anteriormente.

Exención de Impuestos

Cashier también ofrece los métodos isNotTaxExempt, isTaxExempt y reverseChargeApplies para determinar si el cliente está exento de impuestos. Estos métodos llamarán a la API de Stripe para determinar el estado de exención de impuestos de un cliente:

use App\Models\User;
 
$user = User::find(1);
 
$user->isTaxExempt();
$user->isNotTaxExempt();
$user->reverseChargeApplies();

Advertencia Estos métodos también están disponibles en cualquier objeto Laravel\Cashier\Invoice. Sin embargo, cuando se invocan en un objeto Invoice, los métodos determinarán el estado de exención en el momento en que se creó la factura.

Fecha de Anclaje de Suscripción

De forma predeterminada, el anclaje del ciclo de facturación es la fecha en que se creó la suscripción o, si se utiliza un período de prueba, la fecha en que termina la prueba. Si desea modificar la fecha de anclaje de facturación, puede utilizar el método anchorBillingCycleOn:

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$anchor = Carbon::parse('first day of next month');
 
$request->user()->newSubscription('default', 'price_monthly')
->anchorBillingCycleOn($anchor->startOfDay())
->create($request->paymentMethodId);
 
// ...
});

Para obtener más información sobre la gestión de los ciclos de facturación de las suscripciones, consulte la documentación de Stripe sobre ciclos de facturación

Cancelación de Suscripciones

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

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

Cuando se cancela una suscripción, Cashier establecerá automáticamente la columna ends_at en su tabla de base de datos subscriptions. Esta columna se utiliza para saber cuándo debería comenzar a devolver false el método subscribed.

Por ejemplo, si un cliente cancela una suscripción el 1 de marzo, pero la suscripción no estaba programada para finalizar hasta el 5 de marzo, el método subscribed seguirá devolviendo true hasta el 5 de marzo. Esto se hace porque generalmente se permite a un usuario seguir utilizando una aplicación hasta el final de su ciclo de facturación.

Puede determinar si un usuario ha cancelado su suscripción pero aún está en su "período de gracia" utilizando el método onGracePeriod:

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

Si desea cancelar una suscripción de inmediato, llame al método cancelNow en la suscripción del usuario:

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

Si desea cancelar una suscripción de inmediato y facturar cualquier uso medido no facturado restante o nuevos/pendientes elementos de factura de prorrateo, llame al método cancelNowAndInvoice en la suscripción del usuario:

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

También puede elegir cancelar la suscripción en un momento específico:

$user->subscription('default')->cancelAt(
now()->addDays(10)
);

Reanudación de Suscripciones

Si un cliente ha cancelado su suscripción y desea reanudarla, puede invocar el método resume en la suscripción. El cliente aún debe estar dentro de su "período de gracia" para poder reanudar una suscripción:

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

Si el cliente cancela una suscripción y luego la reanuda antes de que la suscripción haya expirado por completo, el cliente no será facturado de inmediato. En cambio, su suscripción se reactivará y se facturará en el ciclo de facturación original.

Pruebas de Suscripción

Con Método de Pago por Adelantado

Si desea ofrecer períodos de prueba a sus clientes mientras aún recopila información del método de pago por adelantado, debe utilizar el método trialDays al crear sus suscripciones:

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription('default', 'price_monthly')
->trialDays(10)
->create($request->paymentMethodId);
 
// ...
});

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 e instruirá a Stripe para que no comience a facturar al cliente hasta después de esta fecha. Al usar el método trialDays, Cashier sobrescribirá cualquier período de prueba predeterminado configurado para el precio en Stripe.

Advertencia Si la suscripción del cliente no se cancela antes de la fecha de finalización del período de prueba, se le cobrará tan pronto como expire el período de prueba, así que asegúrate de notificar a tus usuarios sobre la fecha de finalización de su período de prueba.

El método trialUntil le permite proporcionar una instancia de DateTime que especifica cuándo debería finalizar el período de prueba:

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

Puede determinar si un 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 la suscripción. Los dos ejemplos a continuación son equivalentes:

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

Puede utilizar el método endTrial para finalizar inmediatamente un período de prueba de suscripción:

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

Para determinar si un período de prueba existente ha expirado, puede utilizar los métodos hasExpiredTrial:

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

Definición de Días de Prueba en Stripe / Cashier

Puede optar por definir cuántos días de prueba recibirán sus precios en el panel de control de Stripe o pasarlos siempre explícitamente utilizando Cashier. Si elige definir los días de prueba de sus precios en Stripe, debe tener en cuenta que las nuevas suscripciones, incluidas las nuevas suscripciones para un cliente que tuvo una suscripción en el pasado, siempre recibirán un período de prueba a menos que llame explícitamente al método skipTrial().

Sin Método de Pago por Adelantado

Si desea ofrecer períodos de prueba sin recopilar la información del método de pago del usuario por adelantado, puede establecer la columna trial_ends_at en el registro del usuario con la fecha de finalización del período de prueba deseada. Esto se hace típicamente durante el registro del usuario:

use App\Models\User;
 
$user = User::create([
// ...
'trial_ends_at' => now()->addDays(10),
]);

Advertencia Asegúrate de agregar un tipo de fecha para el atributo trial_ends_at dentro de la definición de la clase de tu modelo facturable.

Cashier se refiere a este tipo de prueba como una "prueba genérica", ya que no está asociada a ninguna suscripción existente. El método onTrial en la instancia del modelo facturable devolverá true si la fecha actual no ha pasado el valor de trial_ends_at:

if ($user->onTrial()) {
// El usuario está dentro de su período de prueba...
}

Una vez que esté listo para crear una suscripción real para el usuario, puede usar el método newSubscription como de costumbre:

$user = User::find(1);
 
$user->newSubscription('default', 'price_monthly')->create($paymentMethod);

Para recuperar la fecha de finalización del período de prueba del usuario, puede utilizar el método trialEndsAt. Este método devolverá una instancia de fecha Carbon si un usuario está en un período de prueba o null si no lo está. También puede pasar un parámetro opcional del nombre de la suscripción si desea obtener la fecha de finalización del período de prueba para una suscripción específica que no sea la predeterminada:

if ($user->onTrial()) {
$trialEndsAt = $user->trialEndsAt('main');
}

También puede utilizar el método onGenericTrial si desea saber específicamente que el usuario está dentro de su período de prueba "genérico" y aún no ha creado una suscripción real:

if ($user->onGenericTrial()) {
// El usuario está dentro de su período de prueba "genérico"...
}

Ampliación de Pruebas

El método extendTrial le permite extender el período de prueba de una suscripción después de que se haya creado la suscripción. Si la prueba ya ha expirado y el cliente ya está siendo facturado por la suscripción, aún puede ofrecerles un período de prueba extendido. El tiempo pasado dentro del período de prueba se restará de la próxima factura del cliente:

use App\Models\User;
 
$subscription = User::find(1)->subscription('default');
 
// Finalizar la prueba dentro de 7 días...
$subscription->extendTrial(
now()->addDays(7)
);
 
// Agregar 5 días adicionales a la prueba...
$subscription->extendTrial(
$subscription->trial_ends_at->addDays(5)
);

Manejo de Webhooks de Stripe

Nota Puede utilizar Stripe CLI para ayudar a probar webhooks durante el desarrollo local.

Stripe puede notificar a su aplicación de una variedad de eventos a través de webhooks. De forma predeterminada, una ruta que apunta al controlador de webhook de Cashier se registra automáticamente mediante el proveedor de servicios de Cashier. Este controlador manejará todas las solicitudes de webhook entrantes.

De forma predeterminada, el controlador de webhook de Cashier manejará automáticamente la cancelación de suscripciones que tengan demasiados cargos fallidos (según lo definido por su configuración de Stripe), actualizaciones de clientes, eliminaciones de clientes, actualizaciones de suscripciones y cambios de método de pago; sin embargo, como descubriremos pronto, puede extender este controlador para manejar cualquier evento de webhook de Stripe que desee.

Para asegurarse de que su aplicación pueda manejar los webhooks de Stripe, asegúrese de configurar la URL del webhook en el panel de control de Stripe. De forma predeterminada, el controlador de webhook de Cashier responde a la ruta de URL /stripe/webhook. La lista completa de todos los webhooks que debe habilitar en el panel de control de Stripe son:

  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • customer.updated
  • customer.deleted
  • payment_method.automatically_updated
  • invoice.payment_action_required
  • invoice.payment_succeeded

Para mayor comodidad, Cashier incluye un comando Artisan cashier:webhook. Este comando creará un webhook en Stripe que escucha todos los eventos requeridos por Cashier:

php artisan cashier:webhook

De forma predeterminada, el webhook creado apuntará a la URL definida por la variable de entorno APP_URL y la ruta cashier.webhook que se incluye con Cashier. Puede proporcionar la opción --url al invocar el comando si desea utilizar una URL diferente:

php artisan cashier:webhook --url "https://example.com/stripe/webhook"

El webhook que se crea utilizará la versión de la API de Stripe con la que su versión de Cashier es compatible. Si desea utilizar una versión diferente de Stripe, puede proporcionar la opción --api-version:

php artisan cashier:webhook --api-version="2019-12-03"

Después de la creación, el webhook estará inmediatamente activo. Si desea crear el webhook pero tenerlo desactivado hasta que esté listo, puede proporcionar la opción --disabled al invocar el comando:

php artisan cashier:webhook --disabled

Advertencia Asegúrate de proteger las solicitudes entrantes de webhook de Stripe con el middleware de verificación de firma de webhook incluido en Cashier verifying-webhook-signatures.

Webhooks y Protección CSRF

Dado que los webhooks de Stripe deben pasar por alto la protección CSRF de Laravel, asegúrese de listar la URI como una excepción en el middleware VerifyCsrfToken de su aplicación o liste la ruta fuera del grupo de middleware web:

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

Definición de Manipuladores de Eventos de Webhook

Cashier maneja automáticamente las cancelaciones de suscripciones por cargos fallidos y otros eventos comunes de webhook de Stripe. Sin embargo, si tiene eventos adicionales de webhook que le gustaría manejar, puede hacerlo escuchando los siguientes eventos que emite Cashier:

  • Laravel\Cashier\Events\WebhookReceived
  • Laravel\Cashier\Events\WebhookHandled

Ambos eventos contienen la carga completa del webhook de Stripe. Por ejemplo, si desea manejar el webhook invoice.payment_succeeded, puede registrar un escucha que manejará el evento:

<?php
 
namespace App\Listeners;
 
use Laravel\Cashier\Events\WebhookReceived;
 
class StripeEventListener
{
/**
* Manejar los webhooks de Stripe recibidos.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['type'] === 'invoice.payment_succeeded') {
// Manejar el evento entrante...
}
}
}

Una vez que se haya definido su escucha, puede registrarlo en el EventServiceProvider de su aplicación:

<?php
 
namespace App\Providers;
 
use App\Listeners\StripeEventListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Laravel\Cashier\Events\WebhookReceived;
 
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
WebhookReceived::class => [
StripeEventListener::class,
],
];
}

Verificación de Firmas de Webhook

Para asegurar sus webhooks, puede utilizar las firmas de webhook de Stripe. Para mayor comodidad, Cashier incluye automáticamente un middleware que valida que la solicitud entrante de webhook de Stripe es válida.

Para habilitar la verificación de webhook, asegúrese de que la variable de entorno STRIPE_WEBHOOK_SECRET esté configurada en el archivo .env de su aplicación. El "secreto" del webhook se puede obtener desde el panel de control de su cuenta de Stripe.

Cargos Individuales

Carga Simple

Si desea realizar un cargo único contra un cliente, puede utilizar el método charge en una instancia del modelo facturable. Necesitará proporcionar un identificador de método de pago como segundo argumento al método charge:

use Illuminate\Http\Request;
 
Route::post('/purchase', function (Request $request) {
$stripeCharge = $request->user()->charge(
100, $request->paymentMethodId
);
 
// ...
});

El método charge acepta un array como tercer argumento, lo que le permite pasar cualquier opción que desee a la creación subyacente del cargo de Stripe. Puede encontrar más información sobre las opciones disponibles al crear cargos en la documentación de Stripe:

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

También puede usar el método charge sin un cliente o usuario subyacente. Para hacer esto, invoque el método charge en una nueva instancia del modelo facturable de su aplicación:

use App\Models\User;
 
$stripeCharge = (new User)->charge(100, $paymentMethod);

El método charge lanzará una excepción si el cargo falla. Si el cargo tiene éxito, el método devolverá una instancia de Laravel\Cashier\Payment:

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

Advertencia El método charge acepta el monto del pago en la denominación más baja de la moneda utilizada por tu aplicación. Por ejemplo, si los clientes pagan en dólares estadounidenses, los montos deben especificarse en centavos.

Cargo con Factura

A veces, es posible que necesite realizar un cargo único y ofrecer un recibo en PDF a su cliente. El método invoicePrice le permite hacer precisamente eso. Por ejemplo, facturemos a un cliente por cinco camisas nuevas:

$user->invoicePrice('price_tshirt', 5);

La factura se cargará inmediatamente en el método de pago predeterminado del usuario. El método invoicePrice también acepta un array como tercer argumento. Este array contiene las opciones de facturación para el ítem de la factura. El cuarto argumento aceptado por el método también es un array que debe contener las opciones de facturación para la factura en sí:

$user->invoicePrice('price_tshirt', 5, [
'discounts' => [
['coupon' => 'SUMMER21SALE']
],
], [
'default_tax_rates' => ['txr_id'],
]);

De manera similar a invoicePrice, puede utilizar el método tabPrice para crear un cargo único por varios artículos (hasta 250 artículos por factura) agregándolos a la "cuenta" del cliente y luego facturando al cliente. Por ejemplo, podemos facturar a un cliente por cinco camisas y dos tazas:

$user->tabPrice('price_tshirt', 5);
$user->tabPrice('price_mug', 2);
$user->invoice();

Alternativamente, puede usar el método invoiceFor para hacer un cargo "único" contra el método de pago predeterminado del cliente:

$user->invoiceFor('One Time Fee', 500);

Aunque el método invoiceFor está disponible para su uso, se recomienda que utilice los métodos invoicePrice y tabPrice con precios predefinidos. Al hacerlo, tendrá acceso a un mejor análisis y datos dentro del panel de Stripe con respecto a sus ventas por producto.

Advertencia Los métodos invoice, invoicePrice y invoiceFor crearán una factura de Stripe que volverá a intentar los intentos de facturación fallidos. Si no quieres que las facturas vuelvan a intentar cargos fallidos, deberás cerrarlas utilizando la API de Stripe después del primer cargo fallido.

Creación de Intenciones de Pago

Puede crear un nuevo intento de pago de Stripe invocando el método pay en una instancia del modelo facturable. Llamar a este método creará un intento de pago que está envuelto en una instancia de Laravel\Cashier\Payment:

use Illuminate\Http\Request;
 
Route::post('/pay', function (Request $request) {
$payment = $request->user()->pay(
$request->get('amount')
);
 
return $payment->client_secret;
});

Después de crear el intento de pago, puede devolver el secreto del cliente a la interfaz frontal de su aplicación para que el usuario pueda completar el pago en su navegador. Para obtener más información sobre la construcción de flujos de pago completos utilizando intentos de pago de Stripe, consulte la documentación de Stripe.

Al utilizar el método pay, los métodos de pago predeterminados habilitados en su panel de control de Stripe estarán disponibles para el cliente. Alternativamente, si solo desea permitir el uso de algunos métodos de pago específicos, puede utilizar el método payWith:

use Illuminate\Http\Request;
 
Route::post('/pay', function (Request $request) {
$payment = $request->user()->payWith(
$request->get('amount'), ['card', 'bancontact']
);
 
return $payment->client_secret;
});

Advertencia Los métodos pay y payWith aceptan el monto del pago en la denominación más baja de la moneda utilizada por tu aplicación. Por ejemplo, si los clientes pagan en dólares estadounidenses, los montos deben especificarse en centavos.

Reembolso de Cargos

Si necesita reembolsar un cargo de Stripe, puede utilizar el método refund. Este método acepta el ID del intento de pago de Stripe como su primer argumento:

$payment = $user->charge(100, $paymentMethodId);
 
$user->refund($payment->id);

Facturas

Recuperación de Facturas

Puede recuperar fácilmente un array de facturas de un modelo facturable utilizando el método invoices. El método invoices devuelve una colección de instancias de Laravel\Cashier\Invoice:

$invoices = $user->invoices();

Si desea incluir facturas pendientes en los resultados, puede utilizar el método invoicesIncludingPending:

$invoices = $user->invoicesIncludingPending();

Puede utilizar el método findInvoice para recuperar una factura específica por su ID:

$invoice = $user->findInvoice($invoiceId);

Visualización de Información de Factura

Al enumerar las facturas para el cliente, puede utilizar los métodos de la factura para mostrar la información relevante de la factura. Por ejemplo, puede desear listar cada factura en una tabla, permitiendo al usuario descargar fácilmente cualquiera 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>

Facturas Pendientes

Para recuperar la próxima factura de un cliente, puede utilizar el método upcomingInvoice:

$invoice = $user->upcomingInvoice();

De manera similar, si el cliente tiene varias suscripciones, también puede recuperar la próxima factura de una suscripción específica:

$invoice = $user->subscription('default')->upcomingInvoice();

Vista Previa de Facturas de Suscripción

Utilizando el método previewInvoice, puede obtener una vista previa de una factura antes de realizar cambios de precio. Esto le permitirá determinar cómo se verá la factura del cliente cuando se realice un cambio de precio dado:

$invoice = $user->subscription('default')->previewInvoice('price_yearly');

Puede pasar un array de precios al método previewInvoice para obtener una vista previa de las facturas con varios nuevos precios:

$invoice = $user->subscription('default')->previewInvoice(['price_yearly', 'price_metered']);

Generación de PDF de Facturas

Antes de generar facturas en PDF, debe utilizar Composer para instalar la biblioteca Dompdf, que es el renderizador de facturas predeterminado para Cashier:

composer require dompdf/dompdf

Desde una ruta o controlador, puede utilizar el método downloadInvoice para generar una descarga en PDF de una factura específica. Este método generará automáticamente la respuesta HTTP adecuada necesaria para descargar la factura:

use Illuminate\Http\Request;
 
Route::get('/user/invoice/{invoice}', function (Request $request, string $invoiceId) {
return $request->user()->downloadInvoice($invoiceId);
});

De forma predeterminada, todos los datos de la factura se derivan de los datos del cliente y la factura almacenados en Stripe. El nombre de archivo se basa en el valor de configuración app.name. Sin embargo, puede personalizar algunos de estos datos proporcionando un array como segundo argumento al método downloadInvoice. Este array le permite personalizar información como los detalles de su empresa y producto:

return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
'street' => 'Main Str. 1',
'location' => '2000 Antwerp, Belgium',
'phone' => '+32 499 00 00 00',
'email' => '[email protected]',
'url' => 'https://example.com',
'vendorVat' => 'BE123456789',
]);

El método downloadInvoice también permite un nombre de archivo personalizado a través de su tercer argumento. Este nombre de archivo se sufijará automáticamente con .pdf:

return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice');

Renderizado de Facturas Personalizadas

Cashier también permite usar un renderizador de facturas personalizado. De forma predeterminada, Cashier utiliza la implementación DompdfInvoiceRenderer, que utiliza la biblioteca PHP dompdf para generar las facturas de Cashier. Sin embargo, puede usar cualquier renderizador que desee implementando la interfaz Laravel\Cashier\Contracts\InvoiceRenderer. Por ejemplo, puede optar por renderizar un PDF de factura utilizando una llamada API a un servicio de renderizado de PDF de terceros:

use Illuminate\Support\Facades\Http;
use Laravel\Cashier\Contracts\InvoiceRenderer;
use Laravel\Cashier\Invoice;
 
class ApiInvoiceRenderer implements InvoiceRenderer
{
/**
* Renderizar la factura dada y devolver los bytes brutos del PDF.
*/
public function render(Invoice $invoice, array $data = [], array $options = []): string
{
$html = $invoice->view($data)->render();
 
return Http::get('https://example.com/html-to-pdf', ['html' => $html])->get()->body();
}
}

Una vez que haya implementado el contrato del renderizador de facturas, debe actualizar el valor de configuración cashier.invoices.renderer en el archivo de configuración config/cashier.php de su aplicación. Este valor de configuración debe establecerse con el nombre de clase de su implementación de renderizador personalizado.

Checkout

Cashier Stripe también proporciona soporte para Stripe Checkout. Stripe Checkout facilita la implementación de páginas personalizadas para aceptar pagos al proporcionar una página de pago preconstruida y alojada.

La siguiente documentación contiene información sobre cómo empezar a usar Stripe Checkout con Cashier. Para obtener más información sobre Stripe Checkout, también debe considerar revisar la propia documentación de Stripe sobre Checkout.

Checkouts de Productos

Puede realizar un pago para un producto existente que se haya creado en su panel de control de Stripe utilizando el método checkout en un modelo facturable. El método checkout iniciará una nueva sesión de Stripe Checkout. De forma predeterminada, debe pasar un ID de precio de Stripe:

use Illuminate\Http\Request;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout('price_tshirt');
});

Si es necesario, también puede especificar una cantidad de producto:

use Illuminate\Http\Request;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout(['price_tshirt' => 15]);
});

Cuando un cliente visita esta ruta, será redirigido a la página de Checkout de Stripe. De forma predeterminada, cuando un usuario completa o cancela una compra con éxito, se redirigirá a su ruta de home, pero puede especificar URL de retorno personalizadas mediante las opciones success_url y cancel_url:

use Illuminate\Http\Request;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout(['price_tshirt' => 1], [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

Al definir su opción de checkout success_url, puede instruir a Stripe que agregue el ID de sesión de checkout como un parámetro de cadena de consulta al invocar su URL. Para hacerlo, agregue la cadena literal {CHECKOUT_SESSION_ID} a la cadena de consulta de su success_url. Stripe reemplazará este marcador de posición con el ID real de la sesión de checkout:

use Illuminate\Http\Request;
use Stripe\Checkout\Session;
use Stripe\Customer;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout(['price_tshirt' => 1], [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
]);
});
 
Route::get('/checkout-success', function (Request $request) {
$checkoutSession = $request->user()->stripe()->checkout->sessions->retrieve($request->get('session_id'));
 
return view('checkout.success', ['checkoutSession' => $checkoutSession]);
})->name('checkout-success');

Códigos de Promoción

De forma predeterminada, Stripe Checkout no permite códigos promocionales canjeables por el usuario. Afortunadamente, hay una manera fácil de habilitarlos para su página de Checkout. Para hacerlo, puede invocar el método allowPromotionCodes:

use Illuminate\Http\Request;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()
->allowPromotionCodes()
->checkout('price_tshirt');
});

Checkouts de Cargos Individuales

También puede realizar un cargo simple por un producto ad hoc que no se haya creado en su panel de control de Stripe. Para hacerlo, puede usar el método checkoutCharge en un modelo facturable y pasarle una cantidad cobrable, un nombre de producto y una cantidad opcional. Cuando un cliente visita esta ruta, será redirigido a la página de Checkout de Stripe:

use Illuminate\Http\Request;
 
Route::get('/charge-checkout', function (Request $request) {
return $request->user()->checkoutCharge(1200, 'T-Shirt', 5);
});

Advertencia Cuando uses el método checkoutCharge, Stripe siempre creará un nuevo producto y precio en tu panel de control de Stripe. Por lo tanto, recomendamos que crees los productos de antemano en tu panel de Stripe y uses el método checkout en su lugar.

Checkouts de Suscripciones

Advertencia El uso de Stripe Checkout para suscripciones requiere que habilites el webhook customer.subscription.created en tu panel de Stripe. Este webhook creará el registro de suscripción en tu base de datos y almacenará todos los elementos de suscripción relevantes.

También puede utilizar Stripe Checkout para iniciar suscripciones. Después de definir su suscripción con los métodos creadores de suscripciones de Cashier, puede llamar al método checkout. Cuando un cliente visita esta ruta, será redirigido a la página de Checkout de Stripe:

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->checkout();
});

Al igual que con los checkouts de productos, puede personalizar las URL de éxito y cancelación:

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

Por supuesto, también puede habilitar códigos promocionales para los checkouts de suscripción:

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->allowPromotionCodes()
->checkout();
});

Advertencia Desafortunadamente, Stripe Checkout no admite todas las opciones de facturación de suscripciones al iniciar suscripciones. El uso del método anchorBillingCycleOn en el generador de suscripciones, establecer el comportamiento de prorrateo o establecer el comportamiento de pago no tendrá ningún efecto durante las sesiones de Stripe Checkout. Consulta la documentación de la API de sesiones de Stripe Checkout para revisar qué parámetros están disponibles.

Stripe Checkout y Períodos de Prueba

Por supuesto, puede definir un período de prueba al construir una suscripción que se completará usando Stripe Checkout:

$checkout = Auth::user()->newSubscription('default', 'price_monthly')
->trialDays(3)
->checkout();

Sin embargo, el período de prueba debe ser de al menos 48 horas, que es el tiempo mínimo de prueba compatible con Stripe Checkout.

Suscripciones y Webhooks

Recuerda, Stripe y Cashier actualizan los estados de las suscripciones mediante webhooks, por lo que existe la posibilidad de que una suscripción aún no esté activa cuando el cliente regresa a la aplicación después de ingresar su información de pago. Para manejar este escenario, es posible que desees mostrar un mensaje informando al usuario que su pago o suscripción está pendiente.

Recolección de Identificadores Fiscales

Checkout también admite la recopilación del ID fiscal del cliente. Para habilitar esto en una sesión de checkout, invoca el método collectTaxIds al crear la sesión:

$checkout = $user->collectTaxIds()->checkout('price_tshirt');

Cuando se invoca este método, se agregará un nuevo cuadro de selección al cliente que le permite indicar si está comprando como empresa. Si es así, tendrán la oportunidad de proporcionar su número de identificación fiscal.

Advertencia Si ya has configurado la recolección automática de impuestos en el proveedor de servicios de tu aplicación, esta función se habilitará automáticamente y no será necesario invocar el método collectTaxIds.

Checkouts de Invitados

Usando el método Checkout::guest, puedes iniciar sesiones de checkout para invitados de tu aplicación que no tienen una "cuenta":

use Illuminate\Http\Request;
use Laravel\Cashier\Checkout;
 
Route::get('/product-checkout', function (Request $request) {
return Checkout::guest()->create('price_tshirt', [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

Al igual que al crear sesiones de checkout para usuarios existentes, puedes utilizar métodos adicionales disponibles en la instancia de Laravel\Cashier\CheckoutBuilder para personalizar la sesión de checkout para invitados:

use Illuminate\Http\Request;
use Laravel\Cashier\Checkout;
 
Route::get('/product-checkout', function (Request $request) {
return Checkout::guest()
->withPromotionCode('promo-code')
->create('price_tshirt', [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

Después de que se haya completado un checkout de invitado, Stripe puede enviar un evento de webhook checkout.session.completed, así que asegúrate de configurar tu webhook de Stripe para enviar realmente este evento a tu aplicación. Una vez que el webhook haya sido habilitado en el panel de Stripe, puedes manejar el webhook con Cashier. El objeto contenido en la carga útil del webhook será un objeto checkout que puedes inspeccionar para cumplir con el pedido de tu cliente.

Manejo de Pagos Fallidos

A veces, los pagos de suscripciones o cargos individuales pueden fallar. Cuando esto sucede, Cashier lanzará una excepción Laravel\Cashier\Exceptions\IncompletePayment que te informa que esto ocurrió. Después de capturar esta excepción, tienes dos opciones sobre cómo proceder.

Primero, podrías redirigir a tu cliente a la página de confirmación de pago dedicada que se incluye con Cashier. Esta página ya tiene una ruta con nombre asociado que se registra mediante el proveedor de servicios de Cashier. Entonces, puedes capturar la excepción IncompletePayment y redirigir al usuario a la página de confirmación de pago:

use Laravel\Cashier\Exceptions\IncompletePayment;
 
try {
$subscription = $user->newSubscription('default', 'price_monthly')
->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, al cliente se le pedirá que ingrese nuevamente la información de su tarjeta de crédito y realice cualquier acción adicional requerida por Stripe, como la confirmación "3D Secure". Después de confirmar su pago, el usuario será redirigido a la URL proporcionada por el parámetro redirect especificado anteriormente. Al redirigirse, se agregarán variables de cadena de consulta message (cadena) y success (entero) a la URL. La página de pago admite actualmente los siguientes tipos de métodos de pago:

  • Credit Cards
  • Alipay
  • Bancontact
  • BECS Direct Debit
  • EPS
  • Giropay
  • iDEAL
  • SEPA Direct Debit

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 los correos electrónicos automáticos de facturación de Stripe en tu panel de Stripe. Sin embargo, si se captura una excepción IncompletePayment, aún deberías informar al usuario que recibirá un correo electrónico con instrucciones adicionales de confirmación de pago.

Las excepciones de pago pueden generarse para los siguientes métodos: charge, invoiceFor e invoice en modelos que utilizan el rasgo Billable. Al interactuar con suscripciones, el método create en el generador de suscripciones y los métodos incrementAndInvoice y swapAndInvoice en los modelos Subscription y SubscriptionItem pueden generar excepciones de pago incompletas.

Determinar si una suscripción existente tiene un pago incompleto se puede lograr mediante el método hasIncompletePayment en el modelo facturable o una instancia de suscripción:

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

Puedes obtener el estado específico de un pago incompleto inspeccionando la propiedad payment en la instancia de la excepción:

use Laravel\Cashier\Exceptions\IncompletePayment;
 
try {
$user->charge(1000, 'pm_card_threeDSecure2Required');
} catch (IncompletePayment $exception) {
// Obtener el estado del intento de pago...
$exception->payment->status;
 
// Verificar condiciones específicas...
if ($exception->payment->requiresPaymentMethod()) {
// ...
} elseif ($exception->payment->requiresConfirmation()) {
// ...
}
}

Confirmación de Pagos

Algunos métodos de pago requieren datos adicionales para confirmar pagos. Por ejemplo, los métodos de pago SEPA requieren datos adicionales de "mandato" durante el proceso de pago. Puedes proporcionar estos datos a Cashier usando el método withPaymentConfirmationOptions:

$subscription->withPaymentConfirmationOptions([
'mandate_data' => '...',
])->swap('price_xxx');

Puedes consultar la documentación de la API de Stripe para revisar todas las opciones aceptadas al confirmar pagos.

Autenticación Fuerte del Cliente

Si tu negocio o uno de tus clientes está ubicado en Europa, deberás cumplir con las regulaciones de Autenticación Fuerte del Cliente (SCA, por sus siglas en inglés) de la Unión Europea. Estas regulaciones fueron impuestas en septiembre de 2019 por la Unión Europea para prevenir el fraude en los pagos. Afortunadamente, Stripe y Cashier están preparados para construir aplicaciones compatibles con SCA.

Advertencia Antes de comenzar, revisa la guía de Stripe sobre PSD2 y SCA así como su documentación sobre las nuevas API de SCA.

Pagos que Requieren Confirmación Adicional

Las regulaciones de SCA a menudo requieren verificación adicional para confirmar y procesar un pago. Cuando esto sucede, Cashier lanzará una excepción Laravel\Cashier\Exceptions\IncompletePayment que te informa que se necesita verificación adicional. Puedes encontrar más información sobre cómo manejar estas excepciones en la documentación sobre manejo de pagos fallidos.

Las pantallas de confirmación de pago presentadas por Stripe o Cashier pueden adaptarse al flujo de pago específico de un banco o emisor de tarjetas y pueden incluir confirmación adicional de la tarjeta, un cargo temporal pequeño, autenticación del dispositivo por separado u otras formas de verificación.

Estado Incompleto y Vencido

Cuando un pago necesita confirmación adicional, la suscripción permanecerá en un estado incomplete o past_due según lo indique su columna stripe_status en la base de datos. Cashier activará automáticamente la suscripción del cliente tan pronto como se complete la confirmación del pago y tu aplicación sea notificada por Stripe mediante un webhook de su finalización.

Para obtener más información sobre los estados incomplete y past_due, consulta nuestra documentación adicional sobre estos estados.

Notificaciones de Pagos Fuera de Sesión

Dado que las regulaciones de SCA requieren que los clientes verifiquen ocasionalmente sus detalles de pago incluso cuando su suscripción está activa, Cashier puede enviar una notificación al cliente cuando se requiera confirmación de pago fuera de sesión. Por ejemplo, esto puede ocurrir al renovar una suscripción. La notificación de pago de Cashier se puede habilitar configurando la variable de entorno CASHIER_PAYMENT_NOTIFICATION con una clase de notificación. Por defecto, esta notificación está desactivada. Por supuesto, 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 asegurarte de que las notificaciones de confirmación de pago fuera de sesión se entreguen, verifica que los webhooks de Stripe estén configurados para tu aplicación y que el webhook invoice.payment_action_required esté habilitado en tu panel de Stripe. Además, tu modelo Billable también debe usar el rasgo Illuminate\Notifications\Notifiable de Laravel.

Advertencia Las notificaciones se enviarán incluso cuando los clientes estén haciendo manualmente un pago que requiera confirmación adicional. Desafortunadamente, no hay forma de que Stripe sepa que el pago se hizo manualmente o "fuera de sesión". Sin embargo, un cliente simplemente verá un mensaje de "Pago exitoso" si visita la página de pago después de confirmar su pago. El cliente no podrá confirmar accidentalmente el mismo pago dos veces e incurrir en un segundo cargo accidental.

SDK de Stripe

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

$stripeSubscription = $subscription->asStripeSubscription();
 
$stripeSubscription->application_fee_percent = 5;
 
$stripeSubscription->save();

También puedes usar el método updateStripeSubscription para actualizar directamente una suscripción de Stripe:

$subscription->updateStripeSubscription(['application_fee_percent' => 5]);

Puedes invocar el método stripe en la clase Cashier si deseas usar directamente el cliente Stripe\StripeClient. Por ejemplo, podrías usar este método para acceder a la instancia de StripeClient y recuperar una lista de precios de tu cuenta de Stripe:

use Laravel\Cashier\Cashier;
 
$prices = Cashier::stripe()->prices->all();

Pruebas

Al probar una aplicación que utiliza Cashier, puedes simular las solicitudes HTTP reales al API de Stripe; sin embargo, esto requiere que vuelvas a implementar parcialmente el comportamiento propio de Cashier. Por lo tanto, recomendamos permitir que tus pruebas accedan al API de Stripe. Aunque es más lento, proporciona más confianza en que tu aplicación está funcionando según lo esperado y cualquier prueba lenta se puede colocar dentro de su propio grupo de pruebas PHPUnit.

Al realizar pruebas, recuerda que Cashier en sí ya tiene un excelente conjunto de pruebas, por lo que solo debes concentrarte en probar el flujo de suscripción y pago de tu propia aplicación y no en cada comportamiento subyacente de Cashier.

Para comenzar, agrega la versión testing de tu clave secreta de Stripe a tu archivo phpunit.xml:

<env name="STRIPE_SECRET" value="sk_test_<your-key>"/>

Ahora, cada vez que interactúas con Cashier durante las pruebas, enviará solicitudes API reales a tu entorno de prueba de Stripe. Para mayor comodidad, debes llenar previamente tu cuenta de prueba de Stripe con suscripciones/precios que puedas usar durante las pruebas.

Nota Para probar una variedad de escenarios de facturación, como denegaciones y fallos de tarjetas de crédito, puede utilizar la amplia gama de números de tarjetas de prueba y tokens proporcionados por Stripe.