- Introducción
- Actualización de Cashier
- Instalación
- Configuración
- Primeros pasos
- Clientes
- Métodos de Pago
- Suscripciones
- Pruebas de Suscripción
- Manejo de Webhooks de Stripe
- Cargos Únicos
- Checkout
- Facturas
- Manejo de Pagos Fallidos
- Autenticación Fuerte de Cliente (SCA)
- SDK de Stripe
- Pruebas
#Introducción
Laravel Cashier Stripe ofrece una interfaz expresiva y fluida para los servicios de facturación por suscripción de Stripe. Maneja casi todo el código repetitivo de facturación por suscripción que usted temía escribir. Además de la gestión básica de suscripciones, Cashier puede manejar cupones, intercambio de suscripciones, "cantidades" de suscripción, períodos de gracia para cancelaciones e incluso generar PDFs de facturas.
#Actualización de Cashier
Al actualizar a una nueva versión de Cashier, es importante que revise cuidadosamente la guía de actualización.
Para evitar cambios incompatibles, Cashier utiliza una versión fija de la API de Stripe. Cashier 15 utiliza la versión de la API de Stripe 2023-10-16. La versión de la API de Stripe se actualizará en lanzamientos menores para aprovechar nuevas funciones y mejoras de Stripe.
#Instalación
Primero, instale el paquete Cashier para Stripe usando el gestor de paquetes Composer:
composer require laravel/cashier
Después de instalar el paquete, publique las migraciones de Cashier usando el comando Artisan vendor:publish:
php artisan vendor:publish --tag="cashier-migrations"
Luego, migre su base de datos:
php artisan migrate
Las migraciones de Cashier agregarán varias columnas a su tabla users. También crearán una nueva tabla subscriptions para almacenar todas las suscripciones de sus clientes y una tabla subscription_items para suscripciones con múltiples precios.
Si lo desea, también puede publicar el archivo de configuración de Cashier usando el comando Artisan vendor:publish:
php artisan vendor:publish --tag="cashier-config"
Por último, para asegurar que Cashier maneje correctamente todos los eventos de Stripe, recuerde configurar el manejo de webhooks de Cashier.
Stripe recomienda que cualquier columna usada para almacenar identificadores de Stripe sea sensible a mayúsculas y minúsculas. Por lo tanto, debe asegurarse de que la colación de la columna stripe_id esté configurada como utf8_bin cuando use MySQL. Más información al respecto se encuentra en la documentación de Stripe.
#Configuración
#Modelo Billable
Antes de usar Cashier, agregue el trait Billable a la definición de su modelo facturable. Normalmente, este será el modelo App\Models\User. Este trait 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 viene con Laravel. Si desea cambiar esto, puede especificar un modelo diferente mediante el método useCustomerModel. Este método normalmente se debe llamar 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);
}
Si está usando un modelo distinto al modelo App\Models\User proporcionado por Laravel, deberá publicar y modificar las migraciones de Cashier para que coincidan con el nombre de la tabla de su modelo alternativo.
#Claves API
A continuación, debe configurar sus claves API de Stripe en el archivo .env de su aplicación. Puede obtener sus claves 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
Debe asegurarse de que la variable de entorno STRIPE_WEBHOOK_SECRET esté definida en el archivo .env de su aplicación, ya que esta variable se usa para garantizar que los webhooks entrantes provienen realmente de Stripe.
#Configuración de Moneda
La moneda predeterminada de Cashier es dólares estadounidenses (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 para formatear los valores monetarios que se muestran en las facturas. Internamente, Cashier utiliza la clase NumberFormatter de PHP para establecer la configuración regional de la moneda:
CASHIER_CURRENCY_LOCALE=nl_BE
Para usar configuraciones regionales distintas a en, asegúrese de que la extensión PHP ext-intl esté instalada y configurada en su servidor.
#Configuración de Impuestos
Gracias a Stripe Tax, es posible calcular automáticamente los impuestos para todas las facturas generadas por Stripe. Puede 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 su aplicación:
use Laravel\Cashier\Cashier;
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Cashier::calculateTaxes();
}
Una vez habilitado el cálculo de impuestos, cualquier nueva suscripción y cualquier factura puntual que se genere recibirá el cálculo automático de impuestos.
Para que esta función funcione correctamente, los datos de facturación de su cliente, como nombre, dirección e identificación fiscal, deben sincronizarse con Stripe. Puede usar los métodos de sincronización de datos de clientes y Identificación Fiscal que ofrece Cashier para lograr esto.
No se calculan impuestos para cargos únicos ni para checkouts de cargo único.
#Registro de Logs
Cashier le permite especificar el canal de logs que se usará para registrar errores fatales de Stripe. Puede especificar el canal de logs definiendo la variable de entorno CASHIER_LOGGER en el archivo .env de su aplicación:
CASHIER_LOGGER=stack
Las excepciones generadas por llamadas a la API de Stripe se registrarán a través del canal de logs predeterminado de su aplicación.
#Uso de Modelos Personalizados
Puede extender libremente los modelos que Cashier usa internamente definiendo su propio modelo y extendiendo el modelo correspondiente de Cashier:
use Laravel\Cashier\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
Después de definir su modelo, puede indicarle a Cashier que use su modelo personalizado mediante la clase Laravel\Cashier\Cashier. Normalmente, debe informar a Cashier sobre sus modelos personalizados en el método boot de la clase App\Providers\AppServiceProvider de su aplicación:
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);
}
#Primeros pasos
#Venta de Productos
Antes de utilizar Stripe Checkout, debe definir Productos con precios fijos en su panel de Stripe. Además, debe configurar el manejo de webhooks de Cashier.
Ofrecer facturación de productos y suscripciones a través de su aplicación puede ser intimidante. Sin embargo, gracias a Cashier y Stripe Checkout, puede construir fácilmente integraciones de pago modernas y robustas.
Para cobrar a los clientes por productos no recurrentes y de cargo único, utilizaremos Cashier para dirigir a los clientes a Stripe Checkout, donde proporcionarán sus datos de pago y confirmarán su compra. Una vez realizado el pago a través de Checkout, el cliente será redirigido a una URL de éxito que usted elija dentro de su aplicación:
use Illuminate\Http\Request;
Route::get('/checkout', function (Request $request) {
$stripePriceId = 'price_deluxe_album';
$quantity = 1;
return $request->user()->checkout([$stripePriceId => $quantity], [
'success_url' => route('checkout-success'),
'cancel_url' => route('checkout-cancel'),
]);
})->name('checkout');
Route::view('checkout.success')->name('checkout-success');
Route::view('checkout.cancel')->name('checkout-cancel');
Como puede ver en el ejemplo anterior, utilizaremos el método checkout proporcionado por Cashier para redirigir al cliente a Stripe Checkout para un "identificador de precio" dado. Al usar Stripe, "precios" se refiere a precios definidos para productos específicos.
Si es necesario, el método checkout creará automáticamente un cliente en Stripe y conectará ese registro de cliente de Stripe con el usuario correspondiente en la base de datos de su aplicación. Después de completar la sesión de checkout, el cliente será redirigido a una página dedicada de éxito o cancelación donde puede mostrar un mensaje informativo al cliente.
#Proporcionar Meta Data a Stripe Checkout
Al vender productos, es común llevar un registro de los pedidos completados y productos comprados mediante modelos Cart y Order definidos por su propia aplicación. Al redirigir a los clientes a Stripe Checkout para completar una compra, puede necesitar proporcionar un identificador de pedido existente para asociar la compra completada con el pedido correspondiente cuando el cliente regrese a su aplicación.
Para lograr esto, puede proporcionar un arreglo de metadata al método checkout. Imaginemos que se crea un Order pendiente dentro de nuestra aplicación cuando un usuario inicia el proceso de checkout. Recuerde, los modelos Cart y Order en este ejemplo son ilustrativos y no son proporcionados por Cashier. Usted es libre de implementar estos conceptos según las necesidades de su propia aplicación:
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
$order = Order::create([
'cart_id' => $cart->id,
'price_ids' => $cart->price_ids,
'status' => 'incomplete',
]);
return $request->user()->checkout($order->price_ids, [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
'metadata' => ['order_id' => $order->id],
]);
})->name('checkout');
Como puede ver en el ejemplo anterior, cuando un usuario inicia el proceso de checkout, proporcionamos todos los identificadores de precio de Stripe asociados al carrito / pedido al método checkout. Por supuesto, su aplicación es responsable de asociar estos ítems con el "carrito de compras" o pedido a medida que el cliente los agrega. También proporcionamos el ID del pedido a la sesión de Stripe Checkout mediante el arreglo metadata. Finalmente, hemos agregado la variable plantilla CHECKOUT_SESSION_ID a la ruta de éxito de Checkout. Cuando Stripe redirige a los clientes de vuelta a su aplicación, esta variable plantilla se llenará automáticamente con el ID de la sesión de Checkout.
A continuación, construyamos la ruta de éxito de Checkout. Esta es la ruta a la que los usuarios serán redirigidos después de que su compra se haya completado mediante Stripe Checkout. Dentro de esta ruta, podemos recuperar el ID de la sesión de Stripe Checkout y la instancia asociada para acceder a los metadatos proporcionados y actualizar el pedido de nuestro cliente en consecuencia:
use App\Models\Order;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
Route::get('/checkout/success', function (Request $request) {
$sessionId = $request->get('session_id');
if ($sessionId === null) {
return;
}
$session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);
if ($session->payment_status !== 'paid') {
return;
}
$orderId = $session['metadata']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
return view('checkout-success', ['order' => $order]);
})->name('checkout-success');
Consulte la documentación de Stripe para más información sobre los datos contenidos en el objeto de sesión de Checkout.
#Venta de Suscripciones
Antes de utilizar Stripe Checkout, debe definir Productos con precios fijos en su panel de Stripe. Además, debe configurar el manejo de webhooks de Cashier.
Ofrecer facturación de productos y suscripciones a través de su aplicación puede ser intimidante. Sin embargo, gracias a Cashier y Stripe Checkout, puede construir fácilmente integraciones de pago modernas y robustas.
Para aprender cómo vender suscripciones usando Cashier y Stripe Checkout, consideremos el escenario simple de un servicio de suscripción con un plan básico mensual (price_basic_monthly) y anual (price_basic_yearly). Estos dos precios podrían agruparse bajo un producto "Basic" (pro_basic) en nuestro panel de Stripe. Además, nuestro servicio de suscripción podría ofrecer un plan Expert como pro_expert.
Primero, veamos cómo un cliente puede suscribirse a nuestros servicios. Por supuesto, puede imaginar que el cliente haga clic en un botón "suscribirse" para el plan Basic en la página de precios de nuestra aplicación. Este botón o enlace debe dirigir al usuario a una ruta de Laravel que cree la sesión de Stripe Checkout para el plan elegido:
use Illuminate\Http\Request;
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_basic_monthly')
->trialDays(5)
->allowPromotionCodes()
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});
Como puede ver en el ejemplo anterior, redirigiremos al cliente a una sesión de Stripe Checkout que le permitirá suscribirse a nuestro plan Basic. Después de un checkout exitoso o una cancelación, el cliente será redirigido a la URL que proporcionamos al método checkout. Para saber cuándo su suscripción realmente ha comenzado (ya que algunos métodos de pago requieren unos segundos para procesarse), también necesitaremos configurar el manejo de webhooks de Cashier.
Ahora que los clientes pueden iniciar suscripciones, necesitamos restringir ciertas partes de nuestra aplicación para que solo los usuarios suscritos puedan acceder a ellas. Por supuesto, siempre podemos determinar el estado actual de la suscripción de un usuario mediante el método subscribed proporcionado por el trait Billable de Cashier:
@if ($user->subscribed())
<p>You are subscribed.</p>
@endif
Incluso podemos determinar fácilmente si un usuario está suscrito a un producto o precio específico:
@if ($user->subscribedToProduct('pro_basic'))
<p>You are subscribed to our Basic product.</p>
@endif
@if ($user->subscribedToPrice('price_basic_monthly'))
<p>You are subscribed to our monthly Basic plan.</p>
@endif
#Construyendo un Middleware para Suscritos
Para mayor comodidad, puede crear un middleware que determine si la solicitud entrante proviene de un usuario suscrito. Una vez definido este middleware, puede asignarlo fácilmente a una ruta para evitar que usuarios no suscritos accedan a ella:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Subscribed
{
/**
* Manejar una solicitud entrante.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirigir al usuario a la página de facturación y pedirle que se suscriba...
return redirect('/billing');
}
return $next($request);
}
}
Una vez definido el middleware, puede asignarlo a una ruta:
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
#Permitir a los Clientes Gestionar su Plan de Facturación
Por supuesto, los clientes pueden querer cambiar su plan de suscripción a otro producto o "nivel". La forma más sencilla de permitir esto es dirigiendo a los clientes al Portal de Facturación para Clientes de Stripe, que ofrece una interfaz alojada que permite a los clientes descargar facturas, actualizar su método de pago y cambiar planes de suscripción.
Primero, defina un enlace o botón dentro de su aplicación que dirija a los usuarios a una ruta de Laravel que utilizaremos para iniciar una sesión en el Portal de Facturación:
<a href="{{ route('billing') }}">
Billing
</a>
A continuación, definamos la ruta que inicia una sesión en el Portal de Facturación de Stripe y redirige al usuario al Portal. El método redirectToBillingPortal acepta la URL a la que los usuarios deben regresar al salir del Portal:
use Illuminate\Http\Request;
Route::get('/billing', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('dashboard'));
})->middleware(['auth'])->name('billing');
Mientras haya configurado el manejo de webhooks de Cashier, Cashier mantendrá automáticamente sincronizadas las tablas de base de datos relacionadas con Cashier inspeccionando los webhooks entrantes de Stripe. Por ejemplo, cuando un usuario cancela su suscripción a través del Portal de Facturación para Clientes de Stripe, Cashier recibirá el webhook correspondiente y marcará la suscripción como "cancelada" en la base de datos de su aplicación.
#Clientes
#Recuperar Clientes
Puede recuperar un cliente por su ID de Stripe usando el método Cashier::findBillable. Este método devolverá una instancia del modelo facturable:
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
#Crear Clientes
Ocasionalmente, puede que desee crear un cliente en Stripe sin iniciar una suscripción. Puede lograr esto usando el método createAsStripeCustomer:
$stripeCustomer = $user->createAsStripeCustomer();
Una vez creado el cliente en Stripe, puede iniciar una suscripción en una fecha posterior. Puede proporcionar un arreglo opcional $options para pasar cualquier parámetro adicional de creación de cliente soportado por la API de Stripe:
$stripeCustomer = $user->createAsStripeCustomer($options);
Puede usar el método asStripeCustomer si desea obtener el objeto cliente de Stripe para un modelo facturable:
$stripeCustomer = $user->asStripeCustomer();
El método createOrGetStripeCustomer puede usarse si desea obtener el objeto cliente de Stripe para un modelo facturable dado pero no está seguro si el modelo ya es un cliente en Stripe. Este método creará un nuevo cliente en Stripe si no existe uno:
$stripeCustomer = $user->createOrGetStripeCustomer();
#Actualizar Clientes
Ocasionalmente, puede que desee actualizar directamente el cliente de Stripe con información adicional. Puede lograrlo utilizando el método updateStripeCustomer. Este método acepta un array de opciones de actualización del cliente compatibles con la API de Stripe:
$stripeCustomer = $user->updateStripeCustomer($options);
#Saldos
Stripe le permite acreditar o debitar el "saldo" de un cliente. Más adelante, este saldo se acreditará o debitará en nuevas facturas. Para verificar el saldo total del cliente puede usar el método balance disponible en su modelo facturable. El método balance devolverá una representación en cadena formateada del saldo en la moneda del cliente:
$balance = $user->balance();
Para acreditar el saldo de un cliente, puede proporcionar un valor al método creditBalance. Si lo desea, también puede proporcionar una descripción:
$user->creditBalance(500, 'Recarga para cliente premium.');
Proporcionar un valor al método debitBalance debitará el saldo del cliente:
$user->debitBalance(300, 'Penalización por mal uso.');
El método applyBalance creará nuevas transacciones de saldo para el cliente. Puede recuperar estos registros de transacciones usando el método balanceTransactions, lo cual puede ser útil para proporcionar un registro de créditos y débitos para que el cliente lo revise:
// Recuperar todas las transacciones...
$transactions = $user->balanceTransactions();
foreach ($transactions as $transaction) {
// Monto de la transacción...
$amount = $transaction->amount(); // $2.31
// Recuperar la factura relacionada cuando esté disponible...
$invoice = $transaction->invoice();
}
#Identificaciones Fiscales
Cashier ofrece una forma sencilla de gestionar las identificaciones fiscales de un cliente. Por ejemplo, el método taxIds puede usarse para recuperar todas las identificaciones fiscales asignadas a un cliente como una colección:
$taxIds = $user->taxIds();
También puede recuperar una identificación fiscal específica para un cliente por su identificador:
$taxId = $user->findTaxId('txi_belgium');
Puede crear un nuevo Tax ID proporcionando un tipo válido y un valor 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 IDs de IVA también la realiza Stripe; sin embargo, este es un proceso asíncrono. Puede recibir notificaciones de las actualizaciones de verificación suscribiéndose al evento webhook customer.tax_id.updated e inspeccionando el parámetro verification de los IDs de IVA. Para más información sobre el manejo de webhooks, consulte la documentación sobre cómo definir manejadores de webhook.
Puede eliminar un Tax ID usando el método deleteTaxId:
$user->deleteTaxId('txi_belgium');
#Sincronización de datos del cliente con Stripe
Normalmente, cuando los usuarios de su aplicación actualizan su nombre, dirección de correo electrónico u otra información que también se almacena en Stripe, debe informar a Stripe sobre las actualizaciones. De esta manera, la copia de la información en Stripe estará sincronizada con la de su aplicación.
Para automatizar esto, puede definir un manejador de eventos en su modelo facturable que reaccione al evento updated del modelo. Luego, dentro de su manejador de eventos, puede 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 actualice el modelo del cliente, su información se sincronizará con Stripe. Para mayor comodidad, Cashier sincronizará automáticamente la información del cliente con Stripe al crear el cliente por primera vez.
Puede personalizar las columnas usadas para sincronizar la información del cliente con Stripe sobrescribiendo varios métodos proporcionados por Cashier. Por ejemplo, puede sobrescribir el método stripeName para personalizar el atributo que debe considerarse como el "nombre" del cliente cuando Cashier sincroniza la información con Stripe:
/**
* Obtener el nombre del cliente que debe sincronizarse con Stripe.
*/
public function stripeName(): string|null
{
return $this->company_name;
}
De manera similar, puede sobrescribir los métodos stripeEmail, stripePhone, stripeAddress y stripePreferredLocales. Estos métodos sincronizarán la información con sus parámetros correspondientes del cliente cuando se actualice el objeto cliente en Stripe. Si desea tener control total sobre el proceso de sincronización de la información del cliente, puede sobrescribir el método syncStripeCustomerDetails.
#Portal de facturación
Stripe ofrece una forma sencilla de configurar un portal de facturación para que su cliente pueda gestionar su suscripción, métodos de pago y ver su historial de facturación. Puede redirigir a sus usuarios al portal de facturación invocando el método redirectToBillingPortal en el modelo facturable desde un controlador o ruta:
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});
Por defecto, cuando el usuario termine de gestionar su suscripción, podrá regresar a la ruta home de su aplicación mediante un enlace dentro del portal de facturación de Stripe. Puede 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 desea generar la URL al portal de facturación sin generar una respuesta HTTP de redirección, puede 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 "únicos" con Stripe, necesitará almacenar un método de pago y obtener su identificador desde Stripe. El enfoque para lograr esto varía según si planea usar el método de pago para suscripciones o cargos únicos, por lo que examinaremos ambos casos a continuación.
#Métodos de pago para suscripciones
Cuando almacena la información de la tarjeta de crédito de un cliente para uso futuro en una suscripción, debe usar la API de "Setup Intents" de Stripe para recopilar de forma segura los detalles del método de pago del cliente. Un "Setup Intent" indica a Stripe la intención de cobrar un método de pago del cliente. El trait Billable de Cashier incluye el método createSetupIntent para crear fácilmente un nuevo Setup Intent. Debe invocar este método desde la ruta o controlador que renderice el formulario que recopila los detalles del método de pago de su cliente:
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
]);
Después de crear el Setup Intent y pasarlo a la vista, debe adjuntar su secreto al elemento que recopilará el método de pago. Por ejemplo, considere este formulario para "actualizar método de pago":
<input id="card-holder-name" type="text">
<!-- Marcador de posición para Stripe Elements -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
A continuación, puede usar la biblioteca Stripe.js para adjuntar un Stripe Element al formulario y recopilar de forma segura los detalles de pago del cliente:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Luego, la tarjeta puede ser verificada y se puede obtener un identificador seguro de "método de pago" desde Stripe usando el método confirmCardSetup de Stripe:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Mostrar "error.message" al usuario...
} else {
// La tarjeta ha sido verificada con éxito...
}
});
Después de que Stripe haya verificado la tarjeta, puede pasar el identificador setupIntent.payment_method resultante a su aplicación Laravel, donde puede adjuntarse al cliente. El método de pago puede agregarse como un nuevo método de pago o usarse para actualizar el método de pago predeterminado. También puede usar inmediatamente el identificador del método de pago para crear una nueva suscripción.
Si desea más información sobre Setup Intents y la recopilación de detalles de pago del cliente, por favor revise esta visión general proporcionada por Stripe.
#Métodos de pago para cargos únicos
Por supuesto, al realizar un cargo único contra el método de pago de un cliente, solo necesitaremos usar un identificador de método de pago una vez. Debido a limitaciones de Stripe, no puede usar el método de pago predeterminado almacenado de un cliente para cargos únicos. Debe permitir que el cliente ingrese los detalles de su método de pago usando la biblioteca Stripe.js. Por ejemplo, considere el siguiente formulario:
<input id="card-holder-name" type="text">
<!-- Marcador de posición de Stripe Elements -->
<div id="card-element"></div>
<button id="card-button">
Process Payment
</button>
Después de definir dicho formulario, puede usar la biblioteca Stripe.js para adjuntar un Stripe Element al formulario y recopilar de forma segura los detalles de pago del cliente:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Luego, la tarjeta puede ser verificada y se puede obtener un identificador seguro de "método de pago" desde Stripe usando el método createPaymentMethod de Stripe:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
// Mostrar "error.message" al usuario...
} else {
// La tarjeta ha sido verificada con éxito...
}
});
Si la tarjeta se verifica correctamente, puede pasar el paymentMethod.id a su aplicación Laravel y procesar un cargo único.
#Recuperar métodos de pago
El método paymentMethods en la instancia del modelo facturable devuelve una colección de instancias Laravel\Cashier\PaymentMethod:
$paymentMethods = $user->paymentMethods();
Por defecto, este método devolverá métodos de pago de todos los tipos. Para recuperar métodos de pago de un tipo específico, puede pasar el type como argumento al método:
$paymentMethods = $user->paymentMethods('sepa_debit');
Para recuperar el método de pago predeterminado del cliente, puede usar el método defaultPaymentMethod:
$paymentMethod = $user->defaultPaymentMethod();
Puede recuperar un método de pago específico que esté adjunto al modelo facturable usando el método findPaymentMethod:
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
#Presencia de método de pago
Para determinar si un modelo facturable tiene un método de pago predeterminado adjunto a su cuenta, invoque el método hasDefaultPaymentMethod:
if ($user->hasDefaultPaymentMethod()) {
// ...
}
Puede 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 algún método de pago en absoluto. Para determinar si existe un método de pago de un tipo específico para el modelo, puede pasar el type como argumento al método:
if ($user->hasPaymentMethod('sepa_debit')) {
// ...
}
#Actualizar el método de pago predeterminado
El método updateDefaultPaymentMethod puede usarse 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 facturación:
$user->updateDefaultPaymentMethod($paymentMethod);
Para sincronizar la información de su método de pago predeterminado con la información del método de pago predeterminado del cliente en Stripe, puede usar el método updateDefaultPaymentMethodFromStripe:
$user->updateDefaultPaymentMethodFromStripe();
El método de pago predeterminado en un cliente solo puede usarse para facturación y creación de nuevas suscripciones. Debido a limitaciones impuestas por Stripe, no puede usarse para cargos únicos.
#Agregar métodos de pago
Para agregar un nuevo método de pago, puede llamar al método addPaymentMethod en el modelo facturable, pasando el identificador del método de pago:
$user->addPaymentMethod($paymentMethod);
Para aprender cómo recuperar identificadores de métodos de pago, por favor revise la documentación sobre almacenamiento de métodos de pago.
#Eliminar métodos de pago
Para eliminar un método de pago, puede llamar al método delete en la instancia Laravel\Cashier\PaymentMethod que desea 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 de métodos de pago para el modelo facturable:
$user->deletePaymentMethods();
Por defecto, este método eliminará métodos de pago de todos los tipos. Para eliminar métodos de pago de un tipo específico, puede pasar el type como argumento al método:
$user->deletePaymentMethods('sepa_debit');
Si un usuario tiene una suscripción activa, su aplicación no debería permitirle eliminar su método de pago predeterminado.
#Suscripciones
Las suscripciones proporcionan una forma de configurar pagos recurrentes para sus clientes. Las suscripciones de Stripe gestionadas por Cashier ofrecen soporte para múltiples precios de suscripción, cantidades, períodos de prueba y más.
#Crear suscripciones
Para crear una suscripción, primero recupere una instancia de su modelo facturable, que normalmente será una instancia de App\Models\User. Una vez que tenga la instancia del modelo, puede 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 debe ser el tipo interno de la suscripción. Si su aplicación solo ofrece una suscripción, podría llamarla default o primary. Este tipo de suscripción es solo para uso interno de la aplicación y no debe 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 u objeto PaymentMethod de Stripe, iniciará la suscripción y actualizará su base de datos con el ID de cliente de Stripe del modelo facturable y otra información relevante de facturación.
Pasar un identificador de método de pago directamente al método create de suscripción también lo añadirá automáticamente a los métodos de pago almacenados del usuario.
#Cobro de pagos recurrentes mediante correos electrónicos de factura
En lugar de cobrar automáticamente los pagos recurrentes de un cliente, puede indicar a Stripe que envíe una factura por correo electrónico al cliente cada vez que su pago recurrente sea debido. 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 cuando se cobran pagos recurrentes mediante 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 se determina mediante la opción days_until_due. Por defecto, son 30 días; sin embargo, puede proporcionar un valor específico para esta opción si lo desea:
$user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [
'days_until_due' => 30
]);
#Cantidades
Si desea establecer una cantidad específica para el precio al crear la suscripción, debe invocar el método quantity en el constructor de la suscripción antes de crearla:
$user->newSubscription('default', 'price_monthly')
->quantity(5)
->create($paymentMethod);
#Detalles adicionales
Si desea especificar opciones adicionales de cliente o suscripción soportadas por Stripe, puede hacerlo pasando estas opciones como segundo y tercer argumento al método create:
$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
], [
'metadata' => ['note' => 'Alguna información extra.'],
]);
#Cupones
Si desea aplicar un cupón al crear la suscripción, puede usar el método withCoupon:
$user->newSubscription('default', 'price_monthly')
->withCoupon('code')
->create($paymentMethod);
O, si desea aplicar un código de promoción de Stripe, puede 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 visible para el cliente. Si necesita encontrar un ID de código de promoción basado en un código visible para el cliente, puede 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 subyacente Stripe\PromotionCode. Puede recuperar el cupón relacionado con el código de promoción invocando el método coupon:
$coupon = $user->findPromotionCode('SUMMERSALE')->coupon();
La instancia del cupón le permite determinar el monto del descuento y si el cupón representa un descuento fijo o un descuento basado en porcentaje:
if ($coupon->isPercentage()) {
return $coupon->percentOff().'%'; // 21.5%
} else {
return $coupon->amountOff(); // $5.99
}
También puede recuperar los descuentos que están actualmente aplicados a un cliente o suscripción:
$discount = $billable->discount();
$discount = $subscription->discount();
Las instancias Laravel\Cashier\Discount devueltas decoran un objeto subyacente Stripe\Discount. Puede recuperar el cupón relacionado con este descuento invocando el método coupon:
$coupon = $subscription->discount()->coupon();
Si desea aplicar un nuevo cupón o código de promoción a un cliente o suscripción, puede hacerlo mediante los métodos applyCoupon o applyPromotionCode:
$billable->applyCoupon('coupon_id');
$billable->applyPromotionCode('promotion_code_id');
$subscription->applyCoupon('coupon_id');
$subscription->applyPromotionCode('promotion_code_id');
Recuerde, debe usar el ID de la API de Stripe asignado al código de promoción y no el código visible para el cliente. Solo se puede aplicar un cupón o código de promoción a un cliente o suscripción en un momento dado.
Para más información sobre este tema, consulte la documentación de Stripe sobre cupones y códigos de promoción.
#Agregar suscripciones
Si desea agregar una suscripción a un cliente que ya tiene un método de pago predeterminado, puede invocar el método add en el constructor de la suscripción:
use App\Models\User;
$user = User::find(1);
$user->newSubscription('default', 'price_monthly')->add();
#Crear suscripciones desde el panel de Stripe
También puede crear suscripciones desde el panel de Stripe. Al hacerlo, Cashier sincronizará las suscripciones recién agregadas y les asignará un tipo default. Para personalizar el tipo de suscripción asignado a las suscripciones creadas desde el panel, defina manejadores de eventos webhook.
Además, solo puede crear un tipo de suscripción a través del panel de Stripe. Si su aplicación ofrece múltiples suscripciones que usan diferentes tipos, solo se podrá agregar un tipo de suscripción mediante el panel de Stripe.
Finalmente, siempre debe asegurarse de agregar solo una suscripción activa por tipo de suscripción que ofrece su aplicación. Si un cliente tiene dos suscripciones default, solo la suscripción agregada más recientemente será usada por Cashier, aunque ambas se sincronizarán con la base de datos de su aplicación.
#Verificar el estado de la suscripción
Una vez que un cliente está suscrito a su aplicación, puede verificar fácilmente el estado de su suscripción usando varios métodos convenientes. Primero, 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 tipo de suscripción como primer argumento:
if ($user->subscribed('default')) {
// ...
}
El método subscribed también es un buen candidato para un middleware de ruta, permitiéndole 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 que paga...
return redirect('billing');
}
return $next($request);
}
}
Si desea determinar si un usuario aún está dentro de su período de prueba, puede usar el método onTrial. Este método puede ser útil para decidir si debe mostrar una advertencia al usuario indicando que todavía está en su período de prueba:
if ($user->subscription('default')->onTrial()) {
// ...
}
El método subscribedToProduct puede usarse para determinar si el usuario está suscrito a un producto dado basado en el identificador de un producto de Stripe. En Stripe, los productos son colecciones de precios. En este ejemplo, determinaremos si la suscripción default del usuario está activamente suscrita al producto "premium" de la aplicación. El identificador del producto de Stripe dado debe corresponder a uno de los identificadores de sus productos en el panel de Stripe:
if ($user->subscribedToProduct('prod_premium', 'default')) {
// ...
}
Pasando un arreglo al método subscribedToProduct, puede determinar si la suscripción default del usuario está activamente suscrita a los productos "basic" o "premium" de la aplicación:
if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {
// ...
}
El método subscribedToPrice puede usarse 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 puede usarse para determinar si el usuario está actualmente suscrito y ya no está dentro de su período de prueba:
if ($user->subscription('default')->recurring()) {
// ...
}
Si un usuario tiene dos suscripciones con el mismo tipo, 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 tipo default; sin embargo, una de las suscripciones puede ser antigua y expirada, mientras que la otra es la suscripción actual y activa. Siempre se devolverá la suscripción más reciente mientras que las suscripciones antiguas se mantienen en la base de datos para revisión histórica.
#Estado de suscripción cancelada
Para determinar si el usuario fue alguna vez un suscriptor activo pero ha cancelado su suscripción, puede usar el método canceled:
if ($user->subscription('default')->canceled()) {
// ...
}
También puede 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 expirar el 10 de marzo, el usuario estará en su "período de gracia" hasta el 10 de marzo. Tenga en cuenta que el método subscribed aún devuelve true durante este tiempo:
if ($user->subscription('default')->onGracePeriod()) {
// ...
}
Para determinar si el usuario ha cancelado su suscripción y ya no está dentro de su "período de gracia", puede 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 la creación, la suscripción se marcará como incomplete. Los estados de suscripción se almacenan en la columna stripe_status de la tabla subscriptions de la base de datos de Cashier.
De manera similar, si se requiere una acción de pago secundaria al cambiar precios, la suscripción se marcará como past_due. Cuando su suscripción está en cualquiera de estos estados, no estará activa hasta que el cliente haya confirmado su pago. Puede 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, debe dirigir al usuario a la página de confirmación de pago de Cashier, pasando el identificador latestPayment. Puede usar el método latestPayment disponible en la instancia de suscripción para obtener este identificador:
<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">
Please confirm your payment.
</a>
Si desea que la suscripción siga considerándose activa cuando está en estado past_due o incomplete, puede usar los métodos keepPastDueSubscriptionsActive y keepIncompleteSubscriptionsActive proporcionados por Cashier. Normalmente, estos métodos deben llamarse en el método register de su App\Providers\AppServiceProvider:
use Laravel\Cashier\Cashier;
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
Cashier::keepIncompleteSubscriptionsActive();
}
Cuando una suscripción está en estado incomplete, no puede modificarse hasta que el pago sea confirmado. Por lo tanto, los métodos swap y updateQuantity lanzarán una excepción cuando la suscripción esté en estado incomplete.
#Alcances de Suscripción
La mayoría de los estados de suscripción también están disponibles como scopes de consulta para que pueda consultar fácilmente su base de datos por suscripciones que estén en un estado determinado:
// Obtener todas las suscripciones activas...
$subscriptions = Subscription::query()->active()->get();
// Obtener todas las suscripciones canceladas de un usuario...
$subscriptions = $user->subscriptions()->canceled()->get();
Una lista completa de los scopes disponibles está disponible a continuación:
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();
#Cambiando Precios
Después de que un cliente se suscribe a su aplicación, ocasionalmente puede querer cambiar a un nuevo precio de suscripción. Para cambiar a un nuevo precio, pase el identificador del precio de Stripe al método swap. Al cambiar precios, se asume que el usuario desea reactivar su suscripción si fue cancelada previamente. El identificador del 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, el período de prueba se mantendrá. Además, si existe una "cantidad" para la suscripción, esa cantidad también se mantendrá.
Si desea cambiar precios y cancelar cualquier período de prueba en el que el cliente esté actualmente, puede invocar el método skipTrial:
$user->subscription('default')
->skipTrial()
->swap('price_yearly');
Si desea cambiar precios y facturar inmediatamente al cliente en lugar de esperar a su próximo ciclo de facturación, puede usar el método swapAndInvoice:
$user = User::find(1);
$user->subscription('default')->swapAndInvoice('price_yearly');
#Prorrateos
Por defecto, Stripe prorratea los cargos al cambiar entre precios. El método noProrate puede usarse para actualizar el precio de la suscripción sin prorratear los cargos:
$user->subscription('default')->noProrate()->swap('price_yearly');
Para más información sobre prorrateos en suscripciones, consulte la documentación de Stripe.
Ejecutar el método noProrate antes del método swapAndInvoice no tendrá efecto sobre el prorrateo. Siempre se emitirá una factura.
#Cantidad en Suscripciones
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. Puede usar los métodos incrementQuantity y decrementQuantity para incrementar o decrementar fácilmente la cantidad de su suscripción:
use App\Models\User;
$user = User::find(1);
$user->subscription('default')->incrementQuantity();
// Añadir cinco a la cantidad actual de la suscripción...
$user->subscription('default')->incrementQuantity(5);
$user->subscription('default')->decrementQuantity();
// Restar cinco a la cantidad actual de la suscripción...
$user->subscription('default')->decrementQuantity(5);
Alternativamente, puede establecer una cantidad específica usando el método updateQuantity:
$user->subscription('default')->updateQuantity(10);
El método noProrate puede usarse para actualizar la cantidad de la suscripción sin prorratear los cargos:
$user->subscription('default')->noProrate()->updateQuantity(10);
Para más información sobre cantidades en suscripciones, consulte la documentación de Stripe.
#Cantidades para Suscripciones con Múltiples Productos
Si su suscripción es una suscripción con múltiples productos, debe pasar el ID del precio cuya cantidad desea 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
Las suscripciones con múltiples productos le permiten asignar múltiples productos facturables a una sola suscripción. Por ejemplo, imagine que está construyendo una aplicación de servicio al cliente "helpdesk" que tiene un precio base de suscripción de $10 por mes pero ofrece un producto adicional de chat en vivo por $15 adicionales al mes. La información para suscripciones con múltiples productos se almacena en la tabla subscription_items de la base de datos de Cashier.
Puede especificar múltiples productos para una suscripción dada pasando un arreglo 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 asociados a su suscripción default. Ambos precios se cobrarán en sus respectivos intervalos de facturación. Si es necesario, puede 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 el cliente será facturado por él en su próximo ciclo de facturación. Si desea facturar al cliente inmediatamente, 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 las suscripciones usando el método removePrice:
$user->subscription('default')->removePrice('price_chat');
No puede eliminar el último precio de una suscripción. En su lugar, debe cancelar la suscripción.
#Cambiando Precios
También puede cambiar los precios asociados 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 ítem de suscripción subyacente con price_basic se elimina y el que tiene price_chat se conserva. Además, se crea un nuevo ítem de suscripción para price_pro.
También puede especificar opciones para los ítems de suscripción pasando un arreglo de pares clave / valor al método swap. Por ejemplo, puede necesitar especificar las cantidades de los 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 ítem de suscripción mismo. Este enfoque es especialmente ú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
Por defecto, Stripe prorratea los cargos al agregar o eliminar precios de una suscripción con múltiples productos. Si desea hacer un ajuste de precio sin prorrateo, debe encadenar el método noProrate a 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 usando los métodos de cantidad existentes pasando el ID del precio como 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');
Cuando una suscripción tiene múltiples precios, los atributos stripe_price y quantity en el modelo Subscription serán null. Para acceder a los atributos de precios individuales, debe usar la relación items disponible en el modelo Subscription.
#Ítems de Suscripción
Cuando una suscripción tiene múltiples precios, tendrá múltiples "ítems" de suscripción almacenados en la tabla subscription_items de su base de datos. Puede acceder a estos 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 de Stripe y la cantidad para un ítem específico...
$stripePrice = $subscriptionItem->stripe_price;
$quantity = $subscriptionItem->quantity;
También puede obtener un precio específico usando el método findItemOrFail:
$user = User::find(1);
$subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat');
#Múltiples Suscripciones
Stripe permite que sus clientes tengan múltiples suscripciones simultáneamente. Por ejemplo, puede administrar un gimnasio que ofrece una suscripción de natación y una de 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 tipo de suscripción al método newSubscription. El tipo 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, puede que desee cambiar a una suscripción anual más adelante. Al ajustar la suscripción del cliente, simplemente podemos cambiar el precio en la suscripción swimming:
$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 su uso del producto 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ían por mes.
Para comenzar a usar la facturación medida, primero debe crear un nuevo producto en su panel de Stripe con un precio medido. Luego, use el método meteredPrice para agregar el ID del precio medido a una suscripción de 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,
]);
#Reportando Uso
A medida que su cliente usa su aplicación, reportará su uso a Stripe para que pueda ser facturado con precisión. Para incrementar el uso de una suscripción medida, puede usar el método reportUsage:
$user = User::find(1);
$user->subscription('default')->reportUsage();
Por defecto, 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 añadir 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 múltiples precios en una sola suscripción, deberá usar el método reportUsageFor para especificar el precio medido para el que desea reportar uso:
$user = User::find(1);
$user->subscription('default')->reportUsageFor('price_metered', 15);
A veces, puede necesitar actualizar el uso que ha reportado previamente. Para lograr esto, puede pasar una marca de tiempo o una instancia de DateTimeInterface como segundo parámetro a reportUsage. Al hacerlo, Stripe actualizará el uso que fue reportado en ese momento dado. Puede continuar actualizando registros de uso anteriores mientras la fecha y hora dada aún estén dentro del período de facturación actual:
$user = User::find(1);
$user->subscription('default')->reportUsage(5, $timestamp);
#Recuperando Registros de Uso
Para recuperar el uso pasado de un cliente, puede usar el método usageRecords de una instancia de suscripción:
$user = User::find(1);
$usageRecords = $user->subscription('default')->usageRecords();
Si su aplicación ofrece múltiples precios en una sola suscripción, puede usar el método usageRecordsFor para especificar el precio medido para el que 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 arreglo asociativo de registros de uso. Puede iterar sobre este arreglo para mostrar el uso total de un cliente:
@foreach ($usageRecords as $usageRecord)
- Periodo Inicio: {{ $usageRecord['period']['start'] }}
- Periodo Fin: {{ $usageRecord['period']['end'] }}
- Uso Total: {{ $usageRecord['total_usage'] }}
@endforeach
Para una referencia completa de todos los datos de uso devueltos y cómo usar la paginación basada en cursor de Stripe, consulte la documentación oficial de la API de Stripe.
#Impuestos en Suscripciones
En lugar de calcular manualmente las tasas de impuestos, puede calcular impuestos automáticamente usando Stripe Tax
Para especificar las tasas de impuestos que un usuario paga en una suscripción, debe implementar el método taxRates en su modelo facturable y devolver un arreglo que contenga los IDs de las tasas de impuestos de Stripe. Puede definir estas tasas de impuestos en su panel 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 de impuesto por cliente, lo cual puede ser útil para una base de usuarios que abarca múltiples países y tasas de impuestos.
Si ofrece suscripciones con múltiples productos, puede definir diferentes tasas de impuestos 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'],
];
}
El método taxRates solo se aplica a cargos de suscripción. Si usa Cashier para hacer cargos "únicos", deberá especificar manualmente la tasa de impuesto en ese momento.
#Sincronizando Tasas de Impuestos
Al cambiar los IDs de tasas de impuestos codificados en el método taxRates, la configuración de impuestos en cualquier suscripción existente para el usuario permanecerá igual. Si desea actualizar el valor del impuesto para 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á cualquier tasa de impuesto de ítems para una suscripción con múltiples productos. Si su aplicación ofrece suscripciones con múltiples productos, debe asegurarse de que su modelo facturable implemente el método priceTaxRates discutido arriba.
#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 fiscal de un cliente:
use App\Models\User;
$user = User::find(1);
$user->isTaxExempt();
$user->isNotTaxExempt();
$user->reverseChargeApplies();
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 Ancla de Suscripción
Por defecto, el ancla del ciclo de facturación es la fecha en que se creó la suscripción o, si se usa un período de prueba, la fecha en que termina el período de prueba. Si desea modificar la fecha ancla de facturación, puede usar 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 más información sobre cómo gestionar ciclos de facturación de suscripciones, consulte la documentación de ciclos de facturación de Stripe
#Cancelando Suscripciones
Para cancelar una suscripción, llame al método cancel en la suscripción del usuario:
$user->subscription('default')->cancel();
Cuando una suscripción es cancelada, Cashier establecerá automáticamente la columna ends_at en su tabla subscriptions de la base de datos. Esta columna se usa para saber cuándo el método subscribed debe comenzar a devolver false.
Por ejemplo, si un cliente cancela una suscripción el 1 de marzo, pero la suscripción no estaba programada para terminar hasta el 5 de marzo, el método subscribed continuará devolviendo true hasta el 5 de marzo. Esto se hace porque normalmente se permite que un usuario continúe usando 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" usando el método onGracePeriod:
if ($user->subscription('default')->onGracePeriod()) {
// ...
}
Si desea cancelar una suscripción inmediatamente, llame al método cancelNow en la suscripción del usuario:
$user->subscription('default')->cancelNow();
Si desea cancelar una suscripción inmediatamente y facturar cualquier uso medido no facturado restante o nuevos ítems de factura por prorrateo pendientes, llame al método cancelNowAndInvoice en la suscripción del usuario:
$user->subscription('default')->cancelNowAndInvoice();
También puede optar por cancelar la suscripción en un momento específico:
$user->subscription('default')->cancelAt(
now()->addDays(10)
);
Finalmente, siempre debe cancelar las suscripciones de los usuarios antes de eliminar el modelo de usuario asociado:
$user->subscription('default')->cancelNow();
$user->delete();
#Reanudando Suscripciones
Si un cliente ha cancelado su suscripción y desea reanudarla, puede invocar el método resume en la suscripción. El cliente debe estar aún 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 completamente, no se le facturará inmediatamente. En cambio, su suscripción será reactivada y se le facturará en el ciclo de facturación original.
#Períodos de Prueba en Suscripciones
#Con Método de Pago Adelantado
Si desea ofrecer períodos de prueba a sus clientes mientras aún recopila la información del método de pago por adelantado, debe usar 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 la suscripción dentro de la base de datos e indicará a Stripe 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.
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, por lo que debe asegurarse de notificar a sus usuarios sobre la fecha de finalización de su prueba.
El método trialUntil le permite proporcionar una instancia de DateTime que especifica cuándo debe 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 usando 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 usar el método endTrial para finalizar inmediatamente un período de prueba de una suscripción:
$user->subscription('default')->endTrial();
Para determinar si un período de prueba existente ha expirado, puede usar los métodos hasExpiredTrial:
if ($user->hasExpiredTrial('default')) {
// ...
}
if ($user->subscription('default')->hasExpiredTrial()) {
// ...
}
#Definiendo los días de prueba en Stripe / Cashier
Puede elegir definir cuántos días de prueba reciben sus precios en el panel de Stripe o siempre pasarlos explícitamente usando 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 deseada de finalización de la prueba. Esto se hace típicamente durante el registro del usuario:
use App\Models\User;
$user = User::create([
// ...
'trial_ends_at' => now()->addDays(10),
]);
Asegúrese de agregar un cast de fecha para el atributo trial_ends_at dentro de la definición de la clase de su modelo facturable.
Cashier se refiere a este tipo de prueba como una "prueba genérica", ya que no está vinculada 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 obtener la fecha de finalización del período de prueba del usuario, puede usar el método trialEndsAt. Este método devolverá una instancia de fecha Carbon si el usuario está en período de prueba o null si no lo está. También puede pasar un parámetro opcional de tipo de suscripción si desea obtener la fecha de finalización de la prueba para una suscripción específica distinta a la predeterminada:
if ($user->onTrial()) {
$trialEndsAt = $user->trialEndsAt('main');
}
También puede usar 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"...
}
#Extender períodos de prueba
El método extendTrial le permite extender el período de prueba de una suscripción después de que esta ha sido creada. Si la prueba ya ha expirado y el cliente ya está siendo facturado por la suscripción, aún puede ofrecerle una extensión del período de prueba. El tiempo transcurrido dentro del período de prueba se deducirá de la próxima factura del cliente:
use App\Models\User;
$subscription = User::find(1)->subscription('default');
// Finalizar la prueba 7 días a partir de ahora...
$subscription->extendTrial(
now()->addDays(7)
);
// Añadir 5 días adicionales a la prueba...
$subscription->extendTrial(
$subscription->trial_ends_at->addDays(5)
);
#Manejo de webhooks de Stripe
Puede usar el Stripe CLI para ayudar a probar webhooks durante el desarrollo local.
Stripe puede notificar a su aplicación sobre una variedad de eventos mediante webhooks. Por defecto, una ruta que apunta al controlador de webhooks de Cashier se registra automáticamente por el proveedor de servicios de Cashier. Este controlador manejará todas las solicitudes entrantes de webhooks.
Por defecto, el controlador de webhooks de Cashier manejará automáticamente la cancelación de suscripciones que tengan demasiados cargos fallidos (según lo definido en su configuración de Stripe), actualizaciones de clientes, eliminaciones de clientes, actualizaciones de suscripciones y cambios en métodos de pago; sin embargo, como veremos 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, configure la URL del webhook en el panel de control de Stripe. Por defecto, el controlador de webhooks de Cashier responde a la ruta /stripe/webhook. La lista completa de todos los webhooks que debe habilitar en el panel de Stripe es:
customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedcustomer.updatedcustomer.deletedpayment_method.automatically_updatedinvoice.payment_action_requiredinvoice.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
Por defecto, el webhook creado apuntará a la URL definida por la variable de entorno APP_URL y la ruta cashier.webhook incluida con Cashier. Puede proporcionar la opción --url al invocar el comando si desea usar una URL diferente:
php artisan cashier:webhook --url "https://example.com/stripe/webhook"
El webhook creado usará la versión de la API de Stripe con la que su versión de Cashier es compatible. Si desea usar 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á activo inmediatamente. Si desea crear el webhook pero mantenerlo deshabilitado hasta que esté listo, puede proporcionar la opción --disabled al invocar el comando:
php artisan cashier:webhook --disabled
Asegúrese de proteger las solicitudes entrantes de webhooks de Stripe con el middleware de verificación de firma de webhook incluido en Cashier.
#Webhooks y protección CSRF
Dado que los webhooks de Stripe deben omitir la protección CSRF de Laravel, asegúrese de listar la URI como excepción en el middleware App\Http\Middleware\VerifyCsrfToken de su aplicación o de listar la ruta fuera del grupo de middleware web:
protected $except = [
'stripe/*',
];
#Definiendo manejadores de eventos de webhook
Cashier maneja automáticamente las cancelaciones de suscripciones por cargos fallidos y otros eventos comunes de webhooks de Stripe. Sin embargo, si tiene eventos adicionales de webhook que desea manejar, puede hacerlo escuchando los siguientes eventos que Cashier despacha:
Laravel\Cashier\Events\WebhookReceivedLaravel\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 listener que maneje el evento:
<?php
namespace App\Listeners;
use Laravel\Cashier\Events\WebhookReceived;
class StripeEventListener
{
/**
* Manejar webhooks recibidos de Stripe.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['type'] === 'invoice.payment_succeeded') {
// Manejar el evento entrante...
}
}
}
Una vez que su listener esté definido, puede registrarlo dentro del 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 usar las firmas de webhook de Stripe. Para mayor comodidad, Cashier incluye automáticamente un middleware que valida que la solicitud entrante del webhook de Stripe sea válida.
Para habilitar la verificación de webhooks, asegúrese de que la variable de entorno STRIPE_WEBHOOK_SECRET esté configurada en el archivo .env de su aplicación. El secret del webhook puede obtenerse desde el panel de su cuenta de Stripe.
#Cargos únicos
#Cargo simple
Si desea realizar un cargo único a un cliente, puede usar el método charge en una instancia del modelo facturable. Necesitará proporcionar un identificador de método de pago como segundo argumento del 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, permitiéndole pasar cualquier opción que desee a la creación subyacente del cargo en Stripe. Más información sobre las opciones disponibles al crear cargos puede encontrarse 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 lograr 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 es exitoso, se devolverá una instancia de Laravel\Cashier\Payment desde el método:
try {
$payment = $user->charge(100, $paymentMethod);
} catch (Exception $e) {
// ...
}
El método charge acepta el monto del pago en la unidad mínima de la moneda usada por su aplicación. Por ejemplo, si los clientes pagan en dólares estadounidenses, los montos deben especificarse en centavos.
#Cargo con factura
A veces puede necesitar hacer un cargo único y ofrecer una factura PDF a su cliente. El método invoicePrice le permite hacer justamente eso. Por ejemplo, facturaremos a un cliente por cinco camisetas nuevas:
$user->invoicePrice('price_tshirt', 5);
La factura se cargará inmediatamente contra 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 usar el método tabPrice para crear un cargo único por múltiples ítems (hasta 250 ítems por factura) agregándolos a la "cuenta" del cliente y luego facturando al cliente. Por ejemplo, podemos facturar a un cliente por cinco camisetas 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 usar los métodos invoicePrice y tabPrice con precios predefinidos. Al hacerlo, tendrá acceso a mejores análisis y datos dentro de su panel de Stripe respecto a sus ventas por producto.
Los métodos invoice, invoicePrice y invoiceFor crearán una factura en Stripe que reintentará los intentos de facturación fallidos. Si no desea que las facturas reintenten cargos fallidos, deberá cerrarlas usando la API de Stripe después del primer cargo fallido.
#Creando Payment Intents
Puede crear un nuevo payment intent de Stripe invocando el método pay en una instancia del modelo facturable. Al llamar a este método se creará un payment intent que estará 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 payment intent, puede devolver el client secret al frontend de su aplicación para que el usuario pueda completar el pago en su navegador. Para leer más sobre cómo construir flujos completos de pago usando payment intents de Stripe, consulte la documentación de Stripe.
Al usar el método pay, los métodos de pago predeterminados habilitados en su panel de Stripe estarán disponibles para el cliente. Alternativamente, si solo desea permitir algunos métodos de pago específicos, puede usar 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;
});
Los métodos pay y payWith aceptan el monto del pago en la unidad mínima de la moneda usada por su aplicación. Por ejemplo, si los clientes pagan en dólares estadounidenses, los montos deben especificarse en centavos.
#Reembolsando cargos
Si necesita reembolsar un cargo de Stripe, puede usar el método refund. Este método acepta el ID del payment intent de Stripe como primer argumento:
$payment = $user->charge(100, $paymentMethodId);
$user->refund($payment->id);
#Facturas
#Recuperando facturas
Puede recuperar fácilmente un array de facturas de un modelo facturable usando el método invoices. El método invoices devuelve una colección de instancias Laravel\Cashier\Invoice:
$invoices = $user->invoices();
Si desea incluir facturas pendientes en los resultados, puede usar el método invoicesIncludingPending:
$invoices = $user->invoicesIncludingPending();
Puede usar el método findInvoice para recuperar una factura específica por su ID:
$invoice = $user->findInvoice($invoiceId);
#Mostrando información de la factura
Al listar las facturas para el cliente, puede usar los métodos de la factura para mostrar la información relevante. Por ejemplo, puede listar cada factura en una tabla, permitiendo que el usuario descargue 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 }}">Descargar</a></td>
</tr>
@endforeach
</table>
#Facturas próximas
Para recuperar la factura próxima de un cliente, puede usar el método upcomingInvoice:
$invoice = $user->upcomingInvoice();
De manera similar, si el cliente tiene múltiples suscripciones, también puede recuperar la factura próxima para una suscripción específica:
$invoice = $user->subscription('default')->upcomingInvoice();
#Previsualizando facturas de suscripción
Usando el método previewInvoice, puede previsualizar una factura antes de hacer cambios en el precio. Esto le permitirá determinar cómo se verá la factura de su 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 previsualizar facturas con múltiples precios nuevos:
$invoice = $user->subscription('default')->previewInvoice(['price_yearly', 'price_metered']);
#Generando PDFs de facturas
Antes de generar PDFs de facturas, debe usar 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 usar el método downloadInvoice para generar una descarga PDF de una factura dada. Este método generará automáticamente la respuesta HTTP adecuada para descargar la factura:
use Illuminate\Http\Request;
Route::get('/user/invoice/{invoice}', function (Request $request, string $invoiceId) {
return $request->user()->downloadInvoice($invoiceId);
});
Por defecto, todos los datos en la factura se derivan de los datos del cliente y la factura almacenados en Stripe. El nombre del 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' => 'info@example.com',
'url' => 'https://example.com',
'vendorVat' => 'BE123456789',
]);
El método downloadInvoice también permite un nombre de archivo personalizado mediante su tercer argumento. Este nombre de archivo se le añadirá automáticamente la extensión .pdf:
return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice');
#Renderizador de facturas personalizado
Cashier también permite usar un renderizador de facturas personalizado. Por defecto, Cashier usa 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 querer renderizar un PDF de factura usando una llamada API a un servicio de renderizado 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 PDF en bruto.
*/
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 debe establecerse con el nombre de clase de su implementación personalizada.
#Checkout
Cashier Stripe también ofrece soporte para Stripe Checkout. Stripe Checkout elimina la complejidad de implementar páginas personalizadas para aceptar pagos al proporcionar una página de pago alojada y preconstruida.
La siguiente documentación contiene información sobre cómo comenzar a usar Stripe Checkout con Cashier. Para aprender más sobre Stripe Checkout, también debería considerar revisar la documentación oficial de Stripe sobre Checkout.
#Checkouts de productos
Puede realizar un checkout para un producto existente que haya sido creado en su panel de Stripe usando el método checkout en un modelo facturable. El método checkout iniciará una nueva sesión de Stripe Checkout. Por defecto, se requiere 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. Por defecto, cuando un usuario completa o cancela una compra con éxito, será redirigido a la ruta home de su aplicación, pero puede especificar URLs de callback personalizadas usando 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 success_url para el checkout, puede indicar a Stripe que agregue el ID de la sesión de checkout como un parámetro en la 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 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
Por defecto, Stripe Checkout no permite códigos de promoción canjeables por usuarios. Afortunadamente, hay una forma sencilla 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 únicos
También puede realizar un cargo simple para un producto ad-hoc que no ha sido creado en su panel de Stripe. Para hacerlo, puede usar el método checkoutCharge en un modelo facturable y pasarle un monto a cobrar, 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);
});
Al usar el método checkoutCharge, Stripe siempre creará un nuevo producto y precio en su panel de Stripe. Por lo tanto, recomendamos crear los productos previamente en su panel de Stripe y usar el método checkout en su lugar.
#Checkouts de suscripciones
Usar Stripe Checkout para suscripciones requiere que habilite el webhook customer.subscription.created en su panel de Stripe. Este webhook creará el registro de la suscripción en su base de datos y almacenará todos los ítems relevantes de la suscripción.
También puede usar Stripe Checkout para iniciar suscripciones. Después de definir su suscripción con los métodos del constructor 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 URLs 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 de promoción para los checkouts de suscripciones:
use Illuminate\Http\Request;
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->allowPromotionCodes()
->checkout();
});
Desafortunadamente, Stripe Checkout no soporta todas las opciones de facturación de suscripciones al iniciar suscripciones. Usar el método anchorBillingCycleOn en el constructor de suscripciones, configurar el comportamiento de prorrateo o el comportamiento de pago no tendrá efecto durante las sesiones de Stripe Checkout. Por favor, consulte la documentación de la API de Stripe Checkout Session 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 crear 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 soportado por Stripe Checkout.
#Suscripciones y webhooks
Recuerde que 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 regrese a la aplicación después de ingresar su información de pago. Para manejar este escenario, puede mostrar un mensaje informando al usuario que su pago o suscripción está pendiente.
#Recolección de IDs fiscales
Checkout también soporta la recolección del ID fiscal de un cliente. Para habilitar esto en una sesión de checkout, invoque el método collectTaxIds al crear la sesión:
$checkout = $user->collectTaxIds()->checkout('price_tshirt');
Cuando se invoca este método, aparecerá una nueva casilla de verificación para el cliente que le permite indicar si está comprando como empresa. Si es así, tendrá la oportunidad de proporcionar su número de ID fiscal.
Si ya ha configurado la recolección automática de impuestos en el proveedor de servicios de su aplicación, esta función se habilitará automáticamente y no es necesario invocar el método collectTaxIds.
#Checkouts para invitados
Usando el método Checkout::guest, puede iniciar sesiones de checkout para invitados de su 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'),
]);
});
De manera similar a cuando crea sesiones de checkout para usuarios existentes, puede utilizar métodos adicionales disponibles en la instancia 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 un checkout para invitados se haya completado, Stripe puede enviar un evento webhook checkout.session.completed, así que asegúrese de configurar su webhook de Stripe para que realmente envíe este evento a su aplicación. Una vez que el webhook esté habilitado en el panel de Stripe, puede manejar el webhook con Cashier. El objeto contenido en la carga útil del webhook será un checkout object que puede inspeccionar para cumplir con el pedido de su cliente.
#Manejo de pagos fallidos
A veces, los pagos para suscripciones o cargos únicos pueden fallar. Cuando esto sucede, Cashier lanzará una excepción Laravel\Cashier\Exceptions\IncompletePayment que le informa que esto ocurrió. Después de capturar esta excepción, tiene dos opciones sobre cómo proceder.
Primero, podría redirigir a su cliente a la página dedicada de confirmación de pago que se incluye con Cashier. Esta página ya tiene una ruta nombrada asociada que se registra a través del proveedor de servicios de Cashier. Por lo tanto, puede 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, se solicitará al cliente que ingrese nuevamente su información de 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 arriba. Al redirigir, se agregarán a la URL las variables de cadena de consulta message (string) y success (integer). Actualmente, la página de pago soporta los siguientes tipos de métodos de pago:
- Tarjetas de crédito
- Alipay
- Bancontact
- BECS Direct Debit
- EPS
- Giropay
- iDEAL
- SEPA Direct Debit
Alternativamente, podría permitir que Stripe maneje la confirmación del pago por usted. En este caso, en lugar de redirigir a la página de confirmación de pago, puede configurar los correos electrónicos automáticos de facturación de Stripe en su panel de Stripe. Sin embargo, si se captura una excepción IncompletePayment, aún debe informar al usuario que recibirá un correo electrónico con instrucciones adicionales para la confirmación del pago.
Las excepciones de pago pueden ser lanzadas para los siguientes métodos: charge, invoiceFor y invoice en modelos que usan el trait Billable. Al interactuar con suscripciones, el método create en el SubscriptionBuilder, y los métodos incrementAndInvoice y swapAndInvoice en los modelos Subscription y SubscriptionItem pueden lanzar excepciones de pago incompleto.
Determinar si una suscripción existente tiene un pago incompleto puede lograrse usando el método hasIncompletePayment en el modelo billable o en una instancia de suscripción:
if ($user->hasIncompletePayment('default')) {
// ...
}
if ($user->subscription('default')->hasIncompletePayment()) {
// ...
}
Puede 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 los pagos. Por ejemplo, los métodos de pago SEPA requieren datos adicionales de "mandato" durante el proceso de pago. Puede proporcionar estos datos a Cashier usando el método withPaymentConfirmationOptions:
$subscription->withPaymentConfirmationOptions([
'mandate_data' => '...',
])->swap('price_xxx');
Puede consultar la documentación de la API de Stripe para revisar todas las opciones aceptadas al confirmar pagos.
#Autenticación fuerte de clientes (SCA)
Si su negocio o uno de sus clientes está basado en Europa, deberá cumplir con las regulaciones de Autenticación Fuerte de Clientes (SCA) de la UE. Estas regulaciones fueron impuestas en septiembre de 2019 por la Unión Europea para prevenir fraudes en pagos. Afortunadamente, Stripe y Cashier están preparados para construir aplicaciones compatibles con SCA.
Antes de comenzar, revise la guía de Stripe sobre PSD2 y SCA así como su documentación sobre las nuevas APIs de SCA.
#Pagos que requieren confirmación adicional
Las regulaciones SCA a menudo requieren una verificación extra para confirmar y procesar un pago. Cuando esto sucede, Cashier lanzará una excepción Laravel\Cashier\Exceptions\IncompletePayment que le informa que se necesita verificación adicional. Más información sobre cómo manejar estas excepciones puede encontrarse en la documentación sobre manejo de pagos fallidos.
Las pantallas de confirmación de pago presentadas por Stripe o Cashier pueden estar adaptadas al flujo de pago específico de un banco o emisor de tarjeta e incluir confirmación adicional de tarjeta, un cargo pequeño temporal, autenticación separada del dispositivo u otras formas de verificación.
#Estado incompleto y vencido
Cuando un pago necesita confirmación adicional, la suscripción permanecerá en estado incomplete o past_due según lo indicado por su columna stripe_status en la base de datos. Cashier activará automáticamente la suscripción del cliente tan pronto como la confirmación del pago esté completa y su aplicación sea notificada por Stripe mediante webhook de su finalización.
Para más información sobre los estados incomplete y past_due, por favor consulte nuestra documentación adicional sobre estos estados.
#Notificaciones de pagos fuera de sesión
Dado que las regulaciones SCA requieren que los clientes verifiquen ocasionalmente sus detalles de pago incluso mientras 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 cuando una suscripción se está renovando. La notificación de pago de Cashier puede habilitarse configurando la variable de entorno CASHIER_PAYMENT_NOTIFICATION con una clase de notificación. Por defecto, esta notificación está deshabilitada. Por supuesto, Cashier incluye una clase de notificación que puede usar para este propósito, pero puede proporcionar su propia clase de notificación si lo desea:
CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment
Para asegurar que las notificaciones de confirmación de pago fuera de sesión se entreguen, verifique que los webhooks de Stripe estén configurados para su aplicación y que el webhook invoice.payment_action_required esté habilitado en su panel de Stripe. Además, su modelo Billable también debe usar el trait Illuminate\Notifications\Notifiable de Laravel.
Las notificaciones se enviarán incluso cuando los clientes realicen manualmente un pago que requiera confirmación adicional. Desafortunadamente, Stripe no puede saber si el pago se hizo manualmente o "fuera de sesión". Pero, un cliente simplemente verá un mensaje de "Pago exitoso" si visita la página de pago después de haber confirmado su pago. El cliente no podrá confirmar accidentalmente el mismo pago dos veces ni 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 desea interactuar directamente con los objetos de Stripe, puede obtenerlos convenientemente usando el método asStripe:
$stripeSubscription = $subscription->asStripeSubscription();
$stripeSubscription->application_fee_percent = 5;
$stripeSubscription->save();
También puede usar el método updateStripeSubscription para actualizar una suscripción de Stripe directamente:
$subscription->updateStripeSubscription(['application_fee_percent' => 5]);
Puede invocar el método stripe en la clase Cashier si desea usar el cliente Stripe\StripeClient directamente. Por ejemplo, podría usar este método para acceder a la instancia StripeClient y obtener una lista de precios de su cuenta de Stripe:
use Laravel\Cashier\Cashier;
$prices = Cashier::stripe()->prices->all();
#Pruebas
Al probar una aplicación que usa Cashier, puede simular las solicitudes HTTP reales a la API de Stripe; sin embargo, esto requiere que reimplemente parcialmente el comportamiento propio de Cashier. Por lo tanto, recomendamos permitir que sus pruebas realicen solicitudes reales a la API de Stripe. Aunque esto es más lento, proporciona mayor confianza de que su aplicación funciona como se espera y cualquier prueba lenta puede colocarse en su propio grupo de pruebas PHPUnit.
Al probar, recuerde que Cashier ya tiene una excelente suite de pruebas, por lo que solo debe enfocarse en probar el flujo de suscripciones y pagos de su propia aplicación y no cada comportamiento subyacente de Cashier.
Para comenzar, agregue la versión de pruebas de su clave secreta de Stripe a su archivo phpunit.xml:
<env name="STRIPE_SECRET" value="sk_test_<your-key>"/>
Ahora, cada vez que interactúe con Cashier durante las pruebas, enviará solicitudes reales a su entorno de pruebas de Stripe. Para mayor comodidad, debe precargar su cuenta de pruebas de Stripe con suscripciones / precios que pueda usar durante las pruebas.
Para probar una variedad de escenarios de facturación, como rechazos y fallos de tarjetas de crédito, puede usar la amplia gama de números de tarjetas y tokens de prueba proporcionados por Stripe.