跳轉至內容

Laravel Cashier (Paddle)

簡介

本文件適用於 Cashier Paddle 2.x 與 Paddle Billing 的整合。如果您仍在使用 Paddle Classic,請使用 Cashier Paddle 1.x

Laravel Cashier PaddlePaddle 的訂閱計費服務提供了一個富有表現力的、流暢的介面。它處理了幾乎所有令人生厭的樣板化訂閱計費程式碼。除了基本的訂閱管理外,Cashier 還可以處理:切換訂閱、訂閱“數量”、訂閱暫停、取消寬限期等。

在深入瞭解 Cashier Paddle 之前,我們建議您同時查閱 Paddle 的 概念指南API 文件

升級 Cashier

升級到新版本的 Cashier 時,請務必仔細查閱 升級指南

安裝

首先,使用 Composer 包管理器安裝適用於 Paddle 的 Cashier 包

1composer require laravel/cashier-paddle

接下來,您應該使用 vendor:publish Artisan 命令釋出 Cashier 遷移檔案

1php artisan vendor:publish --tag="cashier-migrations"

然後,執行應用程式的資料庫遷移。Cashier 遷移將建立一個新的 customers 表。此外,還會建立新的 subscriptionssubscription_items 表來儲存客戶的所有訂閱資訊。最後,會建立一個新的 transactions 表來儲存與客戶關聯的所有 Paddle 交易記錄。

1php artisan migrate

為確保 Cashier 正確處理所有 Paddle 事件,請記得 設定 Cashier 的 Webhook 處理程式

Paddle 沙盒

在本地和測試開發期間,您應該 註冊一個 Paddle 沙盒賬戶。此賬戶將為您提供一個沙盒環境,以便在不進行實際支付的情況下測試和開發應用程式。您可以使用 Paddle 的 測試卡號 來模擬各種支付場景。

使用 Paddle 沙盒環境時,應在應用程式的 .env 檔案中將 PADDLE_SANDBOX 環境變數設定為 true

1PADDLE_SANDBOX=true

完成應用程式開發後,您可以 申請一個 Paddle 供應商賬戶。在應用程式投入生產之前,Paddle 需要稽核您應用程式的域名。

配置

計費模型 (Billable Model)

在使用 Cashier 之前,必須在使用者模型定義中新增 Billable trait。該 trait 提供了各種方法,允許您執行常見的計費任務,例如建立訂閱和更新支付方式資訊。

1use Laravel\Paddle\Billable;
2 
3class User extends Authenticatable
4{
5 use Billable;
6}

如果您有非使用者的計費實體,也可以將該 trait 新增到這些類中。

1use Illuminate\Database\Eloquent\Model;
2use Laravel\Paddle\Billable;
3 
4class Team extends Model
5{
6 use Billable;
7}

API 金鑰

接下來,在應用程式的 .env 檔案中配置您的 Paddle 金鑰。您可以從 Paddle 控制面板獲取您的 Paddle API 金鑰。

1PADDLE_CLIENT_SIDE_TOKEN=your-paddle-client-side-token
2PADDLE_API_KEY=your-paddle-api-key
3PADDLE_RETAIN_KEY=your-paddle-retain-key
4PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret"
5PADDLE_SANDBOX=true

使用 Paddle 沙盒環境時,應將 PADDLE_SANDBOX 環境變數設定為 true。如果您將應用程式部署到生產環境並使用 Paddle 的正式供應商環境,則應將 PADDLE_SANDBOX 變數設定為 false

PADDLE_RETAIN_KEY 是可選的,僅當您將 Paddle 與 Retain 配合使用時才需要設定。

Paddle JS

Paddle 依賴其自身的 JavaScript 庫來啟動 Paddle 結賬小部件。您可以透過在應用程式佈局的結束 </head> 標籤之前放置 @paddleJS Blade 指令來載入該 JavaScript 庫。

1<head>
2 ...
3 
4 @paddleJS
5</head>

貨幣配置

您可以指定在發票上顯示貨幣數值時使用的語言環境。在內部,Cashier 使用 PHP 的 NumberFormatter 來設定貨幣語言環境。

1CASHIER_CURRENCY_LOCALE=nl_BE

為了使用除 en 以外的語言環境,請確保您的伺服器上已安裝並配置了 ext-intl PHP 擴充套件。

覆蓋預設模型

您可以透過定義自己的模型並擴充套件相應的 Cashier 模型,自由地擴充套件 Cashier 在內部使用的模型。

1use Laravel\Paddle\Subscription as CashierSubscription;
2 
3class Subscription extends CashierSubscription
4{
5 // ...
6}

定義模型後,您可以透過 Laravel\Paddle\Cashier 類指示 Cashier 使用您的自定義模型。通常,您應該在應用程式 App\Providers\AppServiceProvider 類的 boot 方法中告知 Cashier 有關您的自定義模型。

1use App\Models\Cashier\Subscription;
2use App\Models\Cashier\Transaction;
3 
4/**
5 * Bootstrap any application services.
6 */
7public function boot(): void
8{
9 Cashier::useSubscriptionModel(Subscription::class);
10 Cashier::useTransactionModel(Transaction::class);
11}

快速入門

銷售產品

在使用 Paddle 結賬之前,您應該在 Paddle 儀表板中定義具有固定價格的產品。此外,您還應該 配置 Paddle 的 Webhook 處理程式

透過應用程式提供產品和訂閱計費可能會讓人望而生畏。然而,得益於 Cashier 和 Paddle 結賬浮層 (Checkout Overlay),您可以輕鬆構建現代、穩健的支付整合。

要對非經常性的單次購買產品進行扣費,我們將利用 Cashier 透過 Paddle 的結賬浮層向客戶收費,客戶將在其中提供支付詳情並確認購買。一旦透過結賬浮層完成付款,客戶將被重定向到您在應用程式內選擇的成功 URL。

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $request->user()->checkout('pri_deluxe_album')
5 ->returnTo(route('dashboard'));
6 
7 return view('buy', ['checkout' => $checkout]);
8})->name('checkout');

如上述示例所示,我們將利用 Cashier 提供的 checkout 方法建立一個結賬物件,為客戶展示給定“價格標識符”的 Paddle 結賬浮層。使用 Paddle 時,“價格”是指 為特定產品定義的價格

如有必要,checkout 方法會自動在 Paddle 中建立客戶,並將該 Paddle 客戶記錄連線到您應用程式資料庫中的相應使用者。完成結賬會話後,客戶將被重定向到一個專門的成功頁面,您可以在該頁面向客戶顯示資訊性訊息。

