1. Paquetes
  2. Laravel Cashier (Paddle)

Introducción

Advertencia En este momento, Cashier Paddle solo es compatible con Paddle Classic, que no está disponible para nuevos clientes de Paddle a menos que contactes con el soporte de Paddle.

Laravel Cashier Paddle proporciona una interfaz expresiva y fluida para los servicios de facturación de suscripciones de Paddle. Maneja casi todo el código boilerplate de facturación de suscripciones que estás evitando. Además de la gestión básica de suscripciones, Cashier puede manejar: cupones, cambio de suscripción, "cantidades" de suscripción, periodos de gracia para cancelación, y más.

Mientras trabajas con Cashier, te recomendamos que también revises las guías de usuario y la documentación de API de Paddle.

Actualización de Cashier

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

Instalación

Primero, instala el paquete Cashier para Paddle utilizando el administrador de paquetes Composer:

composer require laravel/cashier-paddle

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

Entorno de Pruebas de Paddle

Durante el desarrollo local y en entornos de prueba, debes registrar una cuenta de prueba Paddle. Esta cuenta te proporcionará un entorno de prueba para probar y desarrollar tus aplicaciones sin realizar pagos reales. Puedes utilizar números de tarjetas de prueba de Paddle para simular varios escenarios de pago.

Cuando uses el entorno Paddle Sandbox, debes establecer la variable de entorno PADDLE_SANDBOX en true dentro del archivo .env de tu aplicación:

PADDLE_SANDBOX=true

Después de haber terminado el desarrollo de tu aplicación, puedes solicitar una cuenta de vendedor de Paddle. Antes de que tu aplicación se ponga en producción, Paddle deberá aprobar el dominio de tu aplicación.

Migraciones de Base de Datos

El proveedor de servicios Cashier registra su propio directorio de migración de base de datos, así que recuerda migrar tu base de datos después de instalar el paquete. Las migraciones de Cashier crearán una nueva tabla customers. Además, se creará una nueva tabla subscriptions para almacenar todas las suscripciones de tus clientes. Finalmente, se creará una nueva tabla receipts para almacenar toda la información de recibos de tu aplicación:

php artisan migrate

Si necesitas sobrescribir las migraciones que se incluyen con Cashier, puedes publicarlas usando el comando Artisan vendor:publish:

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

Si deseas evitar que se ejecuten las migraciones de Cashier por completo, puedes usar el método ignoreMigrations proporcionado por Cashier. Típicamente, este método debería llamarse en el método register de tu AppServiceProvider:

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

Configuración

Modelo Facturable

Antes de usar Cashier, debes agregar el rasgo Billable a la definición del modelo de usuario. Este rasgo proporciona varios métodos para permitirte realizar tareas de facturación comunes, como crear suscripciones, aplicar cupones y actualizar información de método de pago:

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

Si tienes entidades facturables que no son usuarios, también puedes agregar el rasgo a esas clases:

use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
 
class Team extends Model
{
use Billable;
}

Claves API

A continuación, debes configurar tus claves de Paddle en el archivo .env de tu aplicación. Puedes obtener tus claves de API de Paddle desde el panel de control de Paddle:

PADDLE_VENDOR_ID=your-paddle-vendor-id
PADDLE_VENDOR_AUTH_CODE=your-paddle-vendor-auth-code
PADDLE_PUBLIC_KEY="your-paddle-public-key"
PADDLE_SANDBOX=true

La variable de entorno PADDLE_SANDBOX debe establecerse en true cuando estés usando el entorno de Sandbox de Paddle. La variable PADDLE_SANDBOX debe establecerse en false si estás implementando tu aplicación en producción y estás usando el entorno de vendedor en vivo de Paddle.

Paddle JS

Paddle depende de su propia biblioteca de JavaScript para iniciar el widget de pago de Paddle. Puedes cargar la biblioteca de JavaScript colocando la directiva Blade @paddleJS justo antes de la etiqueta de cierre </head> de la plantilla de tu aplicación:

<head>
...
 
@paddleJS
</head>

Configuración de Moneda

La moneda predeterminada de Cashier es el dólar estadounidense (USD). Puedes cambiar la moneda predeterminada definiendo una variable de entorno CASHIER_CURRENCY en el archivo .env de tu aplicación:

CASHIER_CURRENCY=EUR

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

CASHIER_CURRENCY_LOCALE=nl_BE

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

Anulando Modelos Predeterminados

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

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

Después de definir tu modelo, puedes indicarle a Cashier que use tu modelo personalizado a través de la clase Laravel\Paddle\Cashier. Típicamente, deberías informar a Cashier sobre tus modelos personalizados en el método boot de la clase App\Providers\AppServiceProvider de tu aplicación:

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

Conceptos Principales

Enlaces de Pago

