Cashier (Paddle)
Laravel Cashier (Paddle)
Introduction
This documentation is for Cashier Paddle 2.x’s integration with Paddle Billing. If you’re still using Paddle Classic, you should use Cashier Paddle 1.x.
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. In addition to basic subscription management, Cashier can handle: swapping subscriptions, subscription “quantities”, subscription pausing, cancelation grace periods, and more.
Before digging into Cashier Paddle, we recommend you also review Paddle’s concept guides and API documentation.
Upgrading Cashier
When upgrading to a new version of Cashier, it’s important that you carefully review the upgrade guide.
Installation
First, install the Cashier package for Paddle using the Composer package manager:
composer require laravel/cashier-paddle
composer require laravel/cashier-paddle
Next, you should publish the Cashier migration files using the vendor:publish Artisan command:
php artisan vendor:publish --tag="cashier-migrations"
php artisan vendor:publish --tag="cashier-migrations"
Then, you should run your application’s database migrations. The Cashier migrations will create a new customers table. In addition, new subscriptions and subscription_items tables will be created to store all of your customer’s subscriptions. Lastly, a new transactions table will be created to store all of the Paddle transactions associated with your customers:
php artisan migrate
php artisan migrate
To ensure Cashier properly handles all Paddle events, remember to set up Cashier’s webhook handling.
Paddle Sandbox
During local and staging development, you should register a Paddle Sandbox account. This account will give you a sandboxed environment to test and develop your applications without making actual payments. You may use Paddle’s test card numbers to simulate various payment scenarios.
When using the Paddle Sandbox environment, you should set the PADDLE_SANDBOX environment variable to true within your application’s .env file:
PADDLE_SANDBOX=true
PADDLE_SANDBOX=true
After you have finished developing your application you may apply for a Paddle vendor account. Before your application is placed into production, Paddle will need to approve your application’s domain.
Configuration
Billable Model
Before using Cashier, you must add the Billable trait to your user model definition. This trait provides various methods to allow you to perform common billing tasks, such as creating subscriptions and updating payment method information:
use Laravel\Paddle\Billable;
class User extends Authenticatable
{
use Billable;
}
use Laravel\Paddle\Billable;
class User extends Authenticatable
{
use Billable;
}
If you have billable entities that are not users, you may also add the trait to those classes:
use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
{
use Billable;
}
use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
{
use Billable;
}
API Keys
Next, you should configure your Paddle keys in your application’s .env file. You can retrieve your Paddle API keys from the Paddle control panel:
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
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
The PADDLE_SANDBOX environment variable should be set to true when you are using Paddle’s Sandbox environment. The PADDLE_SANDBOX variable should be set to false if you are deploying your application to production and are using Paddle’s live vendor environment.
The PADDLE_RETAIN_KEY is optional and should only be set if you’re using Paddle with Retain.
Paddle JS
Paddle relies on its own JavaScript library to initiate the Paddle checkout widget. You can load the JavaScript library by placing the @paddleJS Blade directive right before your application layout’s closing </head> tag:
<head>
...
@paddleJS
</head>
<head>
...
@paddleJS
</head>
Currency Configuration
You can specify a locale to be used when formatting money values for display on invoices. Internally, Cashier utilizes PHP’s NumberFormatter class to set the currency locale:
CASHIER_CURRENCY_LOCALE=nl_BE
CASHIER_CURRENCY_LOCALE=nl_BE
In order to use locales other than en, ensure the ext-intl PHP extension is installed and configured on your server.
Overriding Default Models
You are free to extend the models used internally by Cashier by defining your own model and extending the corresponding Cashier model:
use Laravel\Paddle\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
use Laravel\Paddle\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
After defining your model, you may instruct Cashier to use your custom model via the Laravel\Paddle\Cashier class. Typically, you should inform Cashier about your custom models in the boot method of your application’s App\Providers\AppServiceProvider class:
use App\Models\Cashier\Subscription;
use App\Models\Cashier\Transaction;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useTransactionModel(Transaction::class);
}
use App\Models\Cashier\Subscription;
use App\Models\Cashier\Transaction;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useTransactionModel(Transaction::class);
}
Quickstart
Selling Products
Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should configure Paddle’s webhook handling.
Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and Paddle’s Checkout Overlay, you can easily build modern, robust payment integrations.
To charge customers for non-recurring, single-charge products, we’ll utilize Cashier to charge customers with Paddle’s Checkout Overlay, where they will provide their payment details and confirm their purchase. Once the payment has been made via the Checkout Overlay, the customer will be redirected to a success URL of your choosing within your application:
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');
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');
As you can see in the example above, we will utilize Cashier’s provided checkout method to create a checkout object to present the customer the Paddle Checkout Overlay for a given “price identifier”. When using Paddle, “prices” refer to defined prices for specific products.
If necessary, the checkout method will automatically create a customer in Paddle and connect that Paddle customer record to the corresponding user in your application’s database. After completing the checkout session, the customer will be redirected to a dedicated success page where you can display an informational message to the customer.
In the buy view, we will include a button to display the Checkout Overlay. The paddle-button Blade component is included with Cashier Paddle; however, you may also manually render an overlay checkout:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy Product
</x-paddle-button>
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy Product
</x-paddle-button>
Providing Meta Data to Paddle Checkout
When selling products, it’s common to keep track of completed orders and purchased products via Cart and Order models defined by your own application. When redirecting customers to Paddle’s Checkout Overlay to complete a purchase, you may need to provide an existing order identifier so that you can associate the completed purchase with the corresponding order when the customer is redirected back to your application.
To accomplish this, you may provide an array of custom data to the checkout method. Let’s imagine that a pending Order is created within our application when a user begins the checkout process. Remember, the Cart and Order models in this example are illustrative and not provided by Cashier. You are free to implement these concepts based on the needs of your own application:
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');
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');
As you can see in the example above, when a user begins the checkout process, we will provide all of the cart / order’s associated Paddle price identifiers to the checkout method. Of course, your application is responsible for associating these items with the “shopping cart” or order as a customer adds them. We also provide the order’s ID to the Paddle Checkout Overlay via the customData method.
Of course, you will likely want to mark the order as “complete” once the customer has finished the checkout process. To accomplish this, you may listen to the webhooks dispatched by Paddle and raised via events by Cashier to store order information in your database.
To get started, listen for the TransactionCompleted event dispatched by Cashier. Typically, you should register the event listener in the boot method of your application’s AppServiceProvider:
use App\Listeners\CompleteOrder;
use Illuminate\Support\Facades\Event;
use Laravel\Paddle\Events\TransactionCompleted;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(TransactionCompleted::class, CompleteOrder::class);
}
use App\Listeners\CompleteOrder;
use Illuminate\Support\Facades\Event;
use Laravel\Paddle\Events\TransactionCompleted;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(TransactionCompleted::class, CompleteOrder::class);
}
In this example, the CompleteOrder listener might look like the following:
namespace App\Listeners;
use App\Models\Order;
use Laravel\Paddle\Cashier;
use Laravel\Paddle\Events\TransactionCompleted;
class CompleteOrder
{
/**
* Handle the incoming Cashier webhook event.
*/
public function handle(TransactionCompleted $event): void
{
$orderId = $event->payload['data']['custom_data']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
}
}
namespace App\Listeners;
use App\Models\Order;
use Laravel\Paddle\Cashier;
use Laravel\Paddle\Events\TransactionCompleted;
class CompleteOrder
{
/**
* Handle the incoming Cashier webhook event.
*/
public function handle(TransactionCompleted $event): void
{
$orderId = $event->payload['data']['custom_data']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
}
}
Please refer to Paddle’s documentation for more information on the data contained by the transaction.completed event.
Selling Subscriptions
Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should configure Paddle’s webhook handling.
Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and Paddle’s Checkout Overlay, you can easily build modern, robust payment integrations.
To learn how to sell subscriptions using Cashier and Paddle’s Checkout Overlay, let’s consider the simple scenario of a subscription service with a basic monthly (price_basic_monthly) and yearly (price_basic_yearly) plan. These two prices could be grouped under a “Basic” product (pro_basic) in our Paddle dashboard. In addition, our subscription service might offer an Expert plan as pro_expert.
First, let’s discover how a customer can subscribe to our services. Of course, you can imagine the customer might click a “subscribe” button for the Basic plan on our application’s pricing page. This button will invoke a Paddle Checkout Overlay for their chosen plan. To get started, let’s initiate a checkout session via the checkout method:
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');
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');
In the subscribe view, we will include a button to display the Checkout Overlay. The paddle-button Blade component is included with Cashier Paddle; however, you may also manually render an overlay checkout:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Now, when the Subscribe button is clicked, the customer will be able to enter their payment details and initiate their subscription. To know when their subscription has actually started (since some payment methods require a few seconds to process), you should also configure Cashier’s webhook handling.
Now that customers can start subscriptions, we need to restrict certain portions of our application so that only subscribed users can access them. Of course, we can always determine a user’s current subscription status via the subscribed method provided by Cashier’s Billable trait:
@if ($user->subscribed())
<p>You are subscribed.</p>
@endif
@if ($user->subscribed())
<p>You are subscribed.</p>
@endif
We can even easily determine if a user is subscribed to specific product or price:
@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
@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
Building a Subscribed Middleware
For convenience, you may wish to create a middleware which determines if the incoming request is from a subscribed user. Once this middleware has been defined, you may easily assign it to a route to prevent users that are not subscribed from accessing the route:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Subscribed
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirect user to billing page and ask them to subscribe...
return redirect('/subscribe');
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Subscribed
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirect user to billing page and ask them to subscribe...
return redirect('/subscribe');
}
return $next($request);
}
}
Once the middleware has been defined, you may assign it to a route:
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
Allowing Customers to Manage Their Billing Plan
Of course, customers may want to change their subscription plan to another product or “tier”. In our example from above, we’d want to allow the customer to change their plan from a monthly subscription to a yearly subscription. For this you’ll need to implement something like a button that leads to the below route:
use Illuminate\Http\Request;
Route::put('/subscription/{price}/swap', function (Request $request, $price) {
$user->subscription()->swap($price); // With "$price" being "price_basic_yearly" for this example.
return redirect()->route('dashboard');
})->name('subscription.swap');
use Illuminate\Http\Request;
Route::put('/subscription/{price}/swap', function (Request $request, $price) {
$user->subscription()->swap($price); // With "$price" being "price_basic_yearly" for this example.
return redirect()->route('dashboard');
})->name('subscription.swap');
Besides swapping plans you’ll also need to allow your customers to cancel their subscription. Like swapping plans, provide a button that leads to the following route:
use Illuminate\Http\Request;
Route::put('/subscription/cancel', function (Request $request, $price) {
$user->subscription()->cancel();
return redirect()->route('dashboard');
})->name('subscription.cancel');
use Illuminate\Http\Request;
Route::put('/subscription/cancel', function (Request $request, $price) {
$user->subscription()->cancel();
return redirect()->route('dashboard');
})->name('subscription.cancel');
And now your subscription will get canceled at the end of its billing period.
As long as you have configured Cashier’s webhook handling, Cashier will automatically keep your application’s Cashier-related database tables in sync by inspecting the incoming webhooks from Paddle. So, for example, when you cancel a customer’s subscription via Paddle’s dashboard, Cashier will receive the corresponding webhook and mark the subscription as “canceled” in your application’s database.
Checkout Sessions
Most operations to bill customers are performed using “checkouts” via Paddle’s Checkout Overlay widget or by utilizing inline checkout.
Before processing checkout payments using Paddle, you should define your application’s default payment link in your Paddle checkout settings dashboard.
Overlay Checkout
Before displaying the Checkout Overlay widget, you must generate a checkout session using Cashier. A checkout session will inform the checkout widget of the billing operation that should be performed:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Cashier includes a paddle-button Blade component. You may pass the checkout session to this component as a “prop”. Then, when this button is clicked, Paddle’s checkout widget will be displayed:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
By default, this will display the widget using Paddle’s default styling. You can customize the widget by adding Paddle supported attributes like the data-theme='light' attribute to the component:
<x-paddle-button :checkout="$checkout" class="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>
<x-paddle-button :checkout="$checkout" class="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>
The Paddle checkout widget is asynchronous. Once the user creates a subscription within the widget, Paddle will send your application a webhook so that you may properly update the subscription state in your application’s database. Therefore, it’s important that you properly set up webhooks to accommodate for state changes from Paddle.
After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user’s subscription might not be immediately available after completing the checkout.
Manually Rendering an Overlay Checkout
You may also manually render an overlay checkout without using Laravel’s built-in Blade components. To get started, generate the checkout session as demonstrated in previous examples:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Next, you may use Paddle.js to initialize the checkout. In this example, we will create a link that is assigned the paddle_button class. Paddle.js will detect this class and display the overlay checkout when the link is clicked:
<?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>
<?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>
Inline Checkout
If you don’t want to make use of Paddle’s “overlay” style checkout widget, Paddle also provides the option to display the widget inline. While this approach does not allow you to adjust any of the checkout’s HTML fields, it allows you to embed the widget within your application.
To make it easy for you to get started with inline checkout, Cashier includes a paddle-checkout Blade component. To get started, you should generate a checkout session:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Then, you may pass the checkout session to the component’s checkout attribute:
<x-paddle-checkout :checkout="$checkout" class="w-full" />
<x-paddle-checkout :checkout="$checkout" class="w-full" />
To adjust the height of the inline checkout component, you may pass the height attribute to the Blade component:
<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />
<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />
Please consult Paddle’s guide on Inline Checkout and available checkout settings for further details on the inline checkout’s customization options.
Manually Rendering an Inline Checkout
You may also manually render an inline checkout without using Laravel’s built-in Blade components. To get started, generate the checkout session as demonstrated in previous examples:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Next, you may use Paddle.js to initialize the checkout. In this example, we will demonstrate this using Alpine.js; however, you are free to modify this example for your own frontend stack:
<?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>
<?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>
Guest Checkouts
Sometimes, you may need to create a checkout session for users that do not need an account with your application. To do so, you may use the guest method:
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]);
});
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]);
});
Then, you may provide the checkout session to the Paddle button or inline checkout Blade components.
Price Previews
Paddle allows you to customize prices per currency, essentially allowing you to configure different prices for different countries. Cashier Paddle allows you to retrieve all of these prices using the previewPrices method. This method accepts the price IDs you wish to retrieve prices for:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456']);
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456']);
The currency will be determined based on the IP address of the request; however, you may optionally provide a specific country to retrieve prices for:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [
'country_code' => 'BE',
'postal_code' => '1234',
]]);
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [
'country_code' => 'BE',
'postal_code' => '1234',
]]);
After retrieving the prices you may display them however you wish:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
You may also display the subtotal price and tax amount separately:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
@endforeach
</ul>
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
@endforeach
</ul>
For more information, checkout Paddle’s API documentation regarding price previews.
Customer Price Previews
If a user is already a customer and you would like to display the prices that apply to that customer, you may do so by retrieving the prices directly from the customer instance:
use App\Models\User;
$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);
use App\Models\User;
$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);
Internally, Cashier will use the user’s customer ID to retrieve the prices in their currency. So, for example, a user living in the United States will see prices in US dollars while a user in Belgium will see prices in Euros. If no matching currency can be found, the default currency of the product will be used. You can customize all prices of a product or subscription plan in the Paddle control panel.
Discounts
You may also choose to display prices after a discount. When calling the previewPrices method, you provide the discount ID via the discount_id option:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
'discount_id' => 'dsc_123'
]);
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
'discount_id' => 'dsc_123'
]);
Then, display the calculated prices:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
Customers
Customer Defaults
Cashier allows you to define some useful defaults for your customers when creating checkout sessions. Setting these defaults allow you to pre-fill a customer’s email address and name so that they can immediately move on to the payment portion of the checkout widget. You can set these defaults by overriding the following methods on your billable model:
/**
* Get the customer's name to associate with Paddle.
*/
public function paddleName(): string|null
{
return $this->name;
}
/**
* Get the customer's email address to associate with Paddle.
*/
public function paddleEmail(): string|null
{
return $this->email;
}
/**
* Get the customer's name to associate with Paddle.
*/
public function paddleName(): string|null
{
return $this->name;
}
/**
* Get the customer's email address to associate with Paddle.
*/
public function paddleEmail(): string|null
{
return $this->email;
}
These defaults will be used for every action in Cashier that generates a checkout session.
Retrieving Customers
You can retrieve a customer by their Paddle Customer ID using the Cashier::findBillable method. This method will return an instance of the billable model:
use Laravel\Paddle\Cashier;
$user = Cashier::findBillable($customerId);
use Laravel\Paddle\Cashier;
$user = Cashier::findBillable($customerId);
Creating Customers
Occasionally, you may wish to create a Paddle customer without beginning a subscription. You may accomplish this using the createAsCustomer method:
$customer = $user->createAsCustomer();
$customer = $user->createAsCustomer();
An instance of Laravel\Paddle\Customer is returned. Once the customer has been created in Paddle, you may begin a subscription at a later date. You may provide an optional $options array to pass in any additional customer creation parameters that are supported by the Paddle API:
$customer = $user->createAsCustomer($options);
$customer = $user->createAsCustomer($options);
Subscriptions
Creating Subscriptions
To create a subscription, first retrieve an instance of your billable model from your database, which will typically be an instance of App\Models\User. Once you have retrieved the model instance, you may use the subscribe method to create the model’s checkout session:
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]);
});
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]);
});
The first argument given to the subscribe method is the specific price the user is subscribing to. This value should correspond to the price’s identifier in Paddle. The returnTo method accepts a URL that your user will be redirected to after they successfully complete the checkout. The second argument passed to the subscribe method should be the internal “type” of the subscription. If your application only offers a single subscription, you might call this default or primary. This subscription type is only for internal application usage and is not meant to be displayed to users. In addition, it should not contain spaces and it should never be changed after creating the subscription.
You may also provide an array of custom metadata regarding the subscription using the customData method:
$checkout = $request->user()->subscribe($premium = 12345, 'default')
->customData(['key' => 'value'])
->returnTo(route('home'));
$checkout = $request->user()->subscribe($premium = 12345, 'default')
->customData(['key' => 'value'])
->returnTo(route('home'));
Once a subscription checkout session has been created, the checkout session may be provided to the paddle-button Blade component that is included with Cashier Paddle:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
After the user has finished their checkout, a subscription_created webhook will be dispatched from Paddle. Cashier will receive this webhook and setup the subscription for your customer. In order to make sure all webhooks are properly received and handled by your application, ensure you have properly setup webhook handling.
(本文超出站点单文档体积上限,已按小节截断)