buy 檢視中,我們將包含一個按鈕來顯示結賬浮層。paddle-button Blade 元件隨 Cashier Paddle 一起提供;但是,您也可以 手動渲染浮層結賬

1<x-paddle-button :checkout="$checkout" class="px-8 py-4">
2 Buy Product
3</x-paddle-button>

向 Paddle 結賬提供元資料

銷售產品時,通常需要透過您自己應用程式定義的 CartOrder 模型來跟蹤已完成的訂單和已購買的產品。將客戶重定向到 Paddle 結賬浮層以完成購買時,您可能需要提供現有的訂單識別符號,以便在客戶重定向回您的應用程式時,將已完成的購買與相應的訂單關聯起來。

為實現這一點,您可以向 checkout 方法提供一個自定義資料陣列。假設當用戶開始結賬流程時,在我們的應用程式中建立了一個待處理的 Order。請記住,此示例中的 CartOrder 模型僅為說明之用,並非由 Cashier 提供。您可以根據自己應用程式的需求自由實現這些概念。

1use App\Models\Cart;
2use App\Models\Order;
3use Illuminate\Http\Request;
4 
5Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
6 $order = Order::create([
7 'cart_id' => $cart->id,
8 'price_ids' => $cart->price_ids,
9 'status' => 'incomplete',
10 ]);
11 
12 $checkout = $request->user()->checkout($order->price_ids)
13 ->customData(['order_id' => $order->id]);
14 
15 return view('billing', ['checkout' => $checkout]);
16})->name('checkout');

如上述示例所示,當用戶開始結賬流程時,我們將向 checkout 方法提供所有與購物車/訂單相關的 Paddle 價格標識符。當然,您的應用程式有責任在客戶新增商品時將這些專案與“購物車”或訂單關聯起來。我們還透過 customData 方法將訂單 ID 提供給 Paddle 結賬浮層。

當然,您可能希望在客戶完成結賬流程後將訂單標記為“完成”。為此,您可以監聽 Paddle 分派並由 Cashier 引發的 Webhook 事件,將訂單資訊儲存在您的資料庫中。

首先,監聽由 Cashier 分派的 TransactionCompleted 事件。通常,您應該在應用程式 AppServiceProviderboot 方法中註冊事件監聽器。

1use App\Listeners\CompleteOrder;
2use Illuminate\Support\Facades\Event;
3use Laravel\Paddle\Events\TransactionCompleted;
4 
5/**
6 * Bootstrap any application services.
7 */
8public function boot(): void
9{
10 Event::listen(TransactionCompleted::class, CompleteOrder::class);
11}

在此示例中,CompleteOrder 監聽器可能如下所示

1namespace App\Listeners;
2 
3use App\Models\Order;
4use Laravel\Paddle\Cashier;
5use Laravel\Paddle\Events\TransactionCompleted;
6 
7class CompleteOrder
8{
9 /**
10 * Handle the incoming Cashier webhook event.
11 */
12 public function handle(TransactionCompleted $event): void
13 {
14 $orderId = $event->payload['data']['custom_data']['order_id'] ?? null;
15 
16 $order = Order::findOrFail($orderId);
17 
18 $order->update(['status' => 'completed']);
19 }
20}

有關 transaction.completed 事件所包含資料的更多資訊,請參閱 Paddle 的文件

銷售訂閱

在使用 Paddle 結賬之前,您應該在 Paddle 儀表板中定義具有固定價格的產品。此外,您還應該 配置 Paddle 的 Webhook 處理程式

透過應用程式提供產品和訂閱計費可能會讓人望而生畏。然而,得益於 Cashier 和 Paddle 結賬浮層 (Checkout Overlay),您可以輕鬆構建現代、穩健的支付整合。

為了瞭解如何使用 Cashier 和 Paddle 的結賬浮層銷售訂閱,讓我們考慮一個簡單的場景:一個包含基礎月度(price_basic_monthly)和年度(price_basic_yearly)計劃的訂閱服務。這兩個價格可以在我們的 Paddle 儀表板中歸入一個“Basic”產品(pro_basic)下。此外,我們的訂閱服務可能還提供一個名為 pro_expert 的“Expert”計劃。

首先,讓我們瞭解客戶如何訂閱我們的服務。當然,您可以想象客戶可能會在應用程式的定價頁面上點選 Basic 計劃的“訂閱”按鈕。此按鈕將為他們選擇的計劃呼叫 Paddle 結賬浮層。首先,讓我們透過 checkout 方法啟動一個結賬會話。

1use Illuminate\Http\Request;
2 
3Route::get('/subscribe', function (Request $request) {
4 $checkout = $request->user()->checkout('price_basic_monthly')
5 ->returnTo(route('dashboard'));
6 
7 return view('subscribe', ['checkout' => $checkout]);
8})->name('subscribe');

subscribe 檢視中,我們將包含一個按鈕來顯示結賬浮層。paddle-button Blade 元件隨 Cashier Paddle 一起提供;但是,您也可以 手動渲染浮層結賬

1<x-paddle-button :checkout="$checkout" class="px-8 py-4">
2 Subscribe
3</x-paddle-button>

現在,當點選訂閱按鈕時,客戶將能夠輸入他們的支付詳情並啟動訂閱。要了解訂閱何時真正開始(因為某些支付方式需要幾秒鐘來處理),您還應該 配置 Cashier 的 Webhook 處理程式

既然客戶可以開始訂閱,我們需要限制應用程式的某些部分,以便只有已訂閱的使用者才能訪問它們。當然,我們始終可以透過 Cashier 的 Billable trait 提供的 subscribed 方法確定使用者的當前訂閱狀態。

1@if ($user->subscribed())
2 <p>You are subscribed.</p>
3@endif

我們甚至可以輕鬆確定使用者是否訂閱了特定產品或價格。

1@if ($user->subscribedToProduct('pro_basic'))
2 <p>You are subscribed to our Basic product.</p>
3@endif
4 
5@if ($user->subscribedToPrice('price_basic_monthly'))
6 <p>You are subscribed to our monthly Basic plan.</p>
7@endif

構建訂閱中介軟體