Paddle carece de una API CRUD extensa para realizar cambios de estado de suscripción. Por lo tanto, la mayoría de las interacciones con Paddle se realizan a través de su widget de pago. Antes de que podamos mostrar el widget de pago, debemos generar un "enlace de pago" usando Cashier. Un "enlace de pago" informará al widget de pago de la operación de facturación que deseamos realizar:

use App\Models\User;
use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$payLink = $request->user()->newSubscription('default', $premium = 34567)
->returnTo(route('home'))
->create();
 
return view('billing', ['payLink' => $payLink]);
});

Cashier incluye un componente Blade paddle-button. Podemos pasar la URL del enlace de pago a este componente como una "propiedad". Cuando se hace clic en este botón, se mostrará el widget de pago de Paddle:

<x-paddle-button :url="$payLink" class="px-8 py-4">
Suscribirse
</x-paddle-button>

Por defecto, esto mostrará un botón con el estilo estándar de Paddle. Puedes quitar todo el estilo de Paddle agregando el atributo data-theme="none" al componente:

<x-paddle-button :url="$payLink" class="px-8 py-4" data-theme="none">
Suscribirse
</x-paddle-button>

El widget de pago de Paddle es asíncrono. Una vez que el usuario crea o actualiza una suscripción dentro del widget, Paddle enviará webhooks a tu aplicación para que puedas actualizar correctamente el estado de la suscripción en tu propia base de datos. Por lo tanto, es importante que configures correctamente los webhooks para adaptarte a los cambios de estado de Paddle.

Para obtener más información sobre los enlaces de pago, puedes revisar la documentación de la API de Paddle sobre la generación de enlaces de pago.

Advertencia Después de un cambio de estado de suscripción, el retraso para recibir el webhook correspondiente suele ser mínimo, pero debes tenerlo en cuenta en tu aplicación considerando que la suscripción del usuario podría no estar disponible de inmediato después de completar la compra.

Representación Manual de Enlaces de Pago

También puedes renderizar manualmente un enlace de pago sin usar los componentes Blade integrados de Laravel. Para empezar, genera la URL del enlace de pago como se muestra en ejemplos anteriores:

$payLink = $request->user()->newSubscription('default', $premium = 34567)
->returnTo(route('home'))
->create();

A continuación, simplemente adjunta la URL del enlace de pago a un elemento a en tu HTML:

<a href="#!" class="ml-4 paddle_button" data-override="{{ $payLink }}">
Paddle Checkout
</a>

Pagos que Requieren Confirmación Adicional

A veces se requiere verificación adicional para confirmar y procesar un pago. Cuando esto sucede, Paddle presentará una pantalla de confirmación de pago. Las pantallas de confirmación de pago presentadas por Paddle o Cashier pueden adaptarse al flujo de pago específico de un banco o emisor de tarjetas y pueden incluir una verificación adicional de la tarjeta, un cargo temporal pequeño, autenticación de dispositivo separada u otras formas de verificación.

Pago en Línea

Si no deseas usar el widget de pago de estilo "superpuesto" de Paddle, Paddle también proporciona la opción de mostrar el widget de forma incrustada. Aunque este enfoque no te permite ajustar ninguno de los campos HTML del pago, te permite incrustar el widget en tu aplicación.

Para facilitarte el inicio con el pago incrustado, Cashier incluye un componente Blade paddle-checkout. Para empezar, debes generar un enlace de pago y pasar el enlace de pago al atributo override del componente:

<x-paddle-checkout :override="$payLink" class="w-full" />

Para ajustar la altura del componente de pago incrustado, puedes pasar el atributo height al componente Blade:

<x-paddle-checkout :override="$payLink" class="w-full" height="500" />

Pago en Línea Sin Enlaces de Pago

Alternativamente, puedes personalizar el widget con opciones personalizadas en lugar de usar un enlace de pago:

@php
$options = [
'product' => $productId,
'title' => 'Título del Producto',
];
@endphp
 
<x-paddle-checkout :options="$options" class="w-full" />

Consulte la guía sobre el pago en línea de Paddle, así como su referencia de parámetros para obtener más detalles sobre las opciones disponibles en el pago en línea.

Advertencia Si también deseas utilizar la opción passthrough al especificar opciones personalizadas, debes proporcionar un array de clave / valor como su valor. Cashier manejará automáticamente la conversión del array a una cadena JSON. Además, la opción de customer_id passthrough está reservada para el uso interno de Cashier.

Representación Manual de Pago en Línea

También puede renderizar manualmente un pago en línea sin utilizar los componentes Blade integrados de Laravel. Para comenzar, genere la URL del enlace de pago como se muestra en ejemplos anteriores.

A continuación, puede utilizar Paddle.js para inicializar el proceso de pago. Para mantener este ejemplo simple, lo demostraremos utilizando Alpine.js; sin embargo, puede adaptar este ejemplo a su propio stack frontend:

<div class="paddle-checkout" x-data="{}" x-init="
Paddle.Checkout.open({
override: {{ $payLink }},
method: 'inline',
frameTarget: 'paddle-checkout',
frameInitialHeight: 366,
frameStyle: 'width: 100%; background-color: transparent; border: none;'
});
">
</div>

