laravel/cashier-paddle
Laravel Cashier Paddle adds a fluent Laravel interface for Paddle subscriptions, handling common billing boilerplate like subscription creation and management, plan swaps, quantities, pauses, cancellations, and grace periods.
Installation:
composer require laravel/cashier-paddle
Publish the config file:
php artisan vendor:publish --provider="Laravel\CashierPaddle\CashierPaddleServiceProvider"
Configuration:
.env:
PADDLE_VENDOR_ID=your_vendor_id
PADDLE_VENDOR_AUTH_CODE=your_auth_code
PADDLE_WEBHOOK_SECRET=your_webhook_secret
config/cashier-paddle.php (e.g., webhook URL, default currency).First Use Case:
use Laravel\CashierPaddle\Facades\CashierPaddle;
$user->newSubscription('main', 'product_id')
->create($paymentMethodId);
https://your-app.com/paddle-webhook) is registered in Paddle’s developer portal.Subscription Management:
$user->newSubscription('main', 'product_id')
->quantity(2) // For metered billing
->price('custom_price_id') // Override default
->create($paymentMethodId);
$user->subscription('main')->swap('new_product_id');
$user->subscription('main')->pause();
$user->subscription('main')->resume();
Webhooks:
routes/web.php:
Route::post('/paddle-webhook', [PaddleWebhookController::class, 'handle']);
PaddleWebhookController:
use Laravel\CashierPaddle\Events\WebhookReceived;
public function handle(Request $request) {
if (!WebhookReceived::validate($request)) {
abort(403);
}
// Process event...
}
Billing Portal:
return $user->subscription('main')->billable()->paddlePortal();
Invoices & Payments:
$user->invoices()->latest()->get();
$user->subscription('main')->cancel();
php artisan paddle:sync to reconcile local subscriptions with Paddle’s data..env) and mock webhooks:
$this->post('/paddle-webhook', $payload)
->assertOk();
$user->subscription('main')->updateCustomFields(['tier' => 'premium']);
Webhook Validation:
WebhookReceived::validate()). Paddle sends events with a Paddle-Signature header.\Log::debug('Webhook payload:', $request->all());
Idempotency:
event.data.object.id before processing).Subscription States:
active, canceled, paused). Avoid assuming local DB state matches Paddle’s—always fetch fresh data via:
$user->subscription('main')->fresh();
Quantities & Add-ons:
quantity matches Paddle’s product setup. Mismatches may cause failed charges.Webhook URL:
charge.failed events. Retry with:
$user->subscription('main')->resume();
USD. Override per subscription:
->price('price_id')->currency('EUR')
subscription.created):
use Laravel\CashierPaddle\Events\SubscriptionCreated;
event(new SubscriptionCreated($user, $subscription));
Route::middleware(['auth', 'subscribed'])->group(function () {
// Protected routes
});
Paddle facade:
use Laravel\CashierPaddle\Facades\Paddle;
$customers = Paddle::customers()->all();
How can I help you explore Laravel packages today?