為方便起見,您可能希望建立一個 中介軟體,用於確定傳入請求是否來自已訂閱的使用者。定義此中介軟體後,您可以輕鬆將其分配給路由,以防止未訂閱的使用者訪問該路由。

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class Subscribed
10{
11 /**
12 * Handle an incoming request.
13 */
14 public function handle(Request $request, Closure $next): Response
15 {
16 if (! $request->user()?->subscribed()) {
17 // Redirect user to billing page and ask them to subscribe...
18 return redirect('/subscribe');
19 }
20 
21 return $next($request);
22 }
23}

定義中介軟體後,您可以將其分配給路由。

1use App\Http\Middleware\Subscribed;
2 
3Route::get('/dashboard', function () {
4 // ...
5})->middleware([Subscribed::class]);

允許客戶管理其計費套餐

當然,客戶可能希望將其訂閱套餐更改為其他產品或“級別”。在我們上面的示例中,我們希望允許客戶將其套餐從月度訂閱更改為年度訂閱。為此,您需要實現一個類似跳轉到以下路由的按鈕。

1use Illuminate\Http\Request;
2 
3Route::put('/subscription/{price}/swap', function (Request $request, $price) {
4 $user->subscription()->swap($price); // With "$price" being "price_basic_yearly" for this example.
5 
6 return redirect()->route('dashboard');
7})->name('subscription.swap');

除了交換套餐外,您還需要允許您的客戶取消訂閱。像交換套餐一樣,提供一個跳轉到以下路由的按鈕。

1use Illuminate\Http\Request;
2 
3Route::put('/subscription/cancel', function (Request $request, $price) {
4 $user->subscription()->cancel();
5 
6 return redirect()->route('dashboard');
7})->name('subscription.cancel');

現在,您的訂閱將在計費週期結束時取消。

只要您配置了 Cashier 的 Webhook 處理程式,Cashier 就會透過檢查來自 Paddle 的傳入 Webhook,自動保持應用程式中與 Cashier 相關的資料庫表同步。因此,例如,當您透過 Paddle 的儀表板取消客戶的訂閱時,Cashier 將接收到相應的 Webhook 並將應用程式資料庫中的訂閱標記為“已取消”。

結賬會話

大多數向客戶計費的操作都是透過 Paddle 的 結賬浮層小部件 (Checkout Overlay widget) 或利用 內嵌結賬 (inline checkout) 進行的。

在使用 Paddle 處理結賬付款之前,您應該在 Paddle 結賬設定儀表板中定義應用程式的 預設付款連結

浮層結賬

在顯示結賬浮層小部件之前,您必須使用 Cashier 生成結賬會話。結賬會話將通知結賬小部件應執行的計費操作。

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $user->checkout('pri_34567')
5 ->returnTo(route('dashboard'));
6 
7 return view('billing', ['checkout' => $checkout]);
8});

Cashier 包含一個 paddle-button Blade 元件。您可以將結賬會話作為“prop”傳遞給此元件。然後,當點選此按鈕時,將顯示 Paddle 的結賬小部件。

1<x-paddle-button :checkout="$checkout" class="px-8 py-4">
2 Subscribe
3</x-paddle-button>

預設情況下,這將使用 Paddle 的預設樣式顯示小部件。您可以透過向元件新增 Paddle 支援的屬性(例如 data-theme='light' 屬性)來自定義小部件。

1<x-paddle-button :checkout="$checkout" class="px-8 py-4" data-theme="light">
2 Subscribe
3</x-paddle-button>

Paddle 結賬小部件是非同步的。一旦使用者在小部件中建立了訂閱,Paddle 就會向您的應用程式傳送一個 Webhook,以便您可以正確更新應用程式資料庫中的訂閱狀態。因此,正確 設定 Webhook 以適應來自 Paddle 的狀態更改非常重要。

訂閱狀態更改後,接收相應 Webhook 的延遲通常很小,但您應該在應用程式中考慮到這一點,因為您的使用者在完成結賬後可能無法立即使用訂閱。

手動渲染浮層結賬

您也可以在不使用 Laravel 內建 Blade 元件的情況下手動渲染浮層結賬。首先,按照前面的示例 生成結賬會話。

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $user->checkout('pri_34567')
5 ->returnTo(route('dashboard'));
6 
7 return view('billing', ['checkout' => $checkout]);
8});

接下來,您可以使用 Paddle.js 初始化結賬。在此示例中,我們將建立一個分配了 paddle_button 類的連結。Paddle.js 將檢測此類並在點選連結時顯示浮層結賬。

1<?php
2$items = $checkout->getItems();
3$customer = $checkout->getCustomer();
4$custom = $checkout->getCustomData();
5?>
6 
7<a
8 href='#!'
9 class='paddle_button'
10 data-items='{!! json_encode($items) !!}'
11 @if ($customer) data-customer-id='{{ $customer->paddle_id }}' @endif
12 @if ($custom) data-custom-data='{{ json_encode($custom) }}' @endif
13 @if ($returnUrl = $checkout->getReturnUrl()) data-success-url='{{ $returnUrl }}' @endif
14>
15 Buy Product
16</a>

內嵌結賬

如果您不想使用 Paddle 的“浮層”式結賬小部件,Paddle 還提供了內嵌顯示小部件的選項。雖然此方法不允許您調整結賬的任何 HTML 欄位,但它允許您將小部件嵌入到您的應用程式中。

為了讓您輕鬆上手內嵌結賬,Cashier 包含一個 paddle-checkout Blade 元件。首先,您應該 生成一個結賬會話

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $user->checkout('pri_34567')
5 ->returnTo(route('dashboard'));
6 
7 return view('billing', ['checkout' => $checkout]);
8});

然後,您可以將結賬會話傳遞給元件的 checkout 屬性。

1<x-paddle-checkout :checkout="$checkout" class="w-full" />

要調整內嵌結賬元件的高度,您可以將 height 屬性傳遞給 Blade 元件。

1<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />

有關內嵌結賬自定義選項的更多詳細資訊,請查閱 Paddle 關於 內嵌結賬可用結賬設定 的指南。

手動渲染內嵌結賬

您也可以在不使用 Laravel 內建 Blade 元件的情況下手動渲染內嵌結賬。首先,按照前面的示例 生成結賬會話。

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $user->checkout('pri_34567')
5 ->returnTo(route('dashboard'));
6 
7 return view('billing', ['checkout' => $checkout]);
8});

接下來,您可以使用 Paddle.js 初始化結賬。在此示例中,我們將演示如何使用 Alpine.js;但是,您可以根據自己的前端技術棧自由修改此示例。