Identificación de Usuario

A diferencia de Stripe, los usuarios de Paddle son únicos en todo Paddle, no son únicos por cuenta de Paddle. Debido a esto, las API de Paddle actualmente no proporcionan un método para actualizar los detalles de un usuario, como su dirección de correo electrónico. Al generar enlaces de pago, Paddle identifica a los usuarios mediante el parámetro customer_email. Al crear una suscripción, Paddle intentará hacer coincidir el correo electrónico proporcionado por el usuario con un usuario existente en Paddle.

Dada esta peculiaridad, hay algunas cosas importantes que debe tener en cuenta al utilizar Cashier y Paddle. En primer lugar, debe tener en cuenta que, aunque las suscripciones en Cashier están vinculadas al mismo usuario de la aplicación, pueden estar vinculadas a diferentes usuarios dentro de los sistemas internos de Paddle. En segundo lugar, cada suscripción tiene su propia información de método de pago conectado y también puede tener direcciones de correo electrónico diferentes dentro de los sistemas internos de Paddle (dependiendo de qué correo electrónico se asignó al usuario cuando se creó la suscripción).

Por lo tanto, al mostrar suscripciones, siempre debe informar al usuario qué dirección de correo electrónico o información del método de pago está conectada a la suscripción en una base por suscripción. Puede recuperar esta información mediante los siguientes métodos proporcionados por el modelo Laravel\Paddle\Subscription:

$subscription = $user->subscription('default');
 
$subscription->paddleEmail();
$subscription->paymentMethod();
$subscription->cardBrand();
$subscription->cardLastFour();
$subscription->cardExpirationDate();

Actualmente no hay forma de modificar la dirección de correo electrónico de un usuario a través de la API de Paddle. Cuando un usuario desea actualizar su dirección de correo electrónico en Paddle, la única forma de hacerlo es ponerse en contacto con el soporte al cliente de Paddle. Al comunicarse con Paddle, deben proporcionar el valor paddleEmail de la suscripción para ayudar a Paddle a actualizar el usuario correcto.

Precios

Paddle le permite personalizar los precios por moneda, lo que básicamente le permite configurar diferentes precios para diferentes países. Cashier Paddle le permite recuperar todos los precios de un producto dado mediante el método productPrices. Este método acepta los ID de productos de los productos para los cuales desea recuperar los precios:

use Laravel\Paddle\Cashier;
 
$prices = Cashier::productPrices([123, 456]);

La moneda se determinará en función de la dirección IP de la solicitud; sin embargo, opcionalmente puede proporcionar un país específico para recuperar los precios:

use Laravel\Paddle\Cashier;
 
$prices = Cashier::productPrices([123, 456], ['customer_country' => 'BE']);

Después de recuperar los precios, puede mostrarlos como desee:

<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - {{ $price->price()->gross() }}</li>
@endforeach
</ul>

También puede mostrar el precio neto (sin impuestos) y mostrar el monto del impuesto por separado:

<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - {{ $price->price()->net() }} (+ {{ $price->price()->tax() }} tax)</li>
@endforeach
</ul>

Si recuperó precios para planes de suscripción, puede mostrar sus precios iniciales y recurrentes por separado:

<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - Initial: {{ $price->initialPrice()->gross() }} - Recurring: {{ $price->recurringPrice()->gross() }}</li>
@endforeach
</ul>

Para obtener más información, consulte la documentación de la API de Paddle sobre precios.

Clientes

Si un usuario ya es cliente y desea mostrar los precios que se aplican a ese cliente, puede hacerlo recuperando los precios directamente desde la instancia del cliente:

use App\Models\User;
 
$prices = User::find(1)->productPrices([123, 456]);

Internamente, Cashier utilizará el método paddleCountry del usuario para recuperar los precios en su moneda. Por ejemplo, un usuario que vive en los Estados Unidos verá precios en USD, mientras que un usuario en Bélgica verá precios en EUR. Si no se puede encontrar una moneda coincidente, se utilizará la moneda predeterminada del producto. Puede personalizar todos los precios de un producto o plan de suscripción en el panel de control de Paddle.

Cupones

También puede optar por mostrar los precios después de aplicar un descuento de cupón. Al llamar al método productPrices, los cupones se pueden pasar como una cadena delimitada por comas:

use Laravel\Paddle\Cashier;
 
$prices = Cashier::productPrices([123, 456], [
'coupons' => 'SUMMERSALE,20PERCENTOFF'
]);

Luego, muestre los precios calculados utilizando el método price:

<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - {{ $price->price()->gross() }}</li>
@endforeach
</ul>

Puede mostrar los precios originales listados (sin descuentos de cupón) utilizando el método listPrice:

<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - {{ $price->listPrice()->gross() }}</li>
@endforeach
</ul>

