Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel Cashier Paddle
https://github.com/laravel/cashier-paddle
Admin
Laravel Cashier Paddle is a PHP library that integrates Laravel with Paddle's subscription billing,
...
Tokens:
5,450
Snippets:
22
Trust Score:
9.4
Update:
4 months ago
Context
Skills
Chat
Benchmark
90.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel Cashier Paddle Laravel Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading writing, including subscription management, swapping plans, subscription quantities, subscription pausing, cancellation grace periods, and much more. This package integrates seamlessly with Laravel's Eloquent ORM and provides robust webhook handling for automatic synchronization with Paddle's billing system. Built on top of Paddle's API, Cashier simplifies the process of managing recurring billing in Laravel applications. It provides a consistent, Laravel-style API for creating subscriptions, processing one-time charges, managing trials, handling refunds, and tracking transaction history. The package includes Blade components for embedding checkout experiences, comprehensive event system for reacting to billing changes, and full support for multi-item subscriptions with proration handling. ## Adding the Billable Trait to Your Model Enable billing functionality on any Eloquent model Add the `Billable` trait to your User model (or any billable entity) to unlock all subscription and payment features. ```php use Laravel\Paddle\Billable; class User extends Authenticatable { use Billable; } // Create a customer in Paddle $user = User::find(1); $customer = $user->createAsCustomer([ 'trial_ends_at' => now()->addDays(14), ]); // Check subscription status if ($user->subscribed('premium')) { // User has active premium subscription } if ($user->onTrial('basic')) { // User is in trial for basic plan } // Get all transactions foreach ($user->transactions as $transaction) { echo $transaction->total(); // "$29.99" echo $transaction->invoicePdf(); // PDF URL } ``` ## Creating a New Subscription Start a new subscription with a checkout session Create a subscription checkout for a customer with a specific price ID from your Paddle dashboard. ```php use App\Models\User; $user = User::find(1); // Simple subscription to a single price $checkout = $user->subscribe('price_1234567890abc') ->returnTo(route('subscriptions.success')); // Subscription with custom type $checkout = $user->subscribe('price_premium_monthly', 'premium') ->returnTo(route('subscriptions.success')); // Multi-price subscription $checkout = $user->checkout([ 'price_basic_monthly' => 1, 'price_addon_storage' => 3, ])->returnTo(route('success')); // Render checkout in Blade // In controller: return view('subscribe', ['checkout' => $checkout]); // In view: // <x-paddle-checkout :checkout="$checkout" :height="500" /> // @paddleJS ``` ## Checking Subscription Status Verify if a user has an active subscription Use various methods to check subscription states including active, trialing, past due, paused, and canceled. ```php $user = User::find(1); // Check if subscribed (active or on trial) if ($user->subscribed('default')) { // Has valid subscription } // Check specific price if ($user->subscribed('default', 'price_premium')) { // Subscribed to specific price } // Check by product if ($user->subscribedToProduct(['prod_123', 'prod_456'])) { // Subscribed to at least one product } if ($user->subscribedToPrice(['price_basic', 'price_premium'])) { // Subscribed to at least one price } // Get specific subscription $subscription = $user->subscription('premium'); if ($subscription->active()) { // Subscription is active } if ($subscription->onTrial()) { // Still in trial period } if ($subscription->recurring()) { // Active and not on grace period } if ($subscription->onGracePeriod()) { // Canceled but still active until period ends } ``` ## Swapping Subscription Plans Change a subscription to a different price or plan Update a subscription to new prices with flexible proration options. ```php $subscription = $user->subscription('default'); // Swap with proration (default - bill on next renewal) $subscription->prorate()->swap('price_new_plan'); // Swap and bill immediately with proration $subscription->prorateImmediately()->swap('price_new_plan'); // Swap without proration $subscription->noProrate()->swap('price_new_plan'); // Swap immediately without proration $subscription->immediatelyWithoutProrate()->swap('price_new_plan'); // Don't bill for the swap $subscription->doNotBill()->swap('price_new_plan'); // Swap and invoice immediately $subscription->swapAndInvoice('price_premium_yearly'); // Swap multiple items $subscription->swap([ 'price_new_base' => 1, 'price_addon_1' => 2, ]); ``` ## Managing Subscription Quantities Update quantities for metered billing Increment, decrement, or set exact quantities for subscription items. ```php $subscription = $user->subscription('default'); // Increment quantity (for single-price subscriptions) $subscription->incrementQuantity(2); // With specific price (for multi-price subscriptions) $subscription->incrementQuantity(1, 'price_per_seat'); // Increment and invoice immediately $subscription->incrementAndInvoice(5, 'price_per_seat'); // Decrement quantity (minimum 1) $subscription->decrementQuantity(1, 'price_per_seat'); // Set exact quantity $subscription->updateQuantity(10, 'price_per_seat'); // Get current quantity $item = $subscription->findItemOrFail('price_per_seat'); echo $item->quantity; // 10 ``` ## Pausing and Resuming Subscriptions Temporarily pause subscriptions Pause subscriptions immediately or at the next billing period, with optional resume dates. ```php $subscription = $user->subscription('default'); // Pause at next billing period $subscription->pause(); // Pause immediately $subscription->pauseNow(); // Pause until specific date (at next billing) $subscription->pauseUntil(now()->addMonths(2)); // Pause immediately until specific date $subscription->pauseNowUntil(now()->addDays(30)); // Check pause status if ($subscription->paused()) { echo "Subscription is paused"; } if ($subscription->onPausedGracePeriod()) { echo "Will pause on: " . $subscription->paused_at; } // Resume a paused subscription $subscription->resume(); // Resume at future date $subscription->resume(now()->addDays(7)); ``` ## Canceling Subscriptions Cancel subscriptions with grace periods Cancel subscriptions immediately or at the end of the current billing period. ```php $subscription = $user->subscription('default'); // Cancel at end of billing period $subscription->cancel(); // Cancel immediately $subscription->cancelNow(); // Check cancellation status if ($subscription->canceled()) { echo "Subscription is canceled"; } if ($subscription->onGracePeriod()) { echo "Active until: " . $subscription->ends_at; } // Stop a scheduled cancellation $subscription->stopCancelation(); // Redirect user to Paddle's cancellation page return $subscription->redirectToCancel(); // Or get the URL $cancelUrl = $subscription->cancelUrl(); ``` ## Managing Trial Periods Handle free trial periods for subscriptions Extend trials, check trial status, or force trial completion. ```php $user = User::find(1); // Create customer with generic trial $customer = $user->createAsCustomer([ 'trial_ends_at' => now()->addDays(14), ]); // Check trial status if ($user->onTrial('premium')) { echo "On trial until: " . $user->trialEndsAt('premium'); } if ($user->onGenericTrial()) { echo "On generic trial"; } if ($user->hasExpiredTrial('premium')) { echo "Trial has expired"; } // Extend subscription trial $subscription = $user->subscription('premium'); $subscription->extendTrial(now()->addDays(30)); // Force trial to end immediately $subscription->activate(); ``` ## Processing One-Time Charges Create one-time payments without subscriptions Charge customers for one-time purchases or fees. ```php $user = User::find(1); // Simple one-time charge $checkout = $user->charge(5000, 'Setup Fee') ->returnTo(route('checkout.success')); // Charge with options $checkout = $user->charge(9999, 'Consulting Service', [ 'price' => [ 'description' => '2-hour consulting session', 'unit_price' => [ 'amount' => '9999', 'currency_code' => 'USD', ], 'product' => [ 'name' => 'Consulting Service', 'tax_category' => 'standard', ], ], 'quantity' => 1, ])->returnTo(route('success')); // Multiple one-time items $checkout = $user->chargeMany([ [ 'price' => [ 'description' => 'Setup Fee', 'unit_price' => ['amount' => '5000', 'currency_code' => 'USD'], 'product' => ['name' => 'Setup', 'tax_category' => 'standard'], ], 'quantity' => 1, ], [ 'price' => [ 'description' => 'Training Session', 'unit_price' => ['amount' => '15000', 'currency_code' => 'USD'], 'product' => ['name' => 'Training', 'tax_category' => 'standard'], ], 'quantity' => 2, ], ])->returnTo(route('success')); ``` ## Adding One-Time Charges to Subscriptions Bill additional items on top of existing subscriptions Add one-time charges to a subscription, billed immediately or at next renewal. ```php $subscription = $user->subscription('default'); // Charge at next billing period $subscription->charge('price_one_time_fee'); // Charge multiple items at next billing $subscription->charge([ 'price_setup_fee' => 1, 'price_import_service' => 1, ]); // Charge immediately $subscription->chargeAndInvoice('price_one_time_fee'); // Charge multiple items immediately $subscription->chargeAndInvoice([ 'price_priority_support' => 1, 'price_data_migration' => 1, ]); ``` ## Handling Transactions and Invoices Access and manage transaction records Retrieve transaction details, download invoices, and process refunds. ```php $user = User::find(1); // Get all transactions $transactions = $user->transactions; foreach ($transactions as $transaction) { echo $transaction->paddle_id; // Paddle transaction ID echo $transaction->invoice_number; // Invoice number echo $transaction->status; // draft, ready, billed, paid, completed echo $transaction->total(); // "$50.00" echo $transaction->tax(); // "$5.00" echo $transaction->currency; // "USD" echo $transaction->billed_at; // Carbon instance } // Get transaction by Paddle ID $transaction = $user->transactions() ->where('paddle_id', 'txn_123456') ->first(); // Download invoice PDF if ($url = $transaction->invoicePdf()) { return redirect($url); } // Or use convenience method return $transaction->redirectToInvoicePdf(); // Get subscription transactions $subscription = $user->subscription('default'); $transactions = $subscription->transactions; ``` ## Processing Refunds and Credits Refund or credit transactions Issue full or partial refunds, or credit customer accounts. ```php $transaction = Transaction::where('paddle_id', 'txn_123456')->first(); // Full refund $transaction->refund('Customer requested refund'); // Partial refund for specific prices $transaction->refund('Partial refund for quality issue', [ 'price_basic_monthly' => 1500, // Refund $15.00 for this item 'price_addon_feature' => null, // Full refund for this item ]); // Credit to customer account (no money back) $transaction->credit('Credit for service disruption'); // Partial credit $transaction->credit('Partial credit', [ 'price_basic_monthly' => 1000, // $10.00 credit ]); // Check transaction status if ($transaction->status === Transaction::STATUS_COMPLETED) { // Transaction is completed } ``` ## Getting Payment Information Retrieve payment history and upcoming payments Access last payment details and preview next scheduled payment. ```php $subscription = $user->subscription('default'); // Get last payment $lastPayment = $subscription->lastPayment(); if ($lastPayment) { echo $lastPayment->amount(); // "$29.99" echo $lastPayment->rawAmount(); // 2999 (cents) echo $lastPayment->currency()->getCode(); // "USD" echo $lastPayment->date()->format('Y-m-d'); // "2024-11-01" } // Get next scheduled payment $nextPayment = $subscription->nextPayment(); if ($nextPayment) { echo $nextPayment->amount(); // "$29.99" echo $nextPayment->date()->format('F j, Y'); // "December 1, 2024" } // Update payment method URL $updateUrl = $subscription->paymentMethodUpdateUrl(); return redirect($updateUrl); // Or use convenience method return $subscription->redirectToUpdatePaymentMethod(); ``` ## Previewing Prices with Tax Calculate prices with tax before checkout Preview prices with tax calculations for the customer's location. ```php use Laravel\Paddle\Cashier; // Preview single price $previews = Cashier::previewPrices('price_basic_monthly'); foreach ($previews as $preview) { echo $preview->price()->amount(); // "$29.99" echo $preview->subtotal(); // "$29.99" echo $preview->tax(); // "$5.40" echo $preview->total(); // "$35.39" // Raw values (in cents) echo $preview->rawSubtotal(); // 2999 echo $preview->rawTax(); // 540 echo $preview->rawTotal(); // 3539 echo $preview->price()->interval(); // "month" echo $preview->price()->frequency(); // 1 } // Preview multiple prices $previews = Cashier::previewPrices([ 'price_basic_monthly' => 1, 'price_addon_storage' => 3, ]); // Preview with customer context $previews = $user->previewPrices(['price_premium_yearly' => 1]); // Preview with options $previews = Cashier::previewPrices('price_basic_monthly', [ 'customer_ip_address' => '8.8.8.8', 'address' => [ 'postal_code' => '10001', 'country_code' => 'US', ], ]); ``` ## Handling Webhooks Process Paddle webhook events automatically Webhooks are automatically registered and handled at `/paddle/webhook`. Configure your webhook secret in `.env`. ```php // .env configuration PADDLE_SELLER_ID=your-seller-id PADDLE_CLIENT_SIDE_TOKEN=your-client-token PADDLE_API_KEY=your-api-key PADDLE_WEBHOOK_SECRET=your-webhook-secret // Webhooks handled automatically: // - customer.updated // - subscription.created // - subscription.updated // - subscription.paused // - subscription.canceled // - transaction.completed // - transaction.updated // Listen to events in EventServiceProvider use Laravel\Paddle\Events\SubscriptionCreated; use Laravel\Paddle\Events\SubscriptionCanceled; use Laravel\Paddle\Events\TransactionCompleted; protected $listen = [ SubscriptionCreated::class => [ SendSubscriptionConfirmation::class, ], SubscriptionCanceled::class => [ SendCancellationEmail::class, ], TransactionCompleted::class => [ LogTransaction::class, SendInvoiceEmail::class, ], ]; // Example listener public function handle(SubscriptionCreated $event) { $billable = $event->billable; $subscription = $event->subscription; $payload = $event->payload; // Raw Paddle data // Send welcome email, grant access, etc. } ``` ## Customizing Checkout Experience Create custom checkout flows with options Build checkouts for guests, existing customers, or with custom data. ```php use Laravel\Paddle\Checkout; // Guest checkout $checkout = Checkout::guest([ 'price_basic_monthly' => 1, ]) ->customData(['order_id' => 12345]) ->returnTo(route('success')); // Existing customer checkout $customer = $user->customer; $checkout = Checkout::customer($customer, [ 'price_premium_yearly' => 1, ]) ->returnTo(route('success')); // Transaction-based checkout (one-time) $transaction = Cashier::api('POST', 'transactions', [ 'items' => [ ['price_id' => 'price_one_time', 'quantity' => 1] ] ])['data']; $checkout = Checkout::transaction($transaction, $customer) ->returnTo(route('success')); // Render in Blade <x-paddle-button :checkout="$checkout" class="btn btn-primary"> Subscribe Now </x-paddle-button> <!-- Or inline checkout --> <x-paddle-checkout :checkout="$checkout" id="paddle-checkout" :height="500" /> @paddleJS ``` ## Querying Subscriptions with Scopes Filter subscriptions efficiently Use Eloquent scopes to query subscriptions by status. ```php use App\Models\User; use Laravel\Paddle\Subscription; // Get all valid subscriptions (active or trialing) $validSubscriptions = Subscription::valid()->get(); // Get all active subscriptions $activeSubscriptions = Subscription::active()->get(); // Get trialing subscriptions $trialingSubscriptions = Subscription::onTrial()->get(); // Get recurring subscriptions (active without grace periods) $recurringSubscriptions = Subscription::recurring()->get(); // Get paused subscriptions $pausedSubscriptions = Subscription::paused()->get(); // Get canceled subscriptions $canceledSubscriptions = Subscription::canceled()->get(); // Get past due subscriptions $pastDueSubscriptions = Subscription::pastDue()->get(); // Users with active subscriptions $subscribedUsers = User::whereHas('subscriptions', function ($query) { $query->active(); })->get(); // Users on grace period after cancellation $onGracePeriod = Subscription::onGracePeriod()->get(); ``` ## Customizing Models and Configuration Override default models and settings Customize Cashier behavior in your AppServiceProvider. ```php use Laravel\Paddle\Cashier; use App\Models\CustomCustomer; use App\Models\CustomSubscription; public function boot() { // Use custom models Cashier::useCustomerModel(CustomCustomer::class); Cashier::useSubscriptionModel(CustomSubscription::class); Cashier::useSubscriptionItemModel(CustomSubscriptionItem::class); Cashier::useTransactionModel(CustomTransaction::class); // Custom currency formatting Cashier::formatCurrencyUsing(function ($amount, $currency, $locale, $options) { return number_format($amount / 100, 2) . ' ' . strtoupper($currency); }); // Don't register routes (manage manually) Cashier::ignoreRoutes(); // Keep past-due subscriptions active Cashier::keepPastDueSubscriptionsActive(); } // config/cashier.php return [ 'seller_id' => env('PADDLE_SELLER_ID'), 'client_side_token' => env('PADDLE_CLIENT_SIDE_TOKEN'), 'api_key' => env('PADDLE_API_KEY'), 'webhook_secret' => env('PADDLE_WEBHOOK_SECRET'), 'path' => env('CASHIER_PATH', 'paddle'), 'currency' => env('CASHIER_CURRENCY', 'USD'), 'currency_locale' => env('CASHIER_CURRENCY_LOCALE', 'en'), 'sandbox' => env('PADDLE_SANDBOX', false), ]; ``` ## Testing with Cashier Fake Test billing functionality without API calls Use CashierFake to test subscription flows in your test suite. ```php use Laravel\Paddle\Cashier; use Tests\TestCase; class SubscriptionTest extends TestCase { public function test_user_can_subscribe() { // Initialize fake Cashier::fake(); $user = User::factory()->create(); // Perform subscription actions $checkout = $user->subscribe('price_basic_monthly'); // Assertions Cashier::assertSubscriptionCreated(function ($event) { return $event->subscription->paddle_id === 'sub_123'; }); } public function test_user_can_cancel_subscription() { Cashier::fake([ 'subscriptions' => [['id' => 'sub_123', 'status' => 'active']] ]); $subscription = Subscription::factory()->create(); $subscription->cancel(); Cashier::assertSubscriptionCanceled(); } public function test_transaction_completed() { Cashier::fake(); // Test code that triggers transaction Cashier::assertTransactionCompleted(function ($event) { return $event->transaction->paddle_id === 'txn_123'; }); } } ``` ## Summary Laravel Cashier Paddle streamlines subscription billing by providing a fluent, expressive interface for common billing operations. The package handles the complexities of recurring payments, trial periods, subscription changes, and payment processing while maintaining clean, Laravel-style code. With automatic webhook handling, your application stays synchronized with Paddle's billing system without manual intervention. The Billable trait integrates seamlessly with your existing Eloquent models, requiring minimal setup to add full billing capabilities. Integration with Cashier Paddle follows Laravel conventions throughout. Use the provided Blade components for frontend checkout experiences, listen to dispatched events for custom business logic, and leverage Eloquent relationships to access billing data. The package supports advanced scenarios like multi-item subscriptions, proration strategies, quantity-based billing, and complex refund logic. Whether building a SaaS application with tiered pricing or a platform with usage-based billing, Cashier Paddle provides the tools needed to implement professional subscription management with confidence.