1<?php
2$options = $checkout->options();
3 
4$options['settings']['frameTarget'] = 'paddle-checkout';
5$options['settings']['frameInitialHeight'] = 366;
6?>
7 
8<div class="paddle-checkout" x-data="{}" x-init="
9 Paddle.Checkout.open(@json($options));
10">
11</div>

遊客結賬

有時,您可能需要為不需要在您的應用程式中建立賬戶的使用者建立結賬會話。為此,您可以使用 guest 方法。

1use Illuminate\Http\Request;
2use Laravel\Paddle\Checkout;
3 
4Route::get('/buy', function (Request $request) {
5 $checkout = Checkout::guest(['pri_34567'])
6 ->returnTo(route('home'));
7 
8 return view('billing', ['checkout' => $checkout]);
9});

然後,您可以將結賬會話提供給 Paddle 按鈕內嵌結賬 Blade 元件。

價格預覽

Paddle 允許您按貨幣自定義價格,實際上允許您為不同的國家配置不同的價格。Cashier Paddle 允許您使用 previewPrices 方法檢索所有這些價格。此方法接受您希望檢索價格的價格 ID。

1use Laravel\Paddle\Cashier;
2 
3$prices = Cashier::previewPrices(['pri_123', 'pri_456']);

貨幣將根據請求的 IP 地址確定;但是,您可以選擇提供特定國家/地區來檢索價格。

1use Laravel\Paddle\Cashier;
2 
3$prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [
4 'country_code' => 'BE',
5 'postal_code' => '1234',
6]]);

檢索價格後,您可以按照自己的意願顯示它們。

1<ul>
2 @foreach ($prices as $price)
3 <li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
4 @endforeach
5</ul>

您還可以分別顯示小計價格和稅額。

1<ul>
2 @foreach ($prices as $price)
3 <li>{{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
4 @endforeach
5</ul>

有關更多資訊,請 檢視 Paddle 關於價格預覽的 API 文件

客戶價格預覽

如果使用者已經是客戶,並且您想顯示適用於該客戶的價格,可以透過直接從客戶例項中檢索價格來實現。

1use App\Models\User;
2 
3$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);

在內部,Cashier 將使用使用者的客戶 ID 來檢索其貨幣對應的價格。因此,例如,居住在美國的使用者將看到以美元顯示的價格,而居住在比利時的使用者將看到以歐元顯示的價格。如果找不到匹配的貨幣,將使用產品的預設貨幣。您可以在 Paddle 控制面板中自定義產品或訂閱計劃的所有價格。

折扣

您還可以選擇在折扣後顯示價格。呼叫 previewPrices 方法時,可以透過 discount_id 選項提供折扣 ID。

1use Laravel\Paddle\Cashier;
2 
3$prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
4 'discount_id' => 'dsc_123'
5]);

然後,顯示計算出的價格。

1<ul>
2 @foreach ($prices as $price)
3 <li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
4 @endforeach
5</ul>

客戶

客戶預設設定

Cashier 允許您在建立結賬會話時為客戶定義一些有用的預設值。設定這些預設值允許您預填客戶的電子郵件地址和姓名,以便他們可以立即進入結賬小部件的支付部分。您可以透過覆蓋計費模型上的以下方法來設定這些預設值。

1/**
2 * Get the customer's name to associate with Paddle.
3 */
4public function paddleName(): string|null
5{
6 return $this->name;
7}
8 
9/**
10 * Get the customer's email address to associate with Paddle.
11 */
12public function paddleEmail(): string|null
13{
14 return $this->email;
15}

這些預設值將用於 Cashier 中生成 結賬會話 的每一個操作。

檢索客戶

您可以使用 Cashier::findBillable 方法透過 Paddle 客戶 ID 檢索客戶。此方法將返回計費模型的一個例項。

1use Laravel\Paddle\Cashier;
2 
3$user = Cashier::findBillable($customerId);

建立客戶

有時,您可能希望在不開始訂閱的情況下建立 Paddle 客戶。您可以使用 createAsCustomer 方法實現這一點。

1$customer = $user->createAsCustomer();

將返回 Laravel\Paddle\Customer 的一個例項。一旦在 Paddle 中建立了客戶,您就可以在以後的日期開始訂閱。您可以提供一個可選的 $options 陣列,以傳遞 Paddle API 支援的任何其他客戶建立引數

1$customer = $user->createAsCustomer($options);

訂閱

建立訂閱

要建立訂閱,首先從資料庫中檢索計費模型的例項,這通常是 App\Models\User 的一個例項。檢索到模型例項後,您可以使用 subscribe 方法來建立模型的結賬會話。

1use Illuminate\Http\Request;
2 
3Route::get('/user/subscribe', function (Request $request) {
4 $checkout = $request->user()->subscribe($premium = 'pri_123', 'default')
5 ->returnTo(route('home'));
6 
7 return view('billing', ['checkout' => $checkout]);
8});

傳遞給 subscribe 方法的第一個引數是使用者要訂閱的具體價格。此值應對應於 Paddle 中的價格標識符。returnTo 方法接受一個 URL,使用者在成功完成結賬後將被重定向到該 URL。傳遞給 subscribe 方法的第二個引數應該是訂閱的內部“型別”。如果您的應用程式只提供單一訂閱,您可以將其稱為 defaultprimary。此訂閱型別僅用於內部應用程式使用,不打算顯示給使用者。此外,它不應包含空格,並且在建立訂閱後絕不應更改。

您還可以使用 customData 方法提供有關訂閱的自定義元資料陣列。

1$checkout = $request->user()->subscribe($premium = 'pri_123', 'default')
2 ->customData(['key' => 'value'])
3 ->returnTo(route('home'));

建立訂閱結賬會話後,可以將結賬會話提供給隨 Cashier Paddle 一起提供的 paddle-button Blade 元件

1<x-paddle-button :checkout="$checkout" class="px-8 py-4">
2 Subscribe
3</x-paddle-button>

使用者完成結賬後,Paddle 將分派一個 subscription_created Webhook。Cashier 將接收此 Webhook 併為您的客戶設定訂閱。為了確保應用程式正確接收和處理所有 Webhook,請確保您已正確 設定 Webhook 處理程式

檢查訂閱狀態

一旦使用者訂閱了您的應用程式,您就可以使用各種便捷的方法檢查他們的訂閱狀態。首先,如果使用者有有效的訂閱,即使訂閱目前處於試用期,subscribed 方法也會返回 true

1if ($user->subscribed()) {
2 // ...
3}

如果您的應用程式提供多種訂閱,您可以在呼叫 subscribed 方法時指定訂閱型別。