Advertencia Al usar la API de precios, Paddle solo permite aplicar cupones a productos de compra única y no a planes de suscripción.

Clientes

Valores Predeterminados del Cliente

Cashier le permite definir algunos valores predeterminados útiles para sus clientes al crear enlaces de pago. Establecer estos valores predeterminados le permite rellenar previamente la dirección de correo electrónico, el país y el código postal del cliente para que puedan pasar directamente a la parte de pago del widget de pago. Puede establecer estos valores predeterminados sobrescribiendo los siguientes métodos en su modelo facturable:

/**
* Obtiene la dirección de correo electrónico del cliente para asociar con Paddle.
*/
public function paddleEmail(): string|null
{
return $this->email;
}
 
/**
* Obtiene el país del cliente para asociar con Paddle.
*
* Esto debe ser un código de 2 letras. Vea el enlace a continuación para ver los países admitidos.
*
* @link https://developer.paddle.com/reference/platform-parameters/supported-countries
*/
public function paddleCountry(): string|null
{
// ...
}
 
/**
* Obtiene el código postal del cliente para asociar con Paddle.
*
* Vea el enlace a continuación para conocer los países que requieren esto.
*
* @link https://developer.paddle.com/reference/platform-parameters/supported-countries#countries-requiring-postcode
*/
public function paddlePostcode(): string|null
{
// ...
}

Estos valores predeterminados se utilizarán para cada acción en Cashier que genere un enlace de pago.

Suscripciones

Creación de Suscripciones

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

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$payLink = $request->user()->newSubscription('default', $premium = 12345)
->returnTo(route('home'))
->create();
 
return view('billing', ['payLink' => $payLink]);
});

El primer argumento pasado al método newSubscription debe ser el nombre interno de la suscripción. Si tu aplicación solo ofrece una suscripción, podrías llamarla default o primary. Este nombre de suscripción es solo para uso interno de la aplicación y no se debe mostrar a los usuarios. Además, no debe contener espacios y nunca se debe cambiar después de crear la suscripción. El segundo argumento dado al método newSubscription es el plan específico al que el usuario se está suscribiendo. Este valor debe corresponder al identificador del plan en Paddle. El método returnTo acepta una URL a la que se redirigirá al usuario después de completar con éxito el pago.

El método create creará un enlace de pago que puedes usar para generar un botón de pago. El botón de pago se puede generar utilizando el componente Blade paddle-button que se incluye con Cashier Paddle:

<x-paddle-button :url="$payLink" class="px-8 py-4">
Suscribirse
</x-paddle-button>

Después de que el usuario haya completado su compra, se enviará un webhook subscription_created desde Paddle. Cashier recibirá este webhook y configurará la suscripción para tu cliente. Para asegurarte de que todos los webhooks se reciban y manejen correctamente en tu aplicación, asegúrate de configurar el manejo de webhooks correctamente.

Detalles Adicionales

Si deseas especificar detalles adicionales del cliente o de la suscripción, puedes hacerlo pasándolos como un array de pares clave / valor al método create. Para obtener más información sobre los campos adicionales admitidos por Paddle, consulta la documentación de Paddle sobre generar enlaces de pago:

$payLink = $user->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
->create([
'vat_number' => $vatNumber,
]);

Cupones

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

$payLink = $user->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
->withCoupon('code')
->create();

Metadatos

También puedes pasar un array de metadatos utilizando el método withMetadata:

$payLink = $user->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
->withMetadata(['key' => 'value'])
->create();

Advertencia Al proporcionar metadatos, evita usar subscription_name como clave de metadatos. Esta clave está reservada para el uso interno de Cashier.

Verificación del Estado de la Suscripción

