- Introducción
- Actualización de Cashier
- Instalación
- Configuración
- Primeros pasos
- Sesiones de Pago
- Previsualización de Precios
- Clientes
- Suscripciones
- Pruebas de Suscripción
- Manejo de Webhooks de Paddle
- Cargos Únicos
- Transacciones
- Pruebas
#Introducción
Esta documentación es para la integración de Cashier Paddle 2.x con Paddle Billing. Si aún usa Paddle Classic, debe utilizar Cashier Paddle 1.x.
Laravel Cashier Paddle ofrece una interfaz expresiva y fluida para los servicios de facturación por suscripción de Paddle. Maneja casi todo el código repetitivo de facturación por suscripción que usted teme. Además de la gestión básica de suscripciones, Cashier puede manejar: intercambio de suscripciones, "cantidades" de suscripción, pausa de suscripciones, períodos de gracia para cancelaciones y más.
Antes de profundizar en Cashier Paddle, le recomendamos revisar también las guías conceptuales y la documentación API de Paddle.
#Actualización de Cashier
Al actualizar a una nueva versión de Cashier, es importante que revise cuidadosamente la guía de actualización.
#Instalación
Primero, instale el paquete Cashier para Paddle usando el gestor de paquetes Composer:
composer require laravel/cashier-paddle
Luego, debe publicar los archivos de migración de Cashier usando el comando Artisan vendor:publish:
php artisan vendor:publish --tag="cashier-migrations"
Después, debe ejecutar las migraciones de base de datos de su aplicación. Las migraciones de Cashier crearán una nueva tabla customers. Además, se crearán nuevas tablas subscriptions y subscription_items para almacenar todas las suscripciones de sus clientes. Por último, se creará una tabla transactions para almacenar todas las transacciones de Paddle asociadas con sus clientes:
php artisan migrate
Para asegurar que Cashier maneje correctamente todos los eventos de Paddle, recuerde configurar el manejo de webhooks de Cashier.
#Sandbox de Paddle
Durante el desarrollo local y en staging, debe registrar una cuenta Sandbox de Paddle. Esta cuenta le proporcionará un entorno aislado para probar y desarrollar sus aplicaciones sin realizar pagos reales. Puede usar los números de tarjeta de prueba de Paddle para simular diversos escenarios de pago.
Al usar el entorno Sandbox de Paddle, debe establecer la variable de entorno PADDLE_SANDBOX en true dentro del archivo .env de su aplicación:
PADDLE_SANDBOX=true
Una vez que haya terminado de desarrollar su aplicación, puede solicitar una cuenta de vendedor en Paddle. Antes de poner su aplicación en producción, Paddle deberá aprobar el dominio de su aplicación.
#Configuración
#Modelo Billable
Antes de usar Cashier, debe agregar el trait Billable a la definición de su modelo de usuario. Este trait proporciona varios métodos que le permiten realizar tareas comunes de facturación, como crear suscripciones y actualizar la información del método de pago:
use Laravel\Paddle\Billable;
class User extends Authenticatable
{
use Billable;
}
Si tiene entidades facturables que no son usuarios, también puede agregar el trait a esas clases:
use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
{
use Billable;
}
#Claves API
Luego, debe configurar sus claves de Paddle en el archivo .env de su aplicación. Puede obtener sus claves API de Paddle desde el panel de control de Paddle:
PADDLE_CLIENT_SIDE_TOKEN=your-paddle-client-side-token
PADDLE_API_KEY=your-paddle-api-key
PADDLE_RETAIN_KEY=your-paddle-retain-key
PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret"
PADDLE_SANDBOX=true
La variable de entorno PADDLE_SANDBOX debe establecerse en true cuando use el entorno Sandbox de Paddle. La variable PADDLE_SANDBOX debe establecerse en false si está desplegando su aplicación en producción y usa el entorno de vendedor en vivo de Paddle.
La clave PADDLE_RETAIN_KEY es opcional y solo debe configurarse si usa Paddle con Retain.
#Paddle JS
Paddle depende de su propia biblioteca JavaScript para iniciar el widget de checkout de Paddle. Puede cargar la biblioteca JavaScript colocando la directiva Blade @paddleJS justo antes de la etiqueta de cierre </head> del layout de su aplicación:
<head>
...
@paddleJS
</head>
#Configuración de Moneda
Puede especificar una configuración regional para formatear los valores monetarios que se mostrarán en las facturas. Internamente, Cashier utiliza la clase PHP NumberFormatter 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.
#Sobrescribir Modelos Predeterminados
Puede extender libremente los modelos que Cashier usa internamente definiendo su propio modelo y extendiendo el modelo correspondiente de Cashier:
use Laravel\Paddle\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\Paddle\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\Transaction;
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useTransactionModel(Transaction::class);
}
#Primeros pasos
#Venta de Productos
Antes de utilizar Paddle Checkout, debe definir Productos con precios fijos en su panel de Paddle. Además, debe configurar el manejo de webhooks de Paddle.
Ofrecer facturación de productos y suscripciones a través de su aplicación puede ser intimidante. Sin embargo, gracias a Cashier y al Checkout Overlay de Paddle, puede construir fácilmente integraciones de pago modernas y robustas.
Para cobrar a los clientes por productos de cargo único no recurrente, utilizaremos Cashier para cobrar a los clientes con el Checkout Overlay de Paddle, donde proporcionarán sus datos de pago y confirmarán su compra. Una vez realizado el pago a través del Checkout Overlay, el cliente será redirigido a una URL de éxito que usted elija dentro de su aplicación:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $request->user()->checkout('pri_deluxe_album')
->returnTo(route('dashboard'));
return view('buy', ['checkout' => $checkout]);
})->name('checkout');
Como puede ver en el ejemplo anterior, utilizaremos el método checkout proporcionado por Cashier para crear un objeto de checkout que presentará al cliente el Checkout Overlay de Paddle para un "identificador de precio" dado. Al usar Paddle, "precios" se refieren a precios definidos para productos específicos.
Si es necesario, el método checkout creará automáticamente un cliente en Paddle y conectará ese registro de cliente de Paddle 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 de éxito dedicada donde podrá mostrar un mensaje informativo al cliente.
En la vista buy, incluiremos un botón para mostrar el Checkout Overlay. El componente Blade paddle-button se incluye con Cashier Paddle; sin embargo, también puede renderizar manualmente un checkout overlay:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy Product
</x-paddle-button>
#Proporcionar Meta Datos al Checkout de Paddle
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 al Checkout Overlay de Paddle 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 datos personalizados 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',
]);
$checkout = $request->user()->checkout($order->price_ids)
->customData(['order_id' => $order->id]);
return view('billing', ['checkout' => $checkout]);
})->name('checkout');
Como puede ver en el ejemplo anterior, cuando un usuario inicia el proceso de checkout, proporcionamos todos los identificadores de precio de Paddle 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 al Checkout Overlay de Paddle mediante el método customData.
Por supuesto, probablemente querrá marcar el pedido como "completado" una vez que el cliente haya terminado el proceso de checkout. Para lograr esto, puede escuchar los webhooks enviados por Paddle y disparados mediante eventos por Cashier para almacenar la información del pedido en su base de datos.
Para comenzar, escuche el evento TransactionCompleted despachado por Cashier. Normalmente, debe registrar el listener del evento en el método boot de uno de los service providers de su aplicación:
use App\Listeners\CompleteOrder;
use Illuminate\Support\Facades\Event;
use Laravel\Paddle\Events\TransactionCompleted;
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Event::listen(TransactionCompleted::class, CompleteOrder::class);
}
En este ejemplo, el listener CompleteOrder podría verse así:
namespace App\Listeners;
use App\Models\Order;
use Laravel\Cashier\Cashier;
use Laravel\Cashier\Events\TransactionCompleted;
class CompleteOrder
{
/**
* Manejar el evento webhook entrante de Cashier.
*/
public function handle(TransactionCompleted $event): void
{
$orderId = $event->payload['data']['custom_data']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
}
}
Consulte la documentación de Paddle para más información sobre los datos contenidos en el evento transaction.completed.
#Venta de Suscripciones
Antes de utilizar Paddle Checkout, debe definir Productos con precios fijos en su panel de Paddle. Además, debe configurar el manejo de webhooks de Paddle.
Ofrecer facturación de productos y suscripciones a través de su aplicación puede ser intimidante. Sin embargo, gracias a Cashier y al Checkout Overlay de Paddle, puede construir fácilmente integraciones de pago modernas y robustas.
Para aprender cómo vender suscripciones usando Cashier y el Checkout Overlay de Paddle, 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 Paddle. 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 invocará un Checkout Overlay de Paddle para el plan elegido. Para comenzar, iniciemos una sesión de checkout mediante el método checkout:
use Illuminate\Http\Request;
Route::get('/subscribe', function (Request $request) {
$checkout = $request->user()->checkout('price_basic_monthly')
->returnTo(route('dashboard'));
return view('subscribe', ['checkout' => $checkout]);
})->name('subscribe');
En la vista subscribe, incluiremos un botón para mostrar el Checkout Overlay. El componente Blade paddle-button se incluye con Cashier Paddle; sin embargo, también puede renderizar manualmente un checkout overlay:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Ahora, cuando se haga clic en el botón Suscribirse, el cliente podrá ingresar sus datos de pago e iniciar su suscripción. Para saber cuándo su suscripción realmente ha comenzado (ya que algunos métodos de pago requieren unos segundos para procesarse), también debe 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 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
#Creando un Middleware para Usuarios 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('/subscribe');
}
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". En nuestro ejemplo anterior, querríamos permitir que el cliente cambie su plan de una suscripción mensual a una anual. Para esto, deberá implementar algo como un botón que dirija a la siguiente ruta:
use Illuminate\Http\Request;
Route::put('/subscription/{price}/swap', function (Request $request, $price) {
$user->subscription()->swap($price); // Con "$price" siendo "price_basic_yearly" en este ejemplo.
return redirect()->route('dashboard');
})->name('subscription.swap');
Además de cambiar planes, también deberá permitir que sus clientes cancelen su suscripción. Al igual que con el cambio de planes, proporcione un botón que dirija a la siguiente ruta:
use Illuminate\Http\Request;
Route::put('/subscription/cancel', function (Request $request, $price) {
$user->subscription()->cancel();
return redirect()->route('dashboard');
})->name('subscription.cancel');
Y ahora su suscripción se cancelará al final de su período de facturación.
Siempre que 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 Paddle. Por ejemplo, cuando cancele la suscripción de un cliente desde el panel de Paddle, Cashier recibirá el webhook correspondiente y marcará la suscripción como "cancelada" en la base de datos de su aplicación.
#Sesiones de Pago
La mayoría de las operaciones para facturar a los clientes se realizan usando "checkouts" mediante el widget Checkout Overlay de Paddle o utilizando el checkout inline.
Antes de procesar pagos con checkout usando Paddle, debe definir el enlace de pago predeterminado de su aplicación en el panel de configuración de checkout de Paddle.
#Checkout Overlay
Antes de mostrar el widget Checkout Overlay, debe generar una sesión de checkout usando Cashier. Una sesión de checkout informará al widget de checkout sobre la operación de facturación que debe realizarse:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Cashier incluye un componente Blade paddle-button. Puede pasar la sesión de checkout a este componente como una "propiedad". Luego, cuando se haga clic en este botón, se mostrará el widget de checkout de Paddle:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Por defecto, esto mostrará el widget usando el estilo predeterminado de Paddle. Puede personalizar el widget agregando atributos soportados por Paddle como el atributo data-theme='light' al componente:
<x-paddle-button :url="$payLink" class="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>
El widget de checkout de Paddle es asíncrono. Una vez que el usuario crea una suscripción dentro del widget, Paddle enviará un webhook a su aplicación para que pueda actualizar correctamente el estado de la suscripción en la base de datos de su aplicación. Por lo tanto, es importante que configure correctamente los webhooks para manejar los cambios de estado desde Paddle.
Después de un cambio de estado de suscripción, el retraso para recibir el webhook correspondiente suele ser mínimo, pero debe tenerlo en cuenta en su aplicación considerando que la suscripción del usuario podría no estar disponible inmediatamente después de completar el checkout.
#Renderizar Manualmente un Checkout Overlay
También puede renderizar manualmente un checkout overlay sin usar los componentes Blade integrados de Laravel. Para comenzar, genere la sesión de checkout como se mostró en ejemplos anteriores:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Luego, puede usar Paddle.js para inicializar el checkout. En este ejemplo, crearemos un enlace que tiene asignada la clase paddle_button. Paddle.js detectará esta clase y mostrará el checkout overlay cuando se haga clic en el enlace:
<?php
$items = $checkout->getItems();
$customer = $checkout->getCustomer();
$custom = $checkout->getCustomData();
?>
<a
href='#!'
class='paddle_button'
data-items='{!! json_encode($items) !!}'
@if ($customer) data-customer-id='{{ $customer->paddle_id }}' @endif
@if ($custom) data-custom-data='{{ json_encode($custom) }}' @endif
@if ($returnUrl = $checkout->getReturnUrl()) data-success-url='{{ $returnUrl }}' @endif
>
Buy Product
</a>
#Checkout Inline
Si no desea usar el widget de checkout estilo "overlay" de Paddle, Paddle también ofrece la opción de mostrar el widget inline. Aunque este enfoque no permite ajustar ninguno de los campos HTML del checkout, permite incrustar el widget dentro de su aplicación.
Para facilitarle el inicio con el checkout inline, Cashier incluye un componente Blade paddle-checkout. Para comenzar, debe generar una sesión de checkout:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Luego, puede pasar la sesión de checkout al atributo checkout del componente:
<x-paddle-checkout :checkout="$checkout" class="w-full" />
Para ajustar la altura del componente de checkout inline, puede pasar el atributo height al componente Blade:
<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />
Consulte la guía de Paddle sobre Inline Checkout y las configuraciones disponibles para checkout para más detalles sobre las opciones de personalización del checkout inline.
#Renderizar Manualmente un Checkout Inline
También puede renderizar manualmente un checkout inline sin usar los componentes Blade integrados de Laravel. Para comenzar, genere la sesión de checkout como se mostró en ejemplos anteriores:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Luego, puede usar Paddle.js para inicializar el checkout. En este ejemplo, lo demostraremos usando Alpine.js; sin embargo, puede modificar este ejemplo para su propia pila frontend:
<?php
$options = $checkout->options();
$options['settings']['frameTarget'] = 'paddle-checkout';
$options['settings']['frameInitialHeight'] = 366;
?>
<div class="paddle-checkout" x-data="{}" x-init="
Paddle.Checkout.open(@json($options));
">
</div>
#Checkout para Invitados
A veces, puede necesitar crear una sesión de checkout para usuarios que no requieren una cuenta en su aplicación. Para ello, puede usar el método guest:
use Illuminate\Http\Request;
use Laravel\Paddle\Checkout;
Route::get('/buy', function (Request $request) {
$checkout = Checkout::guest('pri_34567')
->returnTo(route('home'));
return view('billing', ['checkout' => $checkout]);
});
Luego, puede proporcionar la sesión de checkout a los componentes Blade de botón Paddle o checkout inline.
#Previsualización de Precios
Paddle le permite personalizar los precios por moneda, lo que esencialmente le permite configurar diferentes precios para distintos países. Cashier Paddle le permite recuperar todos estos precios usando el método previewPrices. Este método acepta los IDs de precio para los cuales desea obtener los precios:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_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(['pri_123', 'pri_456'], ['address' => [
'country_code' => 'BE',
'postal_code' => '1234',
]]);
Después de recuperar los precios, puede mostrarlos como desee:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
También puede mostrar el subtotal y el monto del impuesto por separado:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product_title }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
@endforeach
</ul>
Para más información, consulte la documentación de la API de Paddle sobre vistas previas de precios.
#Vistas Previas de Precios para 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)->previewPrices(['pri_123', 'pri_456']);
Internamente, Cashier usará el ID de cliente del usuario para recuperar los precios en su moneda. Por ejemplo, un usuario que vive en Estados Unidos verá los precios en dólares estadounidenses, mientras que un usuario en Bélgica verá los precios en euros. Si no se encuentra una moneda coincidente, se usará 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.
#Descuentos
También puede optar por mostrar los precios después de aplicar un descuento. Al llamar al método previewPrices, debe proporcionar el ID del descuento mediante la opción discount_id:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
'discount_id' => 'dsc_123'
]);
Luego, muestre los precios calculados:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
#Clientes
#Valores Predeterminados para Clientes
Cashier le permite definir algunos valores predeterminados útiles para sus clientes al crear sesiones de checkout. Establecer estos valores predeterminados le permite rellenar previamente el correo electrónico y el nombre del cliente para que puedan avanzar inmediatamente a la parte de pago del widget de checkout. Puede establecer estos valores predeterminados sobrescribiendo los siguientes métodos en su modelo facturable:
/**
* Obtener el nombre del cliente para asociarlo con Paddle.
*/
public function paddleName(): string|null
{
return $this->name;
}
/**
* Obtener el correo electrónico del cliente para asociarlo con Paddle.
*/
public function paddleEmail(): string|null
{
return $this->email;
}
Estos valores predeterminados se usarán para cada acción en Cashier que genere una sesión de checkout.
#Recuperar Clientes
Puede recuperar un cliente por su ID de cliente de Paddle usando el método Cashier::findBillable. Este método devolverá una instancia del modelo facturable:
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($customerId);
#Crear Clientes
Ocasionalmente, puede que desee crear un cliente en Paddle sin iniciar una suscripción. Puede lograr esto usando el método createAsCustomer:
$customer = $user->createAsCustomer();
Se devuelve una instancia de Laravel\Paddle\Customer. Una vez que el cliente ha sido creado en Paddle, 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 Paddle:
$customer = $user->createAsCustomer($options);
#Suscripciones
#Crear Suscripciones
Para crear una suscripción, primero recupere una instancia de su modelo facturable desde su base de datos, que normalmente será una instancia de App\Models\User. Una vez que tenga la instancia del modelo, puede usar el método subscribe para crear la sesión de checkout del modelo:
use Illuminate\Http\Request;
Route::get('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe($premium = 12345, 'default')
->returnTo(route('home'));
return view('billing', ['checkout' => $checkout]);
});
El primer argumento dado al método subscribe es el precio específico al que el usuario se está suscribiendo. Este valor debe corresponder al identificador del precio en Paddle. El método returnTo acepta una URL a la que su usuario será redirigido después de completar exitosamente el checkout. El segundo argumento pasado al método subscribe debe ser el "tipo" interno de la suscripción. Si su aplicación solo ofrece una suscripción, podría llamarlo 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.
También puede proporcionar un arreglo de metadatos personalizados sobre la suscripción usando el método customData:
$checkout = $request->user()->subscribe($premium = 12345, 'default')
->customData(['key' => 'value'])
->returnTo(route('home'));
Una vez que se ha creado una sesión de checkout para la suscripción, esta sesión puede ser proporcionada al componente Blade paddle-button que se incluye con Cashier Paddle:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Después de que el usuario haya terminado su checkout, Paddle enviará un webhook subscription_created. Cashier recibirá este webhook y configurará la suscripción para su cliente. Para asegurarse de que todos los webhooks sean recibidos y manejados correctamente por su aplicación, asegúrese de haber configurado adecuadamente el manejo de webhooks.
#Verificar el Estado de la Suscripción
Una vez que un usuario está suscrito a su aplicación, puede verificar el estado de su suscripción usando varios métodos convenientes. Primero, el método subscribed devuelve true si el usuario tiene una suscripción válida, incluso si la suscripción está actualmente en su período de prueba:
if ($user->subscribed()) {
// ...
}
Si su aplicación ofrece múltiples suscripciones, puede especificar la suscripción al invocar el método subscribed:
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()) {
// 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()->onTrial()) {
// ...
}
El método subscribedToPrice puede usarse para determinar si el usuario está suscrito a un plan dado basado en un ID de precio de Paddle. En este ejemplo, determinaremos si la suscripción default del usuario está activamente suscrita al precio mensual:
if ($user->subscribedToPrice($monthly = 'pri_123', 'default')) {
// ...
}
El método recurring puede usarse para determinar si el usuario está actualmente en una suscripción activa y ya no está en su período de prueba ni en un período de gracia:
if ($user->subscription()->recurring()) {
// ...
}
#Estado de Suscripción Cancelada
Para determinar si el usuario fue alguna vez un suscriptor activo pero canceló su suscripción, puede usar el método canceled:
if ($user->subscription()->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 está en su "período de gracia" hasta el 10 de marzo. Además, el método subscribed seguirá devolviendo true durante este tiempo:
if ($user->subscription()->onGracePeriod()) {
// ...
}
#Estado de Pago Vencido
Si un pago falla para una suscripción, esta será marcada como past_due. Cuando su suscripción está en este estado, no estará activa hasta que el cliente actualice su información de pago. Puede determinar si una suscripción está vencida usando el método pastDue en la instancia de la suscripción:
if ($user->subscription()->pastDue()) {
// ...
}
Cuando una suscripción está vencida, debe indicar al usuario que actualice su información de pago.
Si desea que las suscripciones sigan considerándose válidas cuando están en estado past_due, puede usar el método keepPastDueSubscriptionsActive proporcionado por Cashier. Normalmente, este método debe llamarse en el método register de su AppServiceProvider:
use Laravel\Paddle\Cashier;
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
}
Cuando una suscripción está en estado past_due, no puede ser modificada hasta que la información de pago haya sido actualizada. Por lo tanto, los métodos swap y updateQuantity lanzarán una excepción cuando la suscripción esté en estado past_due.
#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 dado:
// Obtener todas las suscripciones válidas...
$subscriptions = Subscription::query()->valid()->get();
// Obtener todas las suscripciones canceladas de un usuario...
$subscriptions = $user->subscriptions()->canceled()->get();
Una lista completa de scopes disponibles está a continuación:
Subscription::query()->valid();
Subscription::query()->onTrial();
Subscription::query()->expiredTrial();
Subscription::query()->notOnTrial();
Subscription::query()->active();
Subscription::query()->recurring();
Subscription::query()->pastDue();
Subscription::query()->paused();
Subscription::query()->notPaused();
Subscription::query()->onPausedGracePeriod();
Subscription::query()->notOnPausedGracePeriod();
Subscription::query()->canceled();
Subscription::query()->notCanceled();
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();
#Cargos Únicos en Suscripciones
Los cargos únicos en suscripciones le permiten cobrar a los suscriptores un cargo único adicional además de sus suscripciones. Debe proporcionar uno o varios IDs de precio al invocar el método charge:
// Cobrar un solo precio...
$response = $user->subscription()->charge('pri_123');
// Cobrar múltiples precios a la vez...
$response = $user->subscription()->charge(['pri_123', 'pri_456']);
El método charge no cobrará realmente al cliente hasta el próximo intervalo de facturación de su suscripción. Si desea facturar al cliente inmediatamente, puede usar el método chargeAndInvoice en su lugar:
$response = $user->subscription()->chargeAndInvoice('pri_123');
#Actualizar Información de Pago
Paddle siempre guarda un método de pago por suscripción. Si desea actualizar el método de pago predeterminado para una suscripción, debe redirigir a su cliente a la página alojada por Paddle para actualizar el método de pago usando el método redirectToUpdatePaymentMethod en el modelo de suscripción:
use Illuminate\Http\Request;
Route::get('/update-payment-method', function (Request $request) {
$user = $request->user();
return $user->subscription()->redirectToUpdatePaymentMethod();
});
Cuando un usuario termina de actualizar su información, Paddle enviará un webhook subscription_updated y los detalles de la suscripción se actualizarán en la base de datos de su aplicación.
#Cambiar Planes
Después de que un usuario se ha suscrito a su aplicación, puede que ocasionalmente quiera cambiar a un nuevo plan de suscripción. Para actualizar el plan de suscripción de un usuario, debe pasar el identificador del precio de Paddle al método swap de la suscripción:
use App\Models\User;
$user = User::find(1);
$user->subscription()->swap($premium = 'pri_456');
Si desea cambiar de plan y facturar inmediatamente al usuario en lugar de esperar a su próximo ciclo de facturación, puede usar el método swapAndInvoice:
$user = User::find(1);
$user->subscription()->swapAndInvoice($premium = 'pri_456');
#Prorrateos
Por defecto, Paddle prorratea los cargos al cambiar entre planes. El método noProrate puede usarse para actualizar las suscripciones sin prorratear los cargos:
$user->subscription('default')->noProrate()->swap($premium = 'pri_456');
Si desea desactivar el prorrateo y facturar a los clientes inmediatamente, puede usar el método swapAndInvoice en combinación con noProrate:
$user->subscription('default')->noProrate()->swapAndInvoice($premium = 'pri_456');
O, para no facturar a su cliente por un cambio de suscripción, puede utilizar el método doNotBill:
$user->subscription('default')->doNotBill()->swap($premium = 'pri_456');
Para más información sobre las políticas de prorrateo de Paddle, consulte la documentación de prorrateo de Paddle.
#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. Para incrementar o decrementar fácilmente la cantidad de su suscripción, use los métodos incrementQuantity y decrementQuantity:
$user = User::find(1);
$user->subscription()->incrementQuantity();
// Añadir cinco a la cantidad actual de la suscripción...
$user->subscription()->incrementQuantity(5);
$user->subscription()->decrementQuantity();
// Restar cinco a la cantidad actual de la suscripción...
$user->subscription()->decrementQuantity(5);
Alternativamente, puede establecer una cantidad específica usando el método updateQuantity:
$user->subscription()->updateQuantity(10);
El método noProrate puede usarse para actualizar la cantidad de la suscripción sin prorratear los cargos:
$user->subscription()->noProrate()->updateQuantity(10);
#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()->incrementQuantity(1, 'price_chat');
#Suscripciones con Múltiples Productos
Las suscripciones con múltiples productos le permiten asignar múltiples productos de facturación 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.
Al crear sesiones de checkout para suscripciones, puede especificar múltiples productos para una suscripción dada pasando un arreglo de precios como primer argumento al método subscribe:
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe([
'price_monthly',
'price_chat',
]);
return view('billing', ['checkout' => $checkout]);
});
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 pasar un arreglo asociativo de pares clave / valor para indicar una cantidad específica para cada precio:
$user = User::find(1);
$checkout = $user->subscribe('default', ['price_monthly', 'price_chat' => 5]);
Si desea agregar otro precio a una suscripción existente, debe usar el método swap de la suscripción. Al invocar el método swap, también debe incluir los precios y cantidades actuales de la suscripción:
$user = User::find(1);
$user->subscription()->swap(['price_chat', 'price_original' => 2]);
El ejemplo anterior agregará el nuevo precio, pero el cliente no será facturado por él hasta su próximo ciclo de facturación. Si desea facturar al cliente inmediatamente, puede usar el método swapAndInvoice:
$user->subscription()->swapAndInvoice(['price_chat', 'price_original' => 2]);
Puede eliminar precios de suscripciones usando el método swap y omitiendo el precio que desea eliminar:
$user->subscription()->swap(['price_original' => 2]);
No puede eliminar el último precio de una suscripción. En su lugar, debe cancelar la suscripción.
#Múltiples Suscripciones
Paddle 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 suscripción 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 subscribe como segundo argumento. 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) {
$checkout = $request->user()->subscribe($swimmingMonthly = 'pri_123', 'swimming');
return view('billing', ['checkout' => $checkout]);
});
En este ejemplo, iniciamos una suscripción mensual de natación para el cliente. Sin embargo, puede que quieran cambiar a una suscripción anual en un momento posterior. Al ajustar la suscripción del cliente, simplemente podemos cambiar el precio en la suscripción swimming:
$user->subscription('swimming')->swap($swimmingYearly = 'pri_456');
Por supuesto, también puede cancelar la suscripción por completo:
$user->subscription('swimming')->cancel();
#Pausar Suscripciones
Para pausar una suscripción, llame al método pause en la suscripción del usuario:
$user->subscription()->pause();
Cuando una suscripción está pausada, Cashier establecerá automáticamente la columna paused_at en su base de datos. Esta columna se usa para determinar cuándo el método paused debe 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 seguirá devolviendo false hasta el 5 de marzo. Esto se debe a que normalmente se permite que un usuario continúe usando una aplicación hasta el final de su ciclo de facturación.
Por defecto, la pausa ocurre en el próximo intervalo de facturación para que el cliente pueda usar el resto del período que pagó. Si desea pausar una suscripción inmediatamente, puede usar el método pauseNow:
$user->subscription()->pauseNow();
Usando el método pauseUntil, puede pausar la suscripción hasta un momento específico en el tiempo:
$user->subscription()->pauseUntil(now()->addMonth());
O puede usar el método pauseNowUntil para pausar inmediatamente la suscripción hasta un punto dado en el tiempo:
$user->subscription()->pauseNowUntil(now()->addMonth());
Puede determinar si un usuario ha pausado su suscripción pero aún está en su "período de gracia" usando el método onPausedGracePeriod:
if ($user->subscription()->onPausedGracePeriod()) {
// ...
}
Para reanudar una suscripción pausada, puede invocar el método resume en la suscripción:
$user->subscription()->resume();
Una suscripción no puede ser modificada mientras está pausada. Si desea cambiar a un plan diferente o actualizar cantidades, primero debe reanudar la suscripción.
#Cancelar Suscripciones
Para cancelar una suscripción, llame al método cancel en la suscripción del usuario:
$user->subscription()->cancel();
Cuando una suscripción es cancelada, Cashier establecerá automáticamente la columna ends_at en su base de datos. Esta columna se usa para determinar 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 seguirá 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()->onGracePeriod()) {
// ...
}
Si desea cancelar una suscripción inmediatamente, puede llamar al método cancelNow en la suscripción:
$user->subscription()->cancelNow();
Para detener la cancelación de una suscripción que está en su período de gracia, puede invocar el método stopCancelation:
$user->subscription()->stopCancelation();
Las suscripciones de Paddle no pueden reanudarse después de la cancelación. Si su cliente desea reanudar su suscripción, deberá crear una nueva suscripción.
#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 establecer un tiempo de prueba en el panel de Paddle para el precio al que su cliente se está suscribiendo. Luego, inicie la sesión de checkout normalmente:
use Illuminate\Http\Request;
Route::get('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe('pri_monthly')
->returnTo(route('home'));
return view('billing', ['checkout' => $checkout]);
});
Cuando su aplicación recibe el evento subscription_created, Cashier establecerá la fecha de finalización del período de prueba en el registro de la suscripción dentro de la base de datos de su aplicación, así como indicará a Paddle que no comience a facturar al cliente hasta después de esta fecha.
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.
Puede determinar si el 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()) {
// ...
}
if ($user->subscription()->onTrial()) {
// ...
}
Para determinar si un período de prueba existente ha expirado, puede usar los métodos hasExpiredTrial:
if ($user->hasExpiredTrial()) {
// ...
}
if ($user->subscription()->hasExpiredTrial()) {
// ...
}
Para determinar si un usuario está en período de prueba para un tipo de suscripción específico, puede proporcionar el tipo a los métodos onTrial o hasExpiredTrial:
if ($user->onTrial('default')) {
// ...
}
if ($user->hasExpiredTrial('default')) {
// ...
}
#Sin Método de Pago 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 cliente asociado a su usuario con la fecha de finalización de la prueba que desee. Esto se suele hacer 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é listo para crear una suscripción real para el usuario, puede usar el método subscribe como de costumbre:
use Illuminate\Http\Request;
Route::get('/user/subscribe', function (Request $request) {
$checkout = $user->subscribe('pri_monthly')
->returnTo(route('home'));
return view('billing', ['checkout' => $checkout]);
});
Para obtener la fecha de finalización de la 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('default')) {
$trialEndsAt = $user->trialEndsAt();
}
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 o Activar una Prueba
Puede extender un período de prueba existente en una suscripción invocando el método extendTrial y especificando el momento en que debe finalizar la prueba:
$user->subsription()->extendTrial(now()->addDays(5));
O bien, puede activar inmediatamente una suscripción finalizando su prueba llamando al método activate en la suscripción:
$user->subscription()->activate();
#Manejo de Webhooks de Paddle
Paddle 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 es registrada por el proveedor de servicios de Cashier. Este controlador manejará todas las solicitudes entrantes de webhooks.
Por defecto, este controlador manejará automáticamente la cancelación de suscripciones con demasiados cargos fallidos, actualizaciones de suscripciones y cambios en el método de pago; sin embargo, como veremos pronto, puede extender este controlador para manejar cualquier evento de webhook de Paddle que desee.
Para asegurarse de que su aplicación pueda manejar los webhooks de Paddle, asegúrese 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 debe habilitar en el panel de control de Paddle es:
- Cliente actualizado
- Transacción completada
- Transacción actualizada
- Suscripción creada
- Suscripción actualizada
- Suscripción pausada
- Suscripción cancelada
Asegúrese de proteger las solicitudes entrantes con el middleware incluido de verificación de firma de webhook de Cashier.
#Webhooks y Protección CSRF
Dado que los webhooks de Paddle deben evitar la protección CSRF de Laravel, asegúrese de listar la URI como excepción en su middleware App\Http\Middleware\VerifyCsrfToken o de listar la ruta fuera del grupo de middleware web:
protected $except = [
'paddle/*',
];
#Webhooks y Desarrollo Local
Para que Paddle pueda enviar webhooks a su aplicación durante el desarrollo local, necesitará exponer su aplicación mediante un servicio de compartición de sitios como Ngrok o Expose. Si está desarrollando su aplicación localmente usando Laravel Sail, puede usar el comando de compartición de sitio de Sail.
#Definiendo Manejadores de Eventos de Webhook
Cashier maneja automáticamente la cancelación de suscripciones por cargos fallidos y otros webhooks comunes de Paddle. Sin embargo, si tiene eventos adicionales de webhook que desea manejar, puede hacerlo escuchando los siguientes eventos que Cashier despacha:
Laravel\Paddle\Events\WebhookReceivedLaravel\Paddle\Events\WebhookHandled
Ambos eventos contienen la carga completa del webhook de Paddle. Por ejemplo, si desea manejar el webhook transaction_billed, puede registrar un listener que maneje el evento:
<?php
namespace App\Listeners;
use Laravel\Paddle\Events\WebhookReceived;
class PaddleEventListener
{
/**
* Manejar webhooks recibidos de Paddle.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['alert_name'] === 'transaction_billed') {
// 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\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 de la carga completa de Paddle, también contienen los modelos relevantes que se usaron para procesar el webhook, como el modelo billable, la suscripción o el recibo:
Laravel\Paddle\Events\CustomerUpdatedLaravel\Paddle\Events\TransactionCompletedLaravel\Paddle\Events\TransactionUpdatedLaravel\Paddle\Events\SubscriptionCreatedLaravel\Paddle\Events\SubscriptionUpdatedLaravel\Paddle\Events\SubscriptionPausedLaravel\Paddle\Events\SubscriptionCanceled
También puede sobrescribir la ruta de webhook predeterminada incorporada definiendo la variable de entorno CASHIER_WEBHOOK en el archivo .env de su aplicación. Este valor debe ser la URL completa de su ruta de webhook y debe coincidir con la URL configurada en el panel de control de Paddle:
CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url
#Verificación de Firmas de Webhook
Para asegurar sus webhooks, puede usar las firmas de webhook de Paddle. Para mayor comodidad, Cashier incluye automáticamente un middleware que valida que la solicitud entrante del webhook de Paddle sea válida.
Para habilitar la verificación de webhook, asegúrese de que la variable de entorno PADDLE_WEBHOOK_SECRET esté definida en el archivo .env de su aplicación. El secreto del webhook puede obtenerse desde el panel de control de su cuenta Paddle.
#Cargos Únicos
#Cobrar por Productos
Si desea iniciar una compra de producto para un cliente, puede usar el método checkout en una instancia del modelo billable para generar una sesión de checkout para la compra. El método checkout acepta uno o varios IDs de precio. Si es necesario, puede usar un array asociativo para proporcionar la cantidad del producto que se está comprando:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $request->user()->checkout(['pri_tshirt', 'pri_socks' => 5]);
return view('buy', ['checkout' => $checkout]);
});
Después de generar la sesión de checkout, puede usar el componente Blade paddle-button proporcionado por Cashier para permitir que el usuario vea el widget de checkout de Paddle y complete la compra:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy
</x-paddle-button>
Una sesión de checkout tiene un método customData, que le permite pasar cualquier dato personalizado que desee a la creación subyacente de la transacción. Por favor, consulte la documentación de Paddle para aprender más sobre las opciones disponibles al pasar datos personalizados:
$checkout = $user->checkout('pri_tshirt')
->customData([
'custom_option' => $value,
]);
#Reembolsar Transacciones
Reembolsar transacciones devolverá el monto reembolsado al método de pago de su cliente que se usó en el momento de la compra. Si necesita reembolsar una compra de Paddle, puede usar el método refund en un modelo Cashier\Paddle\Transaction. Este método acepta una razón como primer argumento, uno o más IDs de precio para reembolsar con montos opcionales como un array asociativo. Puede obtener las transacciones para un modelo billable dado usando el método transactions.
Por ejemplo, imagine que queremos reembolsar una transacción específica para los precios pri_123 y pri_456. Queremos reembolsar completamente pri_123, pero solo reembolsar dos dólares para pri_456:
use App\Models\User;
$user = User::find(1);
$transaction = $user->transactions()->first();
$response = $transaction->refund('Accidental charge', [
'pri_123', // Reembolsar completamente este precio...
'pri_456' => 200, // Solo reembolsar parcialmente este precio...
]);
El ejemplo anterior reembolsa ítems específicos en una transacción. Si desea reembolsar toda la transacción, simplemente proporcione una razón:
$response = $transaction->refund('Accidental charge');
Para más información sobre reembolsos, por favor consulte la documentación de reembolsos de Paddle.
Los reembolsos siempre deben ser aprobados por Paddle antes de procesarse completamente.
#Acreditar Transacciones
Al igual que los reembolsos, también puede acreditar transacciones. Acreditar transacciones añadirá fondos al saldo del cliente para que puedan usarse en compras futuras. Acreditar transacciones solo puede hacerse para transacciones cobradas manualmente y no para transacciones cobradas automáticamente (como suscripciones), ya que Paddle maneja automáticamente los créditos de suscripciones:
$transaction = $user->transactions()->first();
// Acreditar completamente un ítem específico...
$response = $transaction->credit('Compensation', 'pri_123');
Para más información, consulte la documentación de Paddle sobre acreditaciones.
Los créditos solo pueden aplicarse a transacciones cobradas manualmente. Las transacciones cobradas automáticamente son acreditadas por Paddle directamente.
#Transacciones
Puede obtener fácilmente un array de las transacciones de un modelo billable a través de la propiedad transactions:
use App\Models\User;
$user = User::find(1);
$transactions = $user->transactions;
Las transacciones representan pagos por sus productos y compras y van acompañadas de facturas. Solo las transacciones completadas se almacenan en la base de datos de su aplicación.
Al listar las transacciones de un cliente, puede usar los métodos de la instancia de transacción para mostrar la información de pago relevante. Por ejemplo, puede listar cada transacción en una tabla, permitiendo que el usuario descargue fácilmente cualquiera de las facturas:
<table>
@foreach ($transactions as $transaction)
<tr>
<td>{{ $transaction->billed_at->toFormattedDateString() }}</td>
<td>{{ $transaction->total() }}</td>
<td>{{ $transaction->tax() }}</td>
<td><a href="{{ route('download-invoice', $transaction->id) }}" target="_blank">Download</a></td>
</tr>
@endforeach
</table>
La ruta download-invoice podría verse así:
use Illuminate\Http\Request;
use Laravel\Cashier\Transaction;
Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) {
return $transaction->redirectToInvoicePdf();
})->name('download-invoice');
#Pagos Pasados y Próximos
Puede usar los métodos lastPayment y nextPayment para obtener y mostrar los pagos pasados o próximos de un cliente para suscripciones recurrentes:
use App\Models\User;
$user = User::find(1);
$subscription = $user->subscription();
$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();
Ambos métodos devolverán una instancia de Laravel\Paddle\Payment; sin embargo, lastPayment devolverá null cuando las transacciones no hayan sido sincronizadas aún por webhooks, mientras que nextPayment devolverá null cuando el ciclo de facturación haya terminado (como cuando una suscripción ha sido cancelada):
Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}
#Pruebas
Durante las pruebas, debe probar manualmente su flujo de facturación para asegurarse de que su integración funciona como se espera.
Para pruebas automatizadas, incluyendo aquellas ejecutadas en un entorno CI, puede usar el HTTP Client de Laravel para simular llamadas HTTP hechas a Paddle. Aunque esto no prueba las respuestas reales de Paddle, proporciona una forma de probar su aplicación sin llamar realmente a la API de Paddle.