1if ($user->subscribed('default')) {
2 // ...
3}

subscribed 方法也非常適合用作 路由中介軟體,允許您根據使用者的訂閱狀態過濾對路由和控制器的訪問。

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class EnsureUserIsSubscribed
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
15 */
16 public function handle(Request $request, Closure $next): Response
17 {
18 if ($request->user() && ! $request->user()->subscribed()) {
19 // This user is not a paying customer...
20 return redirect('/billing');
21 }
22 
23 return $next($request);
24 }
25}

如果您想確定使用者是否仍處於試用期,可以使用 onTrial 方法。此方法對於確定是否應向用戶顯示他們仍處於試用期的警告非常有用。

1if ($user->subscription()->onTrial()) {
2 // ...
3}

subscribedToPrice 方法可用於根據給定的 Paddle 價格 ID 確定使用者是否訂閱了給定的套餐。在此示例中,我們將確定使用者的 default 訂閱是否已積極訂閱了月度價格。

1if ($user->subscribedToPrice($monthly = 'pri_123', 'default')) {
2 // ...
3}

recurring 方法可用於確定使用者目前是否處於活躍訂閱狀態,並且不再處於試用期或寬限期內。

1if ($user->subscription()->recurring()) {
2 // ...
3}

已取消的訂閱狀態

要確定使用者是否曾是活躍訂閱者但已取消訂閱,可以使用 canceled 方法。

1if ($user->subscription()->canceled()) {
2 // ...
3}

您還可以確定使用者是否已取消訂閱,但仍處於直到訂閱完全到期之前的“寬限期”內。例如,如果使用者在 3 月 5 日取消了原定於 3 月 10 日到期的訂閱,則使用者處於直到 3 月 10 日的“寬限期”。此外,在此期間 subscribed 方法仍將返回 true

1if ($user->subscription()->onGracePeriod()) {
2 // ...
3}

逾期狀態

如果訂閱付款失敗,它將被標記為 past_due。當您的訂閱處於此狀態時,它將不會處於活動狀態,直到客戶更新其支付資訊。您可以使用訂閱例項上的 pastDue 方法確定訂閱是否逾期。

1if ($user->subscription()->pastDue()) {
2 // ...
3}

當訂閱逾期時,您應該指示使用者 更新其支付資訊

如果您希望訂閱在 past_due 時仍被視為有效,可以使用 Cashier 提供的 keepPastDueSubscriptionsActive 方法。通常,此方法應在您的 AppServiceProviderregister 方法中呼叫。

1use Laravel\Paddle\Cashier;
2 
3/**
4 * Register any application services.
5 */
6public function register(): void
7{
8 Cashier::keepPastDueSubscriptionsActive();
9}

當訂閱處於 past_due 狀態時,在更新支付資訊之前,它無法更改。因此,當訂閱處於 past_due 狀態時,swapupdateQuantity 方法將丟擲異常。

訂閱作用域

大多數訂閱狀態也可作為查詢作用域使用,以便您可以輕鬆地查詢資料庫中處於給定狀態的訂閱。

1// Get all valid subscriptions...
2$subscriptions = Subscription::query()->valid()->get();
3 
4// Get all of the canceled subscriptions for a user...
5$subscriptions = $user->subscriptions()->canceled()->get();

可用作用域的完整列表如下所示。

1Subscription::query()->valid();
2Subscription::query()->onTrial();
3Subscription::query()->expiredTrial();
4Subscription::query()->notOnTrial();
5Subscription::query()->active();
6Subscription::query()->recurring();
7Subscription::query()->pastDue();
8Subscription::query()->paused();
9Subscription::query()->notPaused();
10Subscription::query()->onPausedGracePeriod();
11Subscription::query()->notOnPausedGracePeriod();
12Subscription::query()->canceled();
13Subscription::query()->notCanceled();
14Subscription::query()->onGracePeriod();
15Subscription::query()->notOnGracePeriod();

訂閱單次扣費

訂閱單次扣費允許您在訂閱的基礎上向訂閱者收取一次性費用。在呼叫 charge 方法時,您必須提供一個或多個價格 ID。

1// Charge a single price...
2$response = $user->subscription()->charge('pri_123');
3 
4// Charge multiple prices at once...
5$response = $user->subscription()->charge(['pri_123', 'pri_456']);

charge 方法實際上不會在下一個訂閱計費週期之前向客戶收費。如果您想立即向客戶開具發票,可以使用 chargeAndInvoice 方法。

1$response = $user->subscription()->chargeAndInvoice('pri_123');

更新支付資訊

Paddle 始終為每個訂閱儲存一種支付方式。如果您想更新訂閱的預設支付方式,應使用訂閱模型上的 redirectToUpdatePaymentMethod 方法將客戶重定向到 Paddle 託管的支付方式更新頁面。

1use Illuminate\Http\Request;
2 
3Route::get('/update-payment-method', function (Request $request) {
4 $user = $request->user();
5 
6 return $user->subscription()->redirectToUpdatePaymentMethod();
7});

當用戶更新完資訊後,Paddle 將分派一個 subscription_updated Webhook,訂閱詳情將在您的應用程式資料庫中更新。

更改套餐

使用者訂閱您的應用程式後,有時可能希望更改為新的訂閱套餐。要更新使用者的訂閱套餐,您應該將 Paddle 價格標識符傳遞給訂閱的 swap 方法。

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$user->subscription()->swap($premium = 'pri_456');

如果您想更換套餐並立即向用戶開具發票,而不是等待下一個計費週期,可以使用 swapAndInvoice 方法。

1$user = User::find(1);
2 
3$user->subscription()->swapAndInvoice($premium = 'pri_456');

按比例計算 (Prorations)

預設情況下,Paddle 在更換套餐時會對費用進行按比例計算。noProrate 方法可用於更新訂閱而不按比例計算費用。

1$user->subscription('default')->noProrate()->swap($premium = 'pri_456');

如果您想停用按比例計算並立即向客戶開具發票,可以結合使用 swapAndInvoice 方法和 noProrate

1$user->subscription('default')->noProrate()->swapAndInvoice($premium = 'pri_456');

或者,為了不對訂閱更改向客戶收費,您可以利用 doNotBill 方法。

1$user->subscription('default')->doNotBill()->swap($premium = 'pri_456');

有關 Paddle 按比例計算政策的更多資訊,請查閱 Paddle 的 按比例計算文件

訂閱數量