Una vez que un usuario se ha suscrito a tu aplicación, puedes verificar el estado de su suscripción utilizando una variedad de métodos convenientes. En primer lugar, el método subscribed devuelve true si el usuario tiene una suscripción activa, incluso si la suscripción está actualmente en su período de prueba:

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

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

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureUserIsSubscribed
{
/**
* Maneja 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 deseas determinar si un usuario todavía está en su período de prueba, puedes usar el método onTrial. Este método puede ser útil para determinar si debes mostrar una advertencia al usuario de que todavía está en su período de prueba:

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

El método subscribedToPlan se puede utilizar para determinar si el usuario está suscrito a un plan específico según un ID de plan de Paddle dado. En este ejemplo, determinaremos si la suscripción "default" del usuario está activamente suscrita al plan mensual:

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

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

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

Estado de Suscripción Cancelada

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

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

También puedes determinar si un usuario ha cancelado su suscripción, pero aún está en su "periodo 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 está en su "periodo de gracia" hasta el 10 de marzo. Ten en cuenta que durante este tiempo, el método subscribed sigue devolviendo true:

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

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

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

Estado de Vencimiento

Si un pago falla para una suscripción, se marcará como past_due. Cuando tu suscripción está en este estado, no estará activa hasta que el cliente actualice su información de pago. Puedes determinar si una suscripción está vencida utilizando el método pastDue en la instancia de suscripción:

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

Cuando una suscripción está vencida, debes instruir al usuario a que actualice su información de pago. Puedes configurar cómo se manejan las suscripciones vencidas en tu configuración de suscripción Paddle.

Si deseas que las suscripciones aún se consideren activas cuando estén past_due, puedes usar el método keepPastDueSubscriptionsActive proporcionado por Cashier. Normalmente, este método debe llamarse en el método register de tu AppServiceProvider:

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

Advertencia Cuando una suscripción está en estado past_due, no se puede cambiar hasta que se actualice la información de pago. Por lo tanto, los métodos swap y updateQuantity generarán una excepción cuando la suscripción esté en estado past_due.

Ámbitos de Suscripción

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

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

Una lista completa de los ámbitos disponibles está disponible a continuación:

Subscription::query()->active();
Subscription::query()->onTrial();
Subscription::query()->notOnTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();
Subscription::query()->ended();
Subscription::query()->paused();
Subscription::query()->notPaused();
Subscription::query()->onPausedGracePeriod();
Subscription::query()->notOnPausedGracePeriod();
Subscription::query()->cancelled();
Subscription::query()->notCancelled();
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();

Cargos Únicos de Suscripción

Los cargos únicos de suscripción te permiten cobrar a los suscriptores un cargo único además de sus suscripciones:

$response = $user->subscription('default')->charge(12.99, 'Support Add-on');

A diferencia de los cargos únicos, este método cobrará inmediatamente el método de pago almacenado del cliente por la suscripción. El monto del cargo siempre debe estar definido en la moneda de la suscripción.

Actualización de Información de Pago

Paddle siempre guarda un método de pago por suscripción. Si deseas actualizar el método de pago predeterminado de una suscripción, primero debes generar una "URL de actualización de suscripción" utilizando el método updateUrl en el modelo de suscripción:

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

Luego, puedes usar la URL generada en combinación con el componente Blade paddle-button proporcionado por Cashier para permitir que el usuario inicie el widget de Paddle y actualice su información de pago:

<x-paddle-button :url="$updateUrl" class="px-8 py-4">
Update Card
</x-paddle-button>

Cuando un usuario haya terminado de actualizar su información, se enviará un webhook subscription_updated desde Paddle y los detalles de la suscripción se actualizarán en la base de datos de tu aplicación.

Cambio de Planes

Después de que un usuario se haya suscrito a tu aplicación, ocasionalmente puede querer cambiar a un nuevo plan de suscripción. Para actualizar el plan de suscripción de un usuario, debes pasar el identificador del plan de Paddle al método swap de la suscripción:

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

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

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

Advertencia Los planes no se pueden intercambiar cuando hay una prueba activa. Para obtener información adicional sobre esta limitación, consulta la documentación de Paddle.

Prorrateos

Por defecto, Paddle prorratea los cargos al cambiar entre planes. El método noProrate se puede usar para actualizar las suscripciones sin prorratear los cargos:

$user->subscription('default')->noProrate()->swap($premium = 34567);

Cantidad de Suscripción

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

$user = User::find(1);
 
$user->subscription('default')->incrementQuantity();
 
// Agrega cinco a la cantidad actual de la suscripción...
$user->subscription('default')->incrementQuantity(5);
 
$user->subscription('default')->decrementQuantity();
 
// Resta cinco de la cantidad actual de la suscripción...
$user->subscription('default')->decrementQuantity(5);

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

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

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

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

Modificadores de Suscripción

Los modificadores de suscripción te permiten implementar la facturación medida o extender suscripciones con complementos.

Por ejemplo, es posible que desees ofrecer un complemento de "Soporte Premium" con tu suscripción estándar. Puedes crear este modificador de la siguiente manera:

$modifier = $user->subscription('default')->newModifier(12.99)->create();

El ejemplo anterior agregará un complemento de $12.99 a la suscripción. Por defecto, este cargo se repetirá en cada intervalo que hayas configurado para la suscripción. Si lo deseas, puedes agregar una descripción legible al modificador utilizando el método description del modificador:

$modifier = $user->subscription('default')->newModifier(12.99)
->description('modify_10x/cashier-paddle.code_test_5')
->create();

Para ilustrar cómo implementar la facturación medida mediante modificadores, imagina que tu aplicación cobra por cada mensaje de SMS enviado por el usuario. Primero, debes crear un plan de $0 en tu panel de Paddle. Una vez que el usuario se haya suscrito a este plan, puedes agregar modificadores que representen cada cargo individual a la suscripción:

$modifier = $user->subscription('default')->newModifier(0.99)
->description('modify_10x/cashier-paddle.code_test_6')
->oneTime()
->create();

Como puedes ver, invocamos el método oneTime al crear este modificador. Este método asegurará que el modificador se cobre solo una vez y no se repita en cada intervalo de facturación.

Recuperación de Modificadores

Puedes recuperar una lista de todos los modificadores de una suscripción a través del método modifiers:

$modifiers = $user->subscription('default')->modifiers();
 
foreach ($modifiers as $modifier) {
$modifier->amount(); // $0.99
$modifier->description; // Nuevo mensaje de texto
}

Eliminación de Modificadores

Los modificadores se pueden eliminar invocando el método delete en una instancia de Laravel\Paddle\Modifier:

$modifier->delete();

Múltiples Suscripciones

Paddle permite que tus clientes tengan múltiples suscripciones simultáneamente. Por ejemplo, puedes tener un gimnasio que ofrezca una suscripción de natación y una suscripción de levantamiento de pesas, y cada suscripción puede tener un precio diferente. Por supuesto, los clientes deben poder suscribirse a uno o ambos planes.

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

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

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

$user->subscription('swimming')->swap($swimmingYearly = 34567);

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

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

Pausa de Suscripciones

Para pausar una suscripción, llama al método pause en la suscripción del usuario:

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

Cuando una suscripción está pausada, Cashier establecerá automáticamente la columna paused_from en tu base de datos. Esta columna se utiliza para saber cuándo el método paused debería comenzar a devolver true. Por ejemplo, si un cliente pausa una suscripción el 1 de marzo, pero la suscripción no estaba programada para renovarse hasta el 5 de marzo, el método paused continuará devolviendo false hasta el 5 de marzo. Esto se hace porque generalmente se permite a un usuario seguir utilizando una aplicación hasta el final de su ciclo de facturación.

Puedes determinar si un usuario ha pausado su suscripción pero aún está en su "periodo de gracia" utilizando el método onPausedGracePeriod:

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

Para reanudar una suscripción pausada, puedes llamar al método unpause en la suscripción del usuario:

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

Advertencia No se puede modificar una suscripción mientras está pausada. Si deseas cambiar a un plan diferente o actualizar cantidades, debes reanudar la suscripción primero.

Cancelación de Suscripciones

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

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

Cuando se cancela una suscripción, Cashier establecerá automáticamente la columna ends_at en tu base de datos. Esta columna se utiliza para saber cuándo el método subscribed debería 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 finalizar hasta el 5 de marzo, el método subscribed continuará devolviendo true hasta el 5 de marzo. Esto se hace porque generalmente se permite a un usuario seguir utilizando una aplicación hasta el final de su ciclo de facturación.

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

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

Si deseas cancelar una suscripción de inmediato, puedes llamar al método cancelNow en la suscripción del usuario:

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

Advertencia Las suscripciones de Paddle no se pueden reanudar después de cancelarse. Si tu cliente desea reanudar su suscripción, deberá suscribirse a una nueva suscripción.

Pruebas de Suscripción

Con Método de Pago por Adelantado

Advertencia Mientras estás en período de prueba y recopilando detalles del método de pago por adelantado, Paddle evita cualquier cambio en la suscripción, como cambiar de plan o actualizar cantidades. Si deseas permitir que un cliente cambie de plan durante una prueba, la suscripción debe cancelarse y recrearse.

Si deseas ofrecer períodos de prueba a tus clientes y aún así recopilar información de método de pago por adelantado, debes usar el método trialDays al crear tus enlaces de pago de suscripción:

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$payLink = $request->user()->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
->trialDays(10)
->create();
 
return view('billing', ['payLink' => $payLink]);
});

Este método establecerá la fecha de finalización del período de prueba en el registro de suscripción dentro de la base de datos de tu aplicación, así como indicará a Paddle que no comience a facturar al cliente hasta después de esta fecha.

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

Puedes determinar si el usuario está en su período de prueba utilizando el método onTrial tanto en la instancia de usuario como en la instancia de suscripción. Los dos ejemplos siguientes son equivalentes:

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

Para determinar si una prueba existente ha expirado, puedes usar los métodos hasExpiredTrial:

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

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

Puedes elegir definir cuántos días de prueba tiene tu plan en el panel de Paddle o siempre pasarlos explícitamente mediante Cashier. Si eliges definir los días de prueba de tu plan en Paddle, debes saber que las nuevas suscripciones, incluidas las nuevas suscripciones para un cliente que tenía una suscripción en el pasado, siempre recibirán un período de prueba a menos que llames explícitamente al método trialDays(0).

Sin Método de Pago por Adelantado

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

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

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 de User 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és listo para crear una suscripción real para el usuario, puedes usar el método newSubscription como de costumbre:

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$payLink = $user->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
->create();
 
return view('billing', ['payLink' => $payLink]);
});

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

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

Puedes usar el método onGenericTrial si deseas saber específicamente si 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"...
}

Advertencia No hay forma de extender o modificar un período de prueba en una suscripción de Paddle después de haberse creado.

Manejo de Webhooks de Paddle

Paddle puede notificar a tu aplicación sobre una variedad de eventos a través de webhooks. Por defecto, el proveedor de servicios Cashier registra una ruta que apunta al controlador de webhooks de Cashier. Este controlador manejará todas las solicitudes entrantes de webhook.

Por defecto, este controlador manejará automáticamente la cancelación de suscripciones que tienen demasiados cargos fallidos (según lo definido por tu configuración de recuperación de Paddle), actualizaciones de suscripciones y cambios de método de pago; sin embargo, como descubriremos pronto, puedes extender este controlador para manejar cualquier evento de webhook de Paddle que desees.

Para asegurarte de que tu aplicación pueda manejar los webhooks de Paddle, asegúrate de configurar la URL del webhook en el panel de control de Paddle. Por defecto, el controlador de webhooks de Cashier responde a la ruta /paddle/webhook. La lista completa de todos los webhooks que debes habilitar en el panel de control de Paddle es:

  • Suscripción Creada
  • Suscripción Actualizada
  • Suscripción Cancelada
  • Pago Realizado
  • Pago de Suscripción Realizado

Advertencia Asegúrate de proteger las solicitudes entrantes con la verificación de firma de webhook incluida en Cashier.

Webhooks y Protección CSRF

Dado que los webhooks de Paddle deben pasar por alto la protección CSRF de Laravel, asegúrate de listar la URI como una excepción en tu middleware App\Http\Middleware\VerifyCsrfToken o listar la ruta fuera del grupo de middleware web:

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

Webhooks y Desarrollo Local

Para que Paddle pueda enviar webhooks de tu aplicación durante el desarrollo local, necesitarás exponer tu aplicación mediante un servicio de uso compartido de sitios como Ngrok o Expose. Si estás desarrollando tu aplicación localmente con Laravel Sail, puedes usar el comando de uso compartido de sitios de Sail.

Definición de Manejadores de Eventos de Webhooks

Cashier maneja automáticamente la cancelación de suscripciones por cargos fallidos y otros webhooks comunes de Paddle. Sin embargo, si tienes eventos de webhook adicionales que te gustaría manejar, puedes hacerlo escuchando los siguientes eventos que son despachados por Cashier:

  • Laravel\Paddle\Events\WebhookReceived
  • Laravel\Paddle\Events\WebhookHandled

Ambos eventos contienen el payload completo del webhook de Paddle. Por ejemplo, si deseas manejar el webhook invoice.payment_succeeded, puedes registrar un escuchador que manejará el evento:

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

Una vez que tu escuchador ha sido definido, puedes registrarlo dentro del EventServiceProvider de tu aplicación:

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

Cashier también emite eventos dedicados al tipo de webhook recibido. Además del payload completo de Paddle, también contienen los modelos relevantes que se utilizaron para procesar el webhook, como el modelo facturable, la suscripción o el recibo:

  • Laravel\Paddle\Events\PaymentSucceeded
  • Laravel\Paddle\Events\SubscriptionPaymentSucceeded
  • Laravel\Paddle\Events\SubscriptionCreated
  • Laravel\Paddle\Events\SubscriptionUpdated
  • Laravel\Paddle\Events\SubscriptionCancelled

También puedes anular la ruta de webhook integrada por defecto definiendo la variable de entorno CASHIER_WEBHOOK en el archivo .env de tu aplicación. Este valor debería ser la URL completa de tu ruta de webhook y debe coincidir con la URL establecida en tu panel de control de Paddle:

CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url

Verificación de Firmas de Webhooks

Para asegurar tus webhooks, puedes usar las firmas de webhook de Paddle. Para mayor comodidad, Cashier incluye automáticamente un middleware que valida que la solicitud de webhook entrante de Paddle sea válida.

Para habilitar la verificación de webhook, asegúrate de que la variable de entorno PADDLE_PUBLIC_KEY esté definida en el archivo .env de tu aplicación. La clave pública se puede obtener desde el panel de control de tu cuenta de Paddle.

Cargos Únicos

Carga Simple

Si deseas hacer un cargo único a un cliente, puedes usar el método charge en una instancia de un modelo facturable para generar un enlace de pago para el cargo. El método charge acepta el monto del cargo (float) como su primer argumento y una descripción del cargo como su segundo argumento:

use Illuminate\Http\Request;
 
Route::get('/store', function (Request $request) {
return view('store', [
'payLink' => $user->charge(12.99, 'Action Figure')
]);
});

Después de generar el enlace de pago, puedes usar el componente Blade paddle-button proporcionado por Cashier para permitir al usuario iniciar el widget de Paddle y completar el cargo:

<x-paddle-button :url="$payLink" class="px-8 py-4">
Comprar
</x-paddle-button>

El método charge acepta un array como su tercer argumento, permitiéndote pasar cualquier opción que desees a la creación del enlace de pago subyacente de Paddle. Consulta la documentación de Paddle para obtener más información sobre las opciones disponibles al crear cargos:

$payLink = $user->charge(12.99, 'Action Figure', [
'custom_option' => $value,
]);

Los cargos se realizan en la moneda especificada en la opción de configuración cashier.currency. Por defecto, esto está configurado en USD. Puedes anular la moneda predeterminada definiendo la variable de entorno CASHIER_CURRENCY en el archivo .env de tu aplicación:

CASHIER_CURRENCY=EUR

También puedes anular los precios por moneda utilizando el sistema de coincidencia de precios dinámicos de Paddle. Para hacerlo, pasa un array de precios en lugar de un monto fijo:

$payLink = $user->charge([
'USD:19.99',
'EUR:15.99',
], 'Action Figure');

Cargando Productos

Si deseas hacer un cargo único contra un producto específico configurado en Paddle, puedes usar el método chargeProduct en una instancia de un modelo facturable para generar un enlace de pago:

use Illuminate\Http\Request;
 
Route::get('/store', function (Request $request) {
return view('store', [
'payLink' => $request->user()->chargeProduct($productId = 123)
]);
});

Luego, puedes proporcionar el enlace de pago al componente paddle-button para permitir al usuario inicializar el widget de Paddle:

<x-paddle-button :url="$payLink" class="px-8 py-4">
Comprar
</x-paddle-button>

El método chargeProduct acepta un array como su segundo argumento, permitiéndote pasar cualquier opción que desees a la creación del enlace de pago subyacente de Paddle. Consulta la documentación de Paddle sobre las opciones disponibles al crear cargos:

$payLink = $user->chargeProduct($productId, [
'custom_option' => $value,
]);

Reembolso de Pedidos

Si necesitas reembolsar un pedido de Paddle, puedes usar el método refund. Este método acepta el ID del pedido de Paddle como su primer argumento. Puedes obtener los recibos para un modelo facturable dado usando el método receipts:

use App\Models\User;
 
$user = User::find(1);
 
$receipt = $user->receipts()->first();
 
$refundRequestId = $user->refund($receipt->order_id);

Opcionalmente, puedes especificar un monto específico para el reembolso, así como una razón para el reembolso:

$receipt = $user->receipts()->first();
 
$refundRequestId = $user->refund(
$receipt->order_id, 5.00, 'Tiempo de producto no utilizado'
);

Nota Puedes usar $refundRequestId como referencia para el reembolso al contactar al soporte de Paddle.

Recibos

Puedes recuperar fácilmente un array de recibos de un modelo facturable a través de la propiedad receipts:

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

Al listar los recibos para el cliente, puedes usar los métodos de la instancia del recibo para mostrar la información relevante del recibo. Por ejemplo, es posible que desees listar cada recibo en una tabla, permitiendo al usuario descargar fácilmente cualquiera de los recibos:

<table>
@foreach ($receipts as $receipt)
<tr>
<td>{{ $receipt->paid_at->toFormattedDateString() }}</td>
<td>{{ $receipt->amount() }}</td>
<td><a href="{{ $receipt->receipt_url }}" target="_blank">Descargar</a></td>
</tr>
@endforeach
</table>

Pagos Pasados y Futuros

Puedes usar los métodos lastPayment y nextPayment para recuperar y mostrar los pagos pasados o futuros de un cliente para suscripciones recurrentes:

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

Ambos de estos métodos devolverán una instancia de Laravel\Paddle\Payment; sin embargo, nextPayment devolverá null cuando el ciclo de facturación haya terminado (como cuando se ha cancelado una suscripción):

Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}

Manejo de Pagos Fallidos

Los pagos de suscripciones fallan por diversas razones, como tarjetas vencidas o tarjetas sin fondos. Cuando esto sucede, te recomendamos que dejes que Paddle maneje los fallos de pago por ti. Específicamente, puedes configurar los correos electrónicos de facturación automática de Paddle en tu panel de Paddle.

Alternativamente, puedes realizar una personalización más precisa escuchando el evento de Paddle subscription_payment_failed a través del evento WebhookReceived despachado por Cashier. También debes asegurarte de que la opción "Subscription Payment Failed" esté habilitada en la configuración de Webhook de tu panel de Paddle:

<?php
 
namespace App\Listeners;
 
use Laravel\Paddle\Events\WebhookReceived;
 
class PaddleEventListener
{
/**
* Maneja los webhooks recibidos de Paddle.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['alert_name'] === 'subscription_payment_failed') {
// Maneja el pago de suscripción fallido...
}
}
}

Una vez que tu escuchador ha sido definido, debes registrarlo dentro del EventServiceProvider de tu aplicación:

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

Pruebas

Mientras estás en pruebas, debes probar manualmente tu flujo de facturación para asegurarte de que tu integración funcione como se espera.

Para pruebas automatizadas, incluidas aquellas ejecutadas dentro de un entorno de integración continua (CI), puedes usar el cliente HTTP de Laravel para simular llamadas HTTP realizadas a Paddle. Aunque esto no prueba las respuestas reales de Paddle, proporciona una forma de probar tu aplicación sin llamar realmente a la API de Paddle.