Laravel Cashier (Paddle)
Вступ
Ця документація стосується інтеграції Cashier Paddle 2.x з Paddle Billing. Якщо ви досі користуєтеся Paddle Classic, вам слід використовувати Cashier Paddle 1.x.
Laravel Cashier Paddle дає виразний, плавний інтерфейс до сервісів білінгу підписок Paddle. Він бере на себе майже весь шаблонний код білінгу підписок, якого ви так боїтеся. Окрім базового керування підписками, Cashier уміє: змінювати підписки, працювати з «кількостями» в підписках, призупиняти підписки, обробляти пільгові періоди після скасування тощо.
Перш ніж заглиблюватися в Cashier Paddle, радимо також переглянути концептуальні посібники та документацію API від Paddle.
Оновлення Cashier
Оновлюючись до нової версії Cashier, важливо уважно переглянути посібник з оновлення.
Встановлення
Спершу встановіть пакет Cashier для Paddle за допомогою менеджера пакетів Composer:
composer require laravel/cashier-paddle
Далі вам слід опублікувати файли міграцій Cashier артизан-командою vendor:publish:
php artisan vendor:publish --tag="cashier-migrations"
Потім виконайте міграції бази даних вашого застосунку. Міграції Cashier створять нову таблицю customers. Крім того, буде створено нові таблиці subscriptions і subscription_items для зберігання всіх підписок ваших клієнтів. Насамкінець буде створено нову таблицю transactions для зберігання всіх транзакцій Paddle, пов'язаних з вашими клієнтами:
php artisan migrate
Щоб Cashier належно обробляв усі події Paddle, не забудьте налаштувати обробку вебхуків Cashier.
Paddle Sandbox
Під час локальної розробки та на staging вам слід зареєструвати обліковий запис Paddle Sandbox. Цей обліковий запис дасть вам пісочницю для тестування й розробки застосунків без реальних платежів. Ви можете скористатися тестовими номерами карток Paddle, щоб змоделювати різні платіжні сценарії.
Використовуючи середовище Paddle Sandbox, вам слід встановити змінну оточення PADDLE_SANDBOX у true у файлі .env вашого застосунку:
PADDLE_SANDBOX=true
Завершивши розробку застосунку, ви можете подати заявку на обліковий запис вендора Paddle. Перш ніж ваш застосунок потрапить у продакшен, Paddle має схвалити домен вашого застосунку.
Конфігурація
Модель з білінгом
Перш ніж користуватися Cashier, вам потрібно додати трейт Billable до визначення вашої моделі користувача. Цей трейт надає різні методи, що дозволяють виконувати типові завдання білінгу, як-от створення підписок і оновлення інформації про платіжний метод:
use Laravel\Paddle\Billable;
class User extends Authenticatable
{
use Billable;
}
Якщо у вас є сутності з білінгом, які не є користувачами, ви можете додати трейт і до цих класів:
use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
{
use Billable;
}
API-ключі
Далі вам слід налаштувати свої ключі Paddle у файлі .env вашого застосунку. Отримати API-ключі Paddle можна з панелі керування 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
Змінну оточення PADDLE_SANDBOX слід встановити в true, коли ви використовуєте середовище Sandbox від Paddle. Змінну PADDLE_SANDBOX слід встановити в false, якщо ви розгортаєте застосунок у продакшені й використовуєте живе вендорське середовище Paddle.
PADDLE_RETAIN_KEY необов'язковий, і його слід встановлювати, лише якщо ви використовуєте Paddle із Retain.
Paddle JS
Paddle покладається на власну JavaScript-бібліотеку, щоб запустити віджет оформлення Paddle. Завантажити цю бібліотеку можна, розмістивши Blade-директиву @paddleJS безпосередньо перед закривальним тегом </head> у макеті вашого застосунку:
<head>
...
@paddleJS
</head>
Конфігурація валюти
Ви можете вказати локаль, яка використовуватиметься для форматування грошових значень для показу в рахунках. Внутрішньо Cashier використовує клас PHP NumberFormatter, щоб задати локаль валюти:
CASHIER_CURRENCY_LOCALE=nl_BE
Щоб використовувати локалі, відмінні від
en, переконайтеся, що PHP-розширенняext-intlвстановлено й налаштовано на вашому сервері.
Заміна моделей за замовчуванням
Ви можете розширювати моделі, які Cashier використовує внутрішньо, визначивши власну модель і розширивши відповідну модель Cashier:
use Laravel\Paddle\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
Визначивши свою модель, ви можете вказати Cashier використовувати вашу власну модель через клас Laravel\Paddle\Cashier. Зазвичай повідомляти Cashier про ваші власні моделі слід у методі boot класу App\Providers\AppServiceProvider вашого застосунку:
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);
}
Швидкий старт
Продаж продуктів
Перш ніж користуватися Paddle Checkout, вам слід визначити продукти з фіксованими цінами у своїй панелі Paddle. Крім того, вам слід налаштувати обробку вебхуків Paddle.
Пропонувати білінг продуктів і підписок через ваш застосунок може здаватися страшним. Однак завдяки Cashier та Checkout Overlay від Paddle ви можете легко побудувати сучасні, надійні платіжні інтеграції.
Щоб списувати кошти з клієнтів за неперіодичні продукти з разовим списанням, ми скористаємося Cashier для списання через Checkout Overlay від Paddle, де клієнти нададуть свої платіжні дані й підтвердять покупку. Щойно платіж буде здійснено через Checkout Overlay, клієнта буде перенаправлено на обрану вами URL-адресу успіху у вашому застосунку:
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');
Як бачите в наведеному вище прикладі, ми скористаємося наданим Cashier методом checkout, щоб створити об'єкт оформлення й показати клієнту Checkout Overlay від Paddle для заданого «ідентифікатора ціни». У Paddle «ціни» - це визначені ціни для конкретних продуктів.
За потреби метод checkout автоматично створить клієнта в Paddle і зв'яже цей запис клієнта Paddle з відповідним користувачем у базі даних вашого застосунку. Після завершення сесії оформлення клієнта буде перенаправлено на спеціальну сторінку успіху, де ви можете показати йому інформаційне повідомлення.
У представленні buy ми додамо кнопку для показу Checkout Overlay. Blade-компонент paddle-button входить до Cashier Paddle; однак ви також можете відрендерити overlay-оформлення вручну:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy Product
</x-paddle-button>
Передавання метаданих до Paddle Checkout
Продаючи продукти, зазвичай відстежують завершені замовлення й куплені продукти через моделі Cart і Order, визначені у вашому власному застосунку. Перенаправляючи клієнтів до Checkout Overlay від Paddle для завершення покупки, вам може знадобитися передати наявний ідентифікатор замовлення, щоб пов'язати завершену покупку з відповідним замовленням, коли клієнта буде перенаправлено назад до вашого застосунку.
Щоб досягти цього, передайте до методу checkout масив власних даних. Уявімо, що в нашому застосунку створюється незавершене замовлення Order, коли користувач починає процес оформлення. Пам'ятайте: моделі Cart і Order у цьому прикладі є ілюстративними і не надаються Cashier. Ви можете реалізувати ці концепції відповідно до потреб власного застосунку:
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');
Як бачите в наведеному вище прикладі, коли користувач починає процес оформлення, ми передаємо до методу checkout усі пов'язані з кошиком / замовленням ідентифікатори цін Paddle. Звісно, ваш застосунок відповідає за прив'язку цих позицій до «кошика» чи замовлення, коли клієнт їх додає. Ми також передаємо ID замовлення до Checkout Overlay від Paddle методом customData.
Звісно, ви, найімовірніше, захочете позначити замовлення як «завершене», щойно клієнт завершить процес оформлення. Щоб досягти цього, ви можете слухати вебхуки, які надсилає Paddle і які Cashier здіймає у вигляді подій, і зберігати інформацію про замовлення у своїй базі даних.
Для початку слухайте подію TransactionCompleted, яку диспетчеризує Cashier. Зазвичай реєструвати слухача події слід у методі boot 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);
}
У цьому прикладі слухач CompleteOrder міг би виглядати так:
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']);
}
}
Докладніше про дані, які містить подія transaction.completed, дивіться в документації Paddle.
Продаж підписок
Перш ніж користуватися Paddle Checkout, вам слід визначити продукти з фіксованими цінами у своїй панелі Paddle. Крім того, вам слід налаштувати обробку вебхуків Paddle.
Пропонувати білінг продуктів і підписок через ваш застосунок може здаватися страшним. Однак завдяки Cashier та Checkout Overlay від Paddle ви можете легко побудувати сучасні, надійні платіжні інтеграції.
Щоб дізнатися, як продавати підписки за допомогою Cashier і Checkout Overlay від Paddle, розгляньмо простий сценарій сервісу підписок з базовим місячним (price_basic_monthly) і річним (price_basic_yearly) планом. Ці дві ціни можна згрупувати під продуктом «Basic» (pro_basic) у нашій панелі Paddle. Крім того, наш сервіс підписок може пропонувати план «Expert» як pro_expert.
Спершу з'ясуймо, як клієнт може підписатися на наші сервіси. Звісно, можна уявити, що клієнт натисне кнопку «subscribe» для плану Basic на сторінці цін нашого застосунку. Ця кнопка викличе Checkout Overlay від Paddle для обраного плану. Для початку ініціюймо сесію оформлення методом 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');
У представленні subscribe ми додамо кнопку для показу Checkout Overlay. Blade-компонент paddle-button входить до Cashier Paddle; однак ви також можете відрендерити overlay-оформлення вручну:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Тепер, коли натиснуто кнопку Subscribe, клієнт зможе ввести свої платіжні дані й розпочати підписку. Щоб знати, коли підписка справді почалася (оскільки деяким платіжним методам потрібно кілька секунд на обробку), вам також слід налаштувати обробку вебхуків Cashier.
Тепер, коли клієнти можуть починати підписки, нам потрібно обмежити певні частини нашого застосунку так, щоб доступ до них мали лише підписані користувачі. Звісно, ми завжди можемо визначити поточний стан підписки користувача методом subscribed, який надає трейт Billable з Cashier:
@if ($user->subscribed())
<p>You are subscribed.</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
Створення middleware для підписаних
Для зручності ви можете створити middleware, який визначає, чи надійшов вхідний запит від підписаного користувача. Щойно цей middleware буде визначено, ви зможете легко призначити його маршруту, щоб не пускати до нього непідписаних користувачів:
<?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);
}
}
Щойно middleware визначено, ви можете призначити його маршруту:
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
Дозвіл клієнтам керувати своїм планом білінгу
Звісно, клієнти можуть захотіти змінити свій план підписки на інший продукт чи «рівень». У нашому прикладі вище ми хотіли б дозволити клієнту змінити план з місячної підписки на річну. Для цього вам потрібно буде реалізувати щось на кшталт кнопки, яка веде до наведеного нижче маршруту:
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/cancel', function (Request $request, $price) {
$user->subscription()->cancel();
return redirect()->route('dashboard');
})->name('subscription.cancel');
І тепер вашу підписку буде скасовано наприкінці її розрахункового періоду.
Доки ви налаштували обробку вебхуків Cashier, Cashier автоматично підтримуватиме пов'язані з ним таблиці бази даних вашого застосунку синхронізованими, аналізуючи вхідні вебхуки від Paddle. Так, наприклад, коли ви скасуєте підписку клієнта через панель Paddle, Cashier отримає відповідний вебхук і позначить підписку як «скасовану» в базі даних вашого застосунку.
Сесії оформлення
Більшість операцій білінгу для клієнтів виконуються через «оформлення» за допомогою віджета Checkout Overlay від Paddle або за допомогою вбудованого оформлення.
Перш ніж обробляти платежі оформлення через Paddle, вам слід визначити посилання на оплату за замовчуванням для вашого застосунку в панелі налаштувань оформлення Paddle.
Overlay-оформлення
Перш ніж показувати віджет Checkout Overlay, вам потрібно згенерувати сесію оформлення за допомогою Cashier. Сесія оформлення повідомить віджету оформлення, яку операцію білінгу слід виконати:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Cashier містить Blade-компонент paddle-button. Ви можете передати сесію оформлення до цього компонента як «проп». Далі, коли цю кнопку буде натиснуто, з'явиться віджет оформлення Paddle:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
За замовчуванням це покаже віджет зі стандартною стилізацією Paddle. Ви можете налаштувати віджет, додавши до компонента атрибути, які підтримує Paddle, як-от атрибут data-theme='light':
<x-paddle-button :checkout="$checkout" class="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>
Віджет оформлення Paddle асинхронний. Щойно користувач створить підписку у віджеті, Paddle надішле вашому застосунку вебхук, щоб ви могли належно оновити стан підписки в базі даних вашого застосунку. Тому важливо, щоб ви правильно налаштували вебхуки для врахування змін стану з боку Paddle.
Після зміни стану підписки затримка отримання відповідного вебхука зазвичай мінімальна, але вам слід урахувати це у своєму застосунку, зважаючи на те, що підписка вашого користувача може бути недоступною одразу після завершення оформлення.
Ручний рендеринг overlay-оформлення
Ви також можете відрендерити overlay-оформлення вручну, не використовуючи вбудовані Blade-компоненти Laravel. Для початку згенеруйте сесію оформлення як показано в попередніх прикладах:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Далі ви можете скористатися Paddle.js, щоб ініціалізувати оформлення. У цьому прикладі ми створимо посилання з класом paddle_button. Paddle.js виявить цей клас і покаже overlay-оформлення, коли посилання буде натиснуто:
<?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>
Вбудоване оформлення
Якщо ви не хочете користуватися віджетом оформлення в стилі «overlay» від Paddle, Paddle також пропонує можливість показати віджет вбудовано. Хоча цей підхід не дозволяє змінювати HTML-поля оформлення, він дає змогу вбудувати віджет у ваш застосунок.
Щоб вам було легко почати з вбудованим оформленням, Cashier містить Blade-компонент paddle-checkout. Для початку вам слід згенерувати сесію оформлення:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Далі ви можете передати сесію оформлення до атрибута checkout компонента:
<x-paddle-checkout :checkout="$checkout" class="w-full" />
Щоб змінити висоту компонента вбудованого оформлення, передайте Blade-компоненту атрибут height:
<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />
Докладніше про можливості налаштування вбудованого оформлення дивіться в посібнику Paddle щодо вбудованого оформлення і доступних налаштуваннях оформлення.
Ручний рендеринг вбудованого оформлення
Ви також можете відрендерити вбудоване оформлення вручну, не використовуючи вбудовані Blade-компоненти Laravel. Для початку згенеруйте сесію оформлення як показано в попередніх прикладах:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Далі ви можете скористатися Paddle.js, щоб ініціалізувати оформлення. У цьому прикладі ми продемонструємо це за допомогою Alpine.js; однак ви можете змінити цей приклад під свій фронтенд-стек:
<?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:
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]);
});
Далі ви можете передати сесію оформлення до Blade-компонентів кнопки Paddle чи вбудованого оформлення.
Попередній перегляд цін
Paddle дозволяє налаштовувати ціни для кожної валюти, тобто фактично задавати різні ціни для різних країн. Cashier Paddle дозволяє отримати всі ці ціни методом previewPrices. Цей метод приймає ID цін, для яких ви хочете отримати ціни:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456']);
Валюту буде визначено на основі IP-адреси запиту; однак ви можете за бажанням указати конкретну країну, для якої отримати ціни:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [
'country_code' => 'BE',
'postal_code' => '1234',
]]);
Отримавши ціни, ви можете показати їх як завгодно:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
Ви також можете показати проміжну суму й суму податку окремо:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
@endforeach
</ul>
Докладніше дивіться в документації API Paddle щодо попереднього перегляду цін.
Попередній перегляд цін для клієнта
Якщо користувач уже є клієнтом і ви хочете показати ціни, що застосовуються саме до нього, отримайте ціни безпосередньо з екземпляра клієнта:
use App\Models\User;
$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);
Внутрішньо Cashier використає ID клієнта, щоб отримати ціни в його валюті. Так, наприклад, користувач зі США побачить ціни в доларах США, а користувач з Бельгії - у євро. Якщо відповідної валюти не знайдено, буде використано валюту продукту за замовчуванням. Усі ціни продукту чи плану підписки можна налаштувати в панелі керування Paddle.
Знижки
Ви також можете показувати ціни зі знижкою. Викликаючи метод previewPrices, передайте ID знижки через опцію discount_id:
use Laravel\Paddle\Cashier;
$prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
'discount_id' => 'dsc_123'
]);
Далі покажіть обчислені ціни:
<ul>
@foreach ($prices as $price)
<li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
@endforeach
</ul>
Клієнти
Значення за замовчуванням для клієнта
Cashier дозволяє визначити кілька корисних значень за замовчуванням для ваших клієнтів під час створення сесій оформлення. Ці значення дозволяють заздалегідь заповнити адресу електронної пошти та ім'я клієнта, щоб той міг одразу перейти до платіжної частини віджета оформлення. Задати ці значення можна, перевизначивши такі методи на своїй моделі з білінгом:
/**
* 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;
}
Ці значення за замовчуванням використовуватимуться для кожної дії в Cashier, яка генерує сесію оформлення.
Отримання клієнтів
Ви можете отримати клієнта за його Paddle Customer ID методом Cashier::findBillable. Цей метод поверне екземпляр моделі з білінгом:
use Laravel\Paddle\Cashier;
$user = Cashier::findBillable($customerId);
Створення клієнтів
Іноді ви можете захотіти створити клієнта Paddle, не починаючи підписки. Зробити це можна методом createAsCustomer:
$customer = $user->createAsCustomer();
Повертається екземпляр Laravel\Paddle\Customer. Щойно клієнта створено в Paddle, ви можете почати підписку пізніше. Ви можете передати необов'язковий масив $options, щоб указати будь-які додаткові параметри створення клієнта, які підтримує API Paddle:
$customer = $user->createAsCustomer($options);
Підписки
Створення підписок
Щоб створити підписку, спершу отримайте з бази даних екземпляр вашої моделі з білінгом, яким зазвичай буде екземпляр App\Models\User. Отримавши екземпляр моделі, ви можете скористатися методом subscribe, щоб створити сесію оформлення для моделі:
use Illuminate\Http\Request;
Route::get('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe($premium = 'pri_123', 'default')
->returnTo(route('home'));
return view('billing', ['checkout' => $checkout]);
});
Перший аргумент методу subscribe - конкретна ціна, на яку підписується користувач. Це значення має відповідати ідентифікатору ціни в Paddle. Метод returnTo приймає URL, на який буде перенаправлено вашого користувача після успішного завершення оформлення. Другим аргументом методу subscribe має бути внутрішній «тип» підписки. Якщо ваш застосунок пропонує лише одну підписку, ви можете назвати її default чи primary. Цей тип підписки призначений лише для внутрішнього використання застосунком і не має показуватися користувачам. Крім того, він не повинен містити пробілів, і його ніколи не слід змінювати після створення підписки.
Ви також можете передати масив власних метаданих щодо підписки методом customData:
$checkout = $request->user()->subscribe($premium = 'pri_123', 'default')
->customData(['key' => 'value'])
->returnTo(route('home'));
Щойно сесію оформлення підписки створено, її можна передати до Blade-компонента paddle-button, який входить до Cashier Paddle:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Subscribe
</x-paddle-button>
Після того як користувач завершить оформлення, Paddle надішле вебхук subscription_created. Cashier отримає цей вебхук і налаштує підписку для вашого клієнта. Щоб переконатися, що всі вебхуки належно отримуються й обробляються вашим застосунком, переконайтеся, що ви правильно налаштували обробку вебхуків.
Перевірка стану підписки
Щойно користувач підписався на ваш застосунок, ви можете перевіряти стан його підписки різними зручними методами. По-перше, метод subscribed повертає true, якщо користувач має дійсну підписку, навіть якщо вона наразі в межах пробного періоду:
if ($user->subscribed()) {
// ...
}
Якщо ваш застосунок пропонує кілька підписок, ви можете вказати підписку під час виклику методу subscribed:
if ($user->subscribed('default')) {
// ...
}
Метод subscribed також чудово підходить для middleware маршруту, дозволяючи фільтрувати доступ до маршрутів і контролерів на основі стану підписки користувача:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsSubscribed
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->user() && ! $request->user()->subscribed()) {
// This user is not a paying customer...
return redirect('/billing');
}
return $next($request);
}
}
Якщо ви хочете визначити, чи користувач досі в межах пробного періоду, скористайтеся методом onTrial. Цей метод може бути корисним, щоб визначити, чи слід показати користувачеві попередження про те, що він досі на пробному періоді:
if ($user->subscription()->onTrial()) {
// ...
}
Метод subscribedToPrice можна використати, щоб визначити, чи підписаний користувач на певний план на основі заданого ID ціни Paddle. У цьому прикладі ми визначимо, чи підписка користувача default активно підписана на місячну ціну:
if ($user->subscribedToPrice($monthly = 'pri_123', 'default')) {
// ...
}
Метод recurring можна використати, щоб визначити, чи має користувач наразі активну підписку і чи вийшов він уже за межі пробного чи пільгового періоду:
if ($user->subscription()->recurring()) {
// ...
}
Стан скасованої підписки
Щоб визначити, чи був користувач колись активним підписником, але скасував свою підписку, скористайтеся методом canceled:
if ($user->subscription()->canceled()) {
// ...
}
Ви також можете визначити, чи скасував користувач підписку, але досі перебуває в «пільговому періоді», доки підписка повністю не спливе. Наприклад, якщо користувач скасує 5 березня підписку, яка спочатку мала спливти 10 березня, він перебуватиме в «пільговому періоді» до 10 березня. Крім того, метод subscribed протягом цього часу все ще повертатиме true:
if ($user->subscription()->onGracePeriod()) {
// ...
}
Стан простроченої оплати
Якщо платіж за підпискою не пройде, її буде позначено як past_due. Коли ваша підписка в цьому стані, вона не буде активною, доки клієнт не оновить свою платіжну інформацію. Визначити, чи підписка прострочена, можна методом pastDue на екземплярі підписки:
if ($user->subscription()->pastDue()) {
// ...
}
Коли підписка прострочена, вам слід указати користувачеві оновити свою платіжну інформацію.
Якщо ви хочете, щоб підписки все ще вважалися дійсними у стані past_due, скористайтеся методом keepPastDueSubscriptionsActive, який надає Cashier. Зазвичай цей метод слід викликати в методі register вашого AppServiceProvider:
use Laravel\Paddle\Cashier;
/**
* Register any application services.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
}
Коли підписка у стані
past_due, її не можна змінити, доки платіжну інформацію не буде оновлено. Тому методиswapіupdateQuantityвидадуть виняток, коли підписка у станіpast_due.
Скопи підписок
Більшість станів підписки також доступні як скопи запитів, тож ви можете легко шукати у своїй базі даних підписки в певному стані:
// Get all valid subscriptions...
$subscriptions = Subscription::query()->valid()->get();
// Get all of the canceled subscriptions for a user...
$subscriptions = $user->subscriptions()->canceled()->get();
Повний список доступних скопів наведено нижче:
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();
Разові списання за підпискою
Разові списання за підпискою дозволяють стягнути з підписників одноразову плату понад їхні підписки. Викликаючи метод charge, ви маєте вказати один чи кілька ID цін:
// Charge a single price...
$response = $user->subscription()->charge('pri_123');
// Charge multiple prices at once...
$response = $user->subscription()->charge(['pri_123', 'pri_456']);
Метод charge фактично не стягне кошти з клієнта до наступного розрахункового інтервалу його підписки. Якщо ви хочете виставити рахунок клієнту негайно, скористайтеся натомість методом chargeAndInvoice:
$response = $user->subscription()->chargeAndInvoice('pri_123');
Оновлення платіжної інформації
Paddle завжди зберігає платіжний метод для кожної підписки. Якщо ви хочете оновити платіжний метод за замовчуванням для підписки, вам слід перенаправити клієнта на розміщену в Paddle сторінку оновлення платіжного методу методом redirectToUpdatePaymentMethod на моделі підписки:
use Illuminate\Http\Request;
Route::get('/update-payment-method', function (Request $request) {
$user = $request->user();
return $user->subscription()->redirectToUpdatePaymentMethod();
});
Коли користувач завершить оновлення своєї інформації, Paddle надішле вебхук subscription_updated, і деталі підписки буде оновлено в базі даних вашого застосунку.
Зміна планів
Після того як користувач підписався на ваш застосунок, він може час від часу хотіти перейти на новий план підписки. Щоб оновити план підписки для користувача, передайте ідентифікатор ціни Paddle до методу swap підписки:
use App\Models\User;
$user = User::find(1);
$user->subscription()->swap($premium = 'pri_456');
Якщо ви хочете змінити план і одразу виставити рахунок користувачеві, не чекаючи наступного розрахункового циклу, скористайтеся методом swapAndInvoice:
$user = User::find(1);
$user->subscription()->swapAndInvoice($premium = 'pri_456');
Пропорційний перерахунок
За замовчуванням Paddle робить пропорційний перерахунок платежів під час зміни планів. Метод noProrate можна використати, щоб оновити підписки без пропорційного перерахунку платежів:
$user->subscription('default')->noProrate()->swap($premium = 'pri_456');
Якщо ви хочете вимкнути пропорційний перерахунок і одразу виставити рахунок клієнтам, скористайтеся методом swapAndInvoice у поєднанні з noProrate:
$user->subscription('default')->noProrate()->swapAndInvoice($premium = 'pri_456');
Або ж, щоб не стягувати з клієнта плату за зміну підписки, скористайтеся методом doNotBill:
$user->subscription('default')->doNotBill()->swap($premium = 'pri_456');
Докладніше про політики пропорційного перерахунку в Paddle дивіться в документації Paddle щодо пропорційного перерахунку.
Кількість у підписці
Іноді на підписки впливає «кількість». Наприклад, застосунок для керування проєктами може стягувати $10 на місяць за проєкт. Щоб легко збільшити чи зменшити кількість у підписці, скористайтеся методами incrementQuantity і decrementQuantity:
$user = User::find(1);
$user->subscription()->incrementQuantity();
// Add five to the subscription's current quantity...
$user->subscription()->incrementQuantity(5);
$user->subscription()->decrementQuantity();
// Subtract five from the subscription's current quantity...
$user->subscription()->decrementQuantity(5);
Як альтернативу ви можете задати конкретну кількість методом updateQuantity:
$user->subscription()->updateQuantity(10);
Метод noProrate можна використати, щоб оновити кількість у підписці без пропорційного перерахунку платежів:
$user->subscription()->noProrate()->updateQuantity(10);
Кількості для підписок з кількома продуктами
Якщо ваша підписка є підпискою з кількома продуктами, вам слід передати ID ціни, кількість якої ви хочете збільшити чи зменшити, другим аргументом методів increment / decrement:
$user->subscription()->incrementQuantity(1, 'price_chat');
Підписки з кількома продуктами
Підписка з кількома продуктами дозволяє призначити одній підписці кілька продуктів білінгу. Наприклад, уявіть, що ви створюєте застосунок «служби підтримки» з базовою ціною підписки $10 на місяць, але пропонуєте додатковий продукт живого чату за додаткові $15 на місяць.
Створюючи сесії оформлення підписки, ви можете вказати кілька продуктів для певної підписки, передавши масив цін першим аргументом методу 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]);
});
У наведеному вище прикладі клієнт матиме дві ціни, прив'язані до його підписки default. Обидві ціни стягуватимуться у відповідні розрахункові інтервали. За потреби ви можете передати асоціативний масив пар ключ / значення, щоб указати конкретну кількість для кожної ціни:
$user = User::find(1);
$checkout = $user->subscribe('default', ['price_monthly', 'price_chat' => 5]);
Якщо ви хочете додати ще одну ціну до наявної підписки, вам потрібно скористатися методом swap підписки. Викликаючи метод swap, вам слід також включити поточні ціни й кількості підписки:
$user = User::find(1);
$user->subscription()->swap(['price_chat', 'price_original' => 2]);
Наведений вище приклад додасть нову ціну, але клієнту не буде виставлено рахунок за неї до наступного розрахункового циклу. Якщо ви хочете виставити рахунок клієнту негайно, скористайтеся методом swapAndInvoice:
$user->subscription()->swapAndInvoice(['price_chat', 'price_original' => 2]);
Ви можете прибрати ціни з підписок методом swap, опустивши ціну, яку хочете прибрати:
$user->subscription()->swap(['price_original' => 2]);
Ви не можете прибрати останню ціну в підписці. Натомість вам слід просто скасувати підписку.
Кілька підписок
Paddle дозволяє вашим клієнтам мати кілька підписок одночасно. Наприклад, ви можете керувати спортзалом, який пропонує підписку на плавання й підписку на важку атлетику, і кожна підписка може мати різну ціну. Звісно, клієнти мають мати змогу підписатися на один чи обидва плани.
Коли ваш застосунок створює підписки, ви можете передати тип підписки до методу subscribe другим аргументом. Типом може бути будь-який рядок, що представляє тип підписки, яку починає користувач:
use Illuminate\Http\Request;
Route::post('/swimming/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe($swimmingMonthly = 'pri_123', 'swimming');
return view('billing', ['checkout' => $checkout]);
});
У цьому прикладі ми розпочали для клієнта місячну підписку на плавання. Однак згодом він може захотіти перейти на річну підписку. Коригуючи підписку клієнта, ми можемо просто змінити ціну в підписці swimming:
$user->subscription('swimming')->swap($swimmingYearly = 'pri_456');
Звісно, ви також можете скасувати підписку повністю:
$user->subscription('swimming')->cancel();
Призупинення підписок
Щоб призупинити підписку, викличте метод pause на підписці користувача:
$user->subscription()->pause();
Коли підписку призупинено, Cashier автоматично встановить колонку paused_at у вашій базі даних. Ця колонка використовується, щоб визначити, коли метод paused має почати повертати true. Наприклад, якщо клієнт призупинить підписку 1 березня, але поновлення підписки було заплановане лише на 5 березня, метод paused продовжуватиме повертати false до 5 березня. Так відбувається тому, що користувачеві зазвичай дозволено користуватися застосунком до кінця його розрахункового циклу.
За замовчуванням призупинення відбувається в наступному розрахунковому інтервалі, тож клієнт може використати залишок оплаченого періоду. Якщо ви хочете призупинити підписку негайно, скористайтеся методом pauseNow:
$user->subscription()->pauseNow();
За допомогою методу pauseUntil ви можете призупинити підписку до конкретного моменту часу:
$user->subscription()->pauseUntil(now()->plus(months: 1));
Або ж ви можете скористатися методом pauseNowUntil, щоб негайно призупинити підписку до заданого моменту часу:
$user->subscription()->pauseNowUntil(now()->plus(months: 1));
Визначити, чи користувач призупинив підписку, але досі перебуває в «пільговому періоді», можна методом onPausedGracePeriod:
if ($user->subscription()->onPausedGracePeriod()) {
// ...
}
Щоб відновити призупинену підписку, викличте на ній метод resume:
$user->subscription()->resume();
Підписку не можна змінювати, доки її призупинено. Якщо ви хочете перейти на інший план чи оновити кількості, спершу потрібно відновити підписку.
Скасування підписок
Щоб скасувати підписку, викличте метод cancel на підписці користувача:
$user->subscription()->cancel();
Коли підписку скасовано, Cashier автоматично встановить колонку ends_at у вашій базі даних. Ця колонка використовується, щоб визначити, коли метод subscribed має почати повертати false. Наприклад, якщо клієнт скасує підписку 1 березня, але завершення підписки було заплановане лише на 5 березня, метод subscribed продовжуватиме повертати true до 5 березня. Так зроблено тому, що користувачеві зазвичай дозволено користуватися застосунком до кінця його розрахункового циклу.
Визначити, чи користувач скасував підписку, але досі перебуває в «пільговому періоді», можна методом onGracePeriod:
if ($user->subscription()->onGracePeriod()) {
// ...
}
Якщо ви хочете скасувати підписку негайно, викличте на ній метод cancelNow:
$user->subscription()->cancelNow();
Щоб зупинити скасування підписки, яка перебуває в пільговому періоді, викличте метод stopCancelation:
$user->subscription()->stopCancelation();
Підписки Paddle не можна відновити після скасування. Якщо ваш клієнт захоче відновити свою підписку, йому доведеться створити нову.
Пробні періоди підписок
З платіжним методом наперед
Якщо ви хочете пропонувати клієнтам пробні періоди, водночас збираючи інформацію про платіжний метод наперед, вам слід задати тривалість пробного періоду в панелі Paddle для ціни, на яку підписується ваш клієнт. Далі ініціюйте сесію оформлення як зазвичай:
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]);
});
Коли ваш застосунок отримає подію subscription_created, Cashier встановить дату завершення пробного періоду в записі підписки в базі даних вашого застосунку, а також вкаже Paddle не починати стягувати кошти з клієнта до цієї дати.
Якщо підписку клієнта не буде скасовано до дати завершення пробного періоду, кошти з нього спишуть одразу після його спливання, тож обов'язково повідомляйте своїх користувачів про дату завершення пробного періоду.
Визначити, чи користувач у межах пробного періоду, можна методом onTrial на екземплярі користувача:
if ($user->onTrial()) {
// ...
}
Щоб визначити, чи наявний пробний період сплив, скористайтеся методом hasExpiredTrial:
if ($user->hasExpiredTrial()) {
// ...
}
Щоб визначити, чи користувач на пробному періоді для конкретного типу підписки, передайте тип до методів onTrial чи hasExpiredTrial:
if ($user->onTrial('default')) {
// ...
}
if ($user->hasExpiredTrial('default')) {
// ...
}
Без платіжного методу наперед
Якщо ви хочете пропонувати пробні періоди, не збираючи інформацію про платіжний метод користувача наперед, ви можете встановити колонку trial_ends_at у записі клієнта, прив'язаному до вашого користувача, на бажану дату завершення пробного періоду. Зазвичай це роблять під час реєстрації користувача:
use App\Models\User;
$user = User::create([
// ...
]);
$user->createAsCustomer([
'trial_ends_at' => now()->plus(days: 10)
]);
Cashier називає такий тип пробного періоду «загальним пробним періодом», оскільки він не прив'язаний до жодної наявної підписки. Метод onTrial на екземплярі User поверне true, якщо поточна дата не перевищує значення trial_ends_at:
if ($user->onTrial()) {
// User is within their trial period...
}
Щойно ви будете готові створити для користувача справжню підписку, скористайтеся методом subscribe як зазвичай:
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]);
});
Щоб отримати дату завершення пробного періоду користувача, скористайтеся методом trialEndsAt. Цей метод поверне екземпляр дати Carbon, якщо користувач на пробному періоді, або null, якщо ні. Ви також можете передати необов'язковий параметр типу підписки, якщо хочете отримати дату завершення пробного періоду для конкретної підписки, відмінної від типової:
if ($user->onTrial('default')) {
$trialEndsAt = $user->trialEndsAt();
}
Ви можете скористатися методом onGenericTrial, якщо хочете дізнатися саме те, що користувач перебуває в «загальному» пробному періоді і ще не створив справжньої підписки:
if ($user->onGenericTrial()) {
// User is within their "generic" trial period...
}
Продовження чи активація пробного періоду
Ви можете продовжити наявний пробний період підписки, викликавши метод extendTrial і вказавши момент часу, коли пробний період має завершитися:
$user->subscription()->extendTrial(now()->plus(days: 5));
Або ж ви можете негайно активувати підписку, завершивши її пробний період, викликавши на ній метод activate:
$user->subscription()->activate();
Обробка вебхуків Paddle
Paddle може сповіщати ваш застосунок про різні події через вебхуки. За замовчуванням сервіс-провайдер Cashier реєструє маршрут, що вказує на контролер вебхуків Cashier. Цей контролер оброблятиме всі вхідні запити вебхуків.
За замовчуванням цей контролер автоматично оброблятиме скасування підписок із занадто великою кількістю невдалих списань, оновлення підписок і зміни платіжного методу; однак, як ми невдовзі побачимо, ви можете розширити цей контролер, щоб обробляти будь-яку подію вебхука Paddle, яку забажаєте.
Щоб ваш застосунок міг обробляти вебхуки Paddle, обов'язково налаштуйте URL вебхука в панелі керування Paddle. За замовчуванням контролер вебхуків Cashier відповідає за шляхом URL /paddle/webhook. Повний список усіх вебхуків, які вам слід увімкнути в панелі керування Paddle:
- Customer Updated
- Transaction Completed
- Transaction Updated
- Subscription Created
- Subscription Updated
- Subscription Paused
- Subscription Canceled
Обов'язково захистіть вхідні запити за допомогою
middlewareперевірки підпису вебхука, що входить до Cashier.
Вебхуки й захист від CSRF
Оскільки вебхуки Paddle мають обходити захист від CSRF у Laravel, вам слід подбати, щоб Laravel не намагався перевіряти CSRF-токен для вхідних вебхуків Paddle. Щоб досягти цього, виключіть paddle/* із захисту від CSRF у файлі bootstrap/app.php вашого застосунку:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(except: [
'paddle/*',
]);
})
Вебхуки й локальна розробка
Щоб Paddle міг надсилати вашому застосунку вебхуки під час локальної розробки, вам потрібно буде відкрити свій застосунок через сервіс спільного доступу до сайтів, як-от Ngrok чи Expose. Якщо ви розробляєте застосунок локально за допомогою Laravel Sail, ви можете скористатися командою спільного доступу до сайту в Sail.
Визначення обробників подій вебхуків
Cashier автоматично обробляє скасування підписки за невдалих списань та інші поширені вебхуки Paddle. Однак, якщо у вас є додаткові події вебхуків, які ви хочете обробляти, ви можете зробити це, слухаючи такі події, які диспетчеризує Cashier:
Laravel\Paddle\Events\WebhookReceivedLaravel\Paddle\Events\WebhookHandled
Обидві події містять повні дані вебхука Paddle. Наприклад, якщо ви хочете обробити вебхук transaction.billed, зареєструйте слухача, який оброблятиме подію:
<?php
namespace App\Listeners;
use Laravel\Paddle\Events\WebhookReceived;
class PaddleEventListener
{
/**
* Handle received Paddle webhooks.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['event_type'] === 'transaction.billed') {
// Handle the incoming event...
}
}
}
Cashier також випромінює події, присвячені типу отриманого вебхука. Окрім повних даних від Paddle, вони також містять релевантні моделі, які було використано для обробки вебхука, як-от модель з білінгом, підписку чи квитанцію:
Laravel\Paddle\Events\CustomerUpdatedLaravel\Paddle\Events\TransactionCompletedLaravel\Paddle\Events\TransactionUpdatedLaravel\Paddle\Events\SubscriptionCreatedLaravel\Paddle\Events\SubscriptionUpdatedLaravel\Paddle\Events\SubscriptionPausedLaravel\Paddle\Events\SubscriptionCanceled
Ви також можете перевизначити вбудований маршрут вебхука за замовчуванням, визначивши змінну оточення CASHIER_WEBHOOK у файлі .env вашого застосунку. Це значення має бути повним URL до вашого маршруту вебхука і має збігатися з URL, заданим у вашій панелі керування Paddle:
CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url
Перевірка підписів вебхуків
Щоб захистити свої вебхуки, ви можете скористатися підписами вебхуків Paddle. Для зручності Cashier автоматично містить middleware, який перевіряє, що вхідний запит вебхука Paddle є дійсним.
Щоб увімкнути перевірку вебхуків, переконайтеся, що змінну оточення PADDLE_WEBHOOK_SECRET визначено у файлі .env вашого застосунку. Секрет вебхука можна отримати з панелі вашого облікового запису Paddle.
Разові списання
Списання за продукти
Якщо ви хочете ініціювати купівлю продукту для клієнта, скористайтеся методом checkout на екземплярі моделі з білінгом, щоб згенерувати сесію оформлення для покупки. Метод checkout приймає один чи кілька ID цін. За потреби можна скористатися асоціативним масивом, щоб указати кількість продукту, який купують:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $request->user()->checkout(['pri_tshirt', 'pri_socks' => 5]);
return view('buy', ['checkout' => $checkout]);
});
Згенерувавши сесію оформлення, ви можете скористатися наданим Cashier Blade-компонентом paddle-button, щоб дозволити користувачеві переглянути віджет оформлення Paddle і завершити покупку:
<x-paddle-button :checkout="$checkout" class="px-8 py-4">
Buy
</x-paddle-button>
Сесія оформлення має метод customData, який дозволяє передати будь-які власні дані до створення транзакції, що лежить в основі. Докладніше про доступні вам опції під час передавання власних даних дивіться в документації Paddle:
$checkout = $user->checkout('pri_tshirt')
->customData([
'custom_option' => $value,
]);
Повернення коштів за транзакціями
Повернення коштів за транзакціями поверне повернуту суму на платіжний метод вашого клієнта, який використовувався під час покупки. Якщо вам потрібно повернути кошти за покупку в Paddle, скористайтеся методом refund на моделі Cashier\Paddle\Transaction. Цей метод приймає причину як перший аргумент, а також один чи кілька ID цін для повернення з необов'язковими сумами у вигляді асоціативного масиву. Отримати транзакції для певної моделі з білінгом можна методом transactions.
Наприклад, уявімо, що ми хочемо повернути кошти за конкретною транзакцією для цін pri_123 і pri_456. Ми хочемо повністю повернути pri_123, але повернути лише два долари за pri_456:
use App\Models\User;
$user = User::find(1);
$transaction = $user->transactions()->first();
$response = $transaction->refund('Accidental charge', [
'pri_123', // Fully refund this price...
'pri_456' => 200, // Only partially refund this price...
]);
Наведений вище приклад повертає кошти за конкретні позиції транзакції. Якщо ви хочете повернути кошти за всю транзакцію, просто вкажіть причину:
$response = $transaction->refund('Accidental charge');
Докладніше про повернення коштів дивіться в документації Paddle щодо повернень.
Повернення коштів завжди має бути схвалене Paddle, перш ніж буде оброблене повністю.
Кредитування транзакцій
Так само як і повертати кошти, ви можете кредитувати транзакції. Кредитування транзакцій додасть кошти на баланс клієнта, щоб їх можна було використати для майбутніх покупок. Кредитувати можна лише транзакції, зібрані вручну, а не автоматично зібрані транзакції (як-от підписки), оскільки Paddle обробляє кредити за підписками автоматично:
$transaction = $user->transactions()->first();
// Credit a specific line item fully...
$response = $transaction->credit('Compensation', 'pri_123');
Докладніше дивіться в документації Paddle щодо кредитування.
Кредити можна застосувати лише до зібраних вручну транзакцій. Автоматично зібрані транзакції кредитує сам Paddle.
Транзакції
Ви можете легко отримати масив транзакцій моделі з білінгом через властивість transactions:
use App\Models\User;
$user = User::find(1);
$transactions = $user->transactions;
Транзакції представляють платежі за ваші продукти й покупки і супроводжуються рахунками. У базі даних вашого застосунку зберігаються лише завершені транзакції.
Перелічуючи транзакції клієнта, ви можете скористатися методами екземпляра транзакції, щоб показати релевантну платіжну інформацію. Наприклад, ви можете захотіти перелічити кожну транзакцію в таблиці, дозволивши користувачеві легко завантажити будь-який із рахунків:
<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>
Маршрут download-invoice може виглядати так:
use Illuminate\Http\Request;
use Laravel\Paddle\Transaction;
Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) {
return $transaction->redirectToInvoicePdf();
})->name('download-invoice');
Минулі та майбутні платежі
Ви можете скористатися методами lastPayment і nextPayment, щоб отримати й показати минулі чи майбутні платежі клієнта за періодичними підписками:
use App\Models\User;
$user = User::find(1);
$subscription = $user->subscription();
$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();
Обидва ці методи повернуть екземпляр Laravel\Paddle\Payment; однак lastPayment поверне null, коли транзакції ще не синхронізовано вебхуками, а nextPayment поверне null, коли розрахунковий цикл завершився (наприклад, коли підписку скасовано):
Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}
Тестування
Під час тестування вам слід вручну перевірити свій потік білінгу, щоб переконатися, що ваша інтеграція працює як очікується.
Для автоматизованих тестів, зокрема тих, що виконуються в CI-середовищі, ви можете скористатися HTTP-клієнтом Laravel, щоб підробити HTTP-виклики до Paddle. Хоча це не тестує фактичні відповіді від Paddle, воно дає спосіб тестувати ваш застосунок, не викликаючи API Paddle насправді.