有時訂閱會受“數量”影響。例如,專案管理應用程式可能會按每個專案每月 10 美元收費。要輕鬆增加或減少訂閱數量,請使用 incrementQuantitydecrementQuantity 方法。

1$user = User::find(1);
2 
3$user->subscription()->incrementQuantity();
4 
5// Add five to the subscription's current quantity...
6$user->subscription()->incrementQuantity(5);
7 
8$user->subscription()->decrementQuantity();
9 
10// Subtract five from the subscription's current quantity...
11$user->subscription()->decrementQuantity(5);

或者,您可以使用 updateQuantity 方法設定特定數量。

1$user->subscription()->updateQuantity(10);

noProrate 方法可用於更新訂閱數量而不按比例計算費用。

1$user->subscription()->noProrate()->updateQuantity(10);

多產品訂閱的數量

如果您的訂閱是 多產品訂閱,則應將您希望增加或減少數量的價格 ID 作為第二個引數傳遞給增加/減少方法。

1$user->subscription()->incrementQuantity(1, 'price_chat');

多產品訂閱

多產品訂閱 允許您將多個計費產品分配給單個訂閱。例如,想象一下您正在構建一個客戶服務“幫助臺”應用程式,其基礎訂閱價格為每月 10 美元,但提供每月額外 15 美元的即時聊天附加產品。

建立訂閱結賬會話時,您可以透過將價格陣列作為 subscribe 方法的第一個引數傳遞,為特定訂閱指定多個產品。

1use Illuminate\Http\Request;
2 
3Route::post('/user/subscribe', function (Request $request) {
4 $checkout = $request->user()->subscribe([
5 'price_monthly',
6 'price_chat',
7 ]);
8 
9 return view('billing', ['checkout' => $checkout]);
10});

在上面的示例中,客戶的 default 訂閱將附加兩個價格。這兩個價格都將在各自的計費週期內收取費用。如有必要,您可以傳遞鍵/值對的關聯陣列來指示每個價格的具體數量。

1$user = User::find(1);
2 
3$checkout = $user->subscribe('default', ['price_monthly', 'price_chat' => 5]);

如果您想向現有訂閱新增另一個價格,必須使用訂閱的 swap 方法。呼叫 swap 方法時,還應包括訂閱當前的價格和數量。

1$user = User::find(1);
2 
3$user->subscription()->swap(['price_chat', 'price_original' => 2]);

上面的示例將新增新價格,但客戶在下一個計費週期之前不會被收取費用。如果您想立即向客戶收費,可以使用 swapAndInvoice 方法。

1$user->subscription()->swapAndInvoice(['price_chat', 'price_original' => 2]);

您可以使用 swap 方法並省略要刪除的價格來從訂閱中刪除價格。

1$user->subscription()->swap(['price_original' => 2]);

您不能刪除訂閱上的最後一個價格。相反,您應該直接取消訂閱。

多重訂閱

Paddle 允許您的客戶同時擁有多個訂閱。例如,您可以經營一家提供游泳訂閱和舉重訂閱的健身房,每種訂閱可能有不同的定價。當然,客戶應該能夠訂閱其中一項或兩項計劃。

當您的應用程式建立訂閱時,您可以將訂閱型別作為第二個引數提供給 subscribe 方法。型別可以是表示使用者正在啟動的訂閱型別的任何字串。

1use Illuminate\Http\Request;
2 
3Route::post('/swimming/subscribe', function (Request $request) {
4 $checkout = $request->user()->subscribe($swimmingMonthly = 'pri_123', 'swimming');
5 
6 return view('billing', ['checkout' => $checkout]);
7});

在此示例中,我們為客戶啟動了月度游泳訂閱。但是,他們以後可能希望更換為年度訂閱。調整客戶的訂閱時,我們可以簡單地交換 swimming 訂閱的價格。

1$user->subscription('swimming')->swap($swimmingYearly = 'pri_456');

當然,您也可以完全取消訂閱。

1$user->subscription('swimming')->cancel();

暫停訂閱

要暫停訂閱,請呼叫使用者訂閱上的 pause 方法。

1$user->subscription()->pause();

當訂閱暫停時,Cashier 會自動設定資料庫中的 paused_at 列。此列用於確定 paused 方法何時開始返回 true。例如,如果客戶在 3 月 1 日暫停了訂閱,但訂閱原定於 3 月 5 日到期扣費,則 paused 方法在 3 月 5 日之前將繼續返回 false。這是因為使用者通常被允許繼續使用應用程式直到其計費週期結束。

預設情況下,暫停發生在下一個計費週期,因此客戶可以使用他們已付款週期的剩餘時間。如果您想立即暫停訂閱,可以使用 pauseNow 方法。

1$user->subscription()->pauseNow();

使用 pauseUntil 方法,您可以將訂閱暫停到特定的時間點。

1$user->subscription()->pauseUntil(now()->plus(months: 1));

或者,您可以使用 pauseNowUntil 方法立即將訂閱暫停到給定的時間點。

1$user->subscription()->pauseNowUntil(now()->plus(months: 1));

您可以使用 onPausedGracePeriod 方法確定使用者是否已暫停訂閱但仍處於“寬限期”內。

1if ($user->subscription()->onPausedGracePeriod()) {
2 // ...
3}

要恢復已暫停的訂閱,您可以呼叫訂閱上的 resume 方法。

1$user->subscription()->resume();

訂閱暫停期間無法修改。如果您想更換套餐或更新數量,必須先恢復訂閱。

取消訂閱

要取消訂閱,請呼叫使用者訂閱上的 cancel 方法。

1$user->subscription()->cancel();

當訂閱取消時,Cashier 會自動設定資料庫中的 ends_at 列。此列用於確定 subscribed 方法何時開始返回 false。例如,如果客戶在 3 月 1 日取消了訂閱,但訂閱原定於 3 月 5 日到期,則 subscribed 方法在 3 月 5 日之前將繼續返回 true。這樣做是因為使用者通常被允許繼續使用應用程式直到其計費週期結束。

您可以使用 onGracePeriod 方法確定使用者是否已取消訂閱但仍處於“寬限期”內。

1if ($user->subscription()->onGracePeriod()) {
2 // ...
3}

如果您希望立即取消訂閱,可以在訂閱上呼叫 cancelNow 方法。

1$user->subscription()->cancelNow();

要阻止處於寬限期的訂閱被取消,您可以呼叫 stopCancelation 方法。

1$user->subscription()->stopCancelation();

Paddle 的訂閱在取消後無法恢復。如果您的客戶希望恢復訂閱,他們將必須建立一個新訂閱。

訂閱試用

預先提供支付方式

如果您想在預先收集支付方式資訊的同時為客戶提供試用期,您應該在客戶訂閱的價格所在的 Paddle 儀表板上設定試用時間。然後,按常規啟動結賬會話。

1use Illuminate\Http\Request;
2 
3Route::get('/user/subscribe', function (Request $request) {
4 $checkout = $request->user()
5 ->subscribe('pri_monthly')
6 ->returnTo(route('home'));
7 
8 return view('billing', ['checkout' => $checkout]);
9});

當您的應用程式收到 subscription_created 事件時,Cashier 將在應用程式資料庫中的訂閱記錄上設定試用期結束日期,並指示 Paddle 在此日期之前不要開始向客戶計費。

如果客戶的訂閱在試用期結束日期之前沒有取消,他們將在試用期結束後立即被收費,因此請務必通知您的使用者試用期結束日期。

您可以使用使用者例項上的 onTrial 方法確定使用者是否處於試用期。

1if ($user->onTrial()) {
2 // ...
3}

要確定現有試用期是否已過期,可以使用 hasExpiredTrial 方法。

1if ($user->hasExpiredTrial()) {
2 // ...
3}

要確定使用者是否針對特定訂閱型別處於試用期,您可以將型別傳遞給 onTrialhasExpiredTrial 方法。

1if ($user->onTrial('default')) {
2 // ...
3}
4 
5if ($user->hasExpiredTrial('default')) {
6 // ...
7}

無需預先提供支付方式

如果您想在不預先收集使用者支付方式資訊的情況下提供試用期,您可以將附加到使用者的客戶記錄上的 trial_ends_at 列設定為您期望的試用結束日期。這通常是在使用者註冊期間完成的。

1use App\Models\User;
2 
3$user = User::create([
4 // ...
5]);
6 
7$user->createAsCustomer([
8 'trial_ends_at' => now()->plus(days: 10)
9]);

Cashier 將這種型別的試用稱為“通用試用”,因為它未附加到任何現有訂閱。如果當前日期未超過 trial_ends_at 的值,User 例項上的 onTrial 方法將返回 true

1if ($user->onTrial()) {
2 // User is within their trial period...
3}

一旦準備好為使用者建立實際訂閱,就可以像往常一樣使用 subscribe 方法。

1use Illuminate\Http\Request;
2 
3Route::get('/user/subscribe', function (Request $request) {
4 $checkout = $request->user()
5 ->subscribe('pri_monthly')
6 ->returnTo(route('home'));
7 
8 return view('billing', ['checkout' => $checkout]);
9});

要檢索使用者的試用結束日期,可以使用 trialEndsAt 方法。如果使用者處於試用期,此方法將返回 Carbon 日期例項,如果不在試用期,則返回 null。如果您想獲取除預設訂閱之外的特定訂閱的試用結束日期,還可以傳遞一個可選的訂閱型別引數。

1if ($user->onTrial('default')) {
2 $trialEndsAt = $user->trialEndsAt();
3}

如果您想明確知道使用者處於其“通用”試用期內且尚未建立實際訂閱,可以使用 onGenericTrial 方法。

1if ($user->onGenericTrial()) {
2 // User is within their "generic" trial period...
3}

延長或啟用試用

您可以透過呼叫 extendTrial 方法並指定試用期應結束的時間點來延長訂閱上的現有試用期。

1$user->subscription()->extendTrial(now()->plus(days: 5));

或者,您可以呼叫訂閱上的 activate 方法來結束試用期,從而立即啟用訂閱。

1$user->subscription()->activate();

處理 Paddle Webhook

Paddle 可以透過 Webhook 向您的應用程式通知各種事件。預設情況下,Cashier 服務提供程式會註冊一個指向 Cashier Webhook 控制器的路由。此控制器將處理所有傳入的 Webhook 請求。

預設情況下,此控制器會自動處理取消失敗次數過多的訂閱、訂閱更新和支付方式更改;但是,正如我們很快會發現的那樣,您可以擴充套件此控制器以處理您喜歡的任何 Paddle Webhook 事件。

為確保您的應用程式能夠處理 Paddle Webhook,請務必 在 Paddle 控制面板中配置 Webhook URL。預設情況下,Cashier 的 Webhook 控制器響應 /paddle/webhook URL 路徑。您應該在 Paddle 控制面板中啟用的所有 Webhook 的完整列表是:

  • 客戶更新 (Customer Updated)
  • 交易完成 (Transaction Completed)
  • 交易更新 (Transaction Updated)
  • 訂閱建立 (Subscription Created)
  • 訂閱更新 (Subscription Updated)
  • 訂閱暫停 (Subscription Paused)
  • 訂閱取消 (Subscription Canceled)

請確保使用 Cashier 包含的 Webhook 簽名驗證 中介軟體來保護傳入的請求。

Webhook 與 CSRF 保護

由於 Paddle Webhook 需要繞過 Laravel 的 CSRF 保護,因此您應確保 Laravel 不會嘗試驗證傳入的 Paddle Webhook 的 CSRF 令牌。為此,您應該在應用程式的 bootstrap/app.php 檔案中從 CSRF 保護中排除 paddle/*

1->withMiddleware(function (Middleware $middleware): void {
2 $middleware->preventRequestForgery(except: [
3 'paddle/*',
4 ]);
5})

Webhook 與本地開發

為了讓 Paddle 能夠在本地開發期間向您的應用程式傳送 Webhook,您需要透過站點共享服務(例如 NgrokExpose)公開您的應用程式。如果您使用 Laravel Sail 本地開發應用程式,則可以使用 Sail 的 站點共享命令

定義 Webhook 事件處理器

Cashier 會自動處理扣費失敗時的訂閱取消以及其他常見的 Paddle Webhook。但是,如果您還有其他想要處理的 Webhook 事件,可以透過監聽由 Cashier 分派的以下事件來完成:

  • Laravel\Paddle\Events\WebhookReceived
  • Laravel\Paddle\Events\WebhookHandled

這兩個事件都包含 Paddle Webhook 的完整負載。例如,如果您希望處理 transaction.billed Webhook,可以註冊一個 監聽器 來處理該事件。

1<?php
2 
3namespace App\Listeners;
4 
5use Laravel\Paddle\Events\WebhookReceived;
6 
7class PaddleEventListener
8{
9 /**
10 * Handle received Paddle webhooks.
11 */
12 public function handle(WebhookReceived $event): void
13 {
14 if ($event->payload['event_type'] === 'transaction.billed') {
15 // Handle the incoming event...
16 }
17 }
18}

Cashier 還發出專用於所接收 Webhook 型別的事件。除了來自 Paddle 的完整負載外,它們還包含用於處理 Webhook 的相關模型,例如計費模型、訂閱或收據。

  • Laravel\Paddle\Events\CustomerUpdated
  • Laravel\Paddle\Events\TransactionCompleted
  • Laravel\Paddle\Events\TransactionUpdated
  • Laravel\Paddle\Events\SubscriptionCreated
  • Laravel\Paddle\Events\SubscriptionUpdated
  • Laravel\Paddle\Events\SubscriptionPaused
  • Laravel\Paddle\Events\SubscriptionCanceled

您還可以透過在應用程式的 .env 檔案中定義 CASHIER_WEBHOOK 環境變數來覆蓋預設的內建 Webhook 路由。此值應為指向 Webhook 路由的完整 URL,並且必須與在 Paddle 控制面板中設定的 URL 相匹配。

1CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url

驗證 Webhook 簽名

要保護您的 Webhook,您可以使用 Paddle 的 Webhook 簽名。為方便起見,Cashier 自動包含了一箇中間件,用於驗證傳入的 Paddle Webhook 請求是否有效。

要啟用 Webhook 驗證,請確保應用程式的 .env 檔案中定義了 PADDLE_WEBHOOK_SECRET 環境變數。Webhook 金鑰可以從您的 Paddle 賬戶儀表板中獲取。

單次扣費

產品扣費

如果您想為客戶發起產品購買,可以使用計費模型例項上的 checkout 方法為該購買生成結賬會話。checkout 方法接受一個或多個價格 ID。如有必要,可以使用關聯陣列來提供所購買產品的數量。

1use Illuminate\Http\Request;
2 
3Route::get('/buy', function (Request $request) {
4 $checkout = $request->user()->checkout(['pri_tshirt', 'pri_socks' => 5]);
5 
6 return view('buy', ['checkout' => $checkout]);
7});

生成結賬會話後,您可以使用 Cashier 提供的 paddle-button Blade 元件,讓使用者檢視 Paddle 結賬小部件並完成購買。

1<x-paddle-button :checkout="$checkout" class="px-8 py-4">
2 Buy
3</x-paddle-button>

結賬會話具有 customData 方法,允許您將任何您希望的自定義資料傳遞給底層的交易建立過程。請查閱 Paddle 文件 以瞭解有關傳遞自定義資料時可用選項的更多資訊。

1$checkout = $user->checkout('pri_tshirt')
2 ->customData([
3 'custom_option' => $value,
4 ]);

退款交易

退款交易將把退款金額退還到購買時客戶使用的支付方式。如果您需要退款 Paddle 購買,可以在 Cashier\Paddle\Transaction 模型上使用 refund 方法。此方法接受原因作為第一個引數,以及一個或多個要退款的價格 ID(帶有可選的金額,作為關聯陣列)。您可以使用 transactions 方法檢索給定計費模型的交易。

例如,假設我們要為價格 pri_123pri_456 退還特定交易。我們想全額退還 pri_123,但只為 pri_456 退款兩美元。

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$transaction = $user->transactions()->first();
6 
7$response = $transaction->refund('Accidental charge', [
8 'pri_123', // Fully refund this price...
9 'pri_456' => 200, // Only partially refund this price...
10]);

上面的示例退還了交易中的特定行專案。如果您想退還整筆交易,只需提供原因即可。

1$response = $transaction->refund('Accidental charge');

有關退款的更多資訊,請查閱 Paddle 的退款文件

退款必須始終在完全處理前由 Paddle 批准。

貸記交易

就像退款一樣,您也可以對交易進行貸記。貸記交易會將資金新增到客戶的餘額中,以便可用於以後的購買。貸記交易只能針對手動收集的交易進行,不能針對自動收集的交易(如訂閱)進行,因為 Paddle 會自動處理訂閱貸記。

1$transaction = $user->transactions()->first();
2 
3// Credit a specific line item fully...
4$response = $transaction->credit('Compensation', 'pri_123');

更多資訊,請 參閱 Paddle 關於貸記的文件

貸記只能應用於手動收集的交易。自動收集的交易由 Paddle 自己進行貸記。

交易記錄

您可以透過 transactions 屬性輕鬆檢索計費模型交易的陣列。

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$transactions = $user->transactions;

交易代表您的產品和購買的付款,並附有發票。只有已完成的交易才會儲存在您的應用程式資料庫中。

列出客戶的交易時,您可以使用交易例項的方法來顯示相關的支付資訊。例如,您可能希望在一個表格中列出每筆交易,以便使用者輕鬆下載任何發票。

1<table>
2 @foreach ($transactions as $transaction)
3 <tr>
4 <td>{{ $transaction->billed_at->toFormattedDateString() }}</td>
5 <td>{{ $transaction->total() }}</td>
6 <td>{{ $transaction->tax() }}</td>
7 <td><a href="{{ route('download-invoice', $transaction->id) }}" target="_blank">Download</a></td>
8 </tr>
9 @endforeach
10</table>

download-invoice 路由可能如下所示:

1use Illuminate\Http\Request;
2use Laravel\Paddle\Transaction;
3 
4Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) {
5 return $transaction->redirectToInvoicePdf();
6})->name('download-invoice');

往期及即將進行的付款

您可以使用 lastPaymentnextPayment 方法來檢索和顯示客戶往期或即將進行的定期訂閱付款。

1use App\Models\User;
2 
3$user = User::find(1);
4 
5$subscription = $user->subscription();
6 
7$lastPayment = $subscription->lastPayment();
8$nextPayment = $subscription->nextPayment();

這兩個方法都將返回 Laravel\Paddle\Payment 的一個例項;但是,當交易尚未透過 Webhook 同步時,lastPayment 將返回 null,而當計費週期結束(例如訂閱已取消)時,nextPayment 將返回 null

1Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}

測試

測試時,您應該手動測試您的計費流程,以確保您的整合按預期工作。

對於自動化測試(包括在 CI 環境中執行的測試),您可以使用 Laravel 的 HTTP 客戶端 來偽造對 Paddle 發出的 HTTP 呼叫。雖然這不會測試來自 Paddle 的實際響應,但它提供了一種在不實際呼叫 Paddle API 的情況下測試應用程式的方法。