跳到內容

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 需要批准您的應用程式網域。

設定

可計費模型

在使用 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 結帳小工具。您可以透過將 @paddleJS Blade 指令放在應用程式版面配置關閉的 </head> 標籤之前來載入 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}

在定義模型之後,您可以指示 Cashier 透過 Laravel\Paddle\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 Checkout 之前,您應該在 Paddle 儀表板中定義具有固定價格的產品。此外,您應該設定 Paddle 的 webhook 處理

透過您的應用程式提供產品和訂閱計費可能令人卻步。但是,感謝 Cashier 和 Paddle 的結帳覆蓋,您可以輕鬆建立現代化、穩健的付款整合。

為了向顧客收取非週期性、單次收費產品的費用,我們將利用 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 Checkout 提供 Meta Data

在銷售產品時,通常會透過您應用程式定義的 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');

如您在上面的範例中所見,當使用者開始結帳流程時,我們會將購物車/訂單的所有相關 Paddle 價格識別碼提供給 checkout 方法。當然,您的應用程式有責任在顧客新增商品時,將這些項目與「購物車」或訂單關聯起來。我們也會透過 customData 方法將訂單 ID 提供給 Paddle 結帳覆蓋層。

當然,您可能希望在顧客完成結帳流程後,將訂單標記為「完成」。為了達成此目的,您可以監聽 Paddle 發送的 webhook,並透過 Cashier 的事件觸發,將訂單資訊儲存在您的資料庫中。

若要開始使用,請監聽 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 的文件:data contained by the transaction.completed event

銷售訂閱

在使用 Paddle Checkout 之前,您應該在 Paddle 儀表板中定義具有固定價格的產品。此外,您應該設定 Paddle 的 webhook 處理

透過您的應用程式提供產品和訂閱計費可能令人卻步。但是,感謝 Cashier 和 Paddle 的結帳覆蓋,您可以輕鬆建立現代化、穩健的付款整合。

若要了解如何使用 Cashier 和 Paddle 的結帳覆蓋層銷售訂閱服務,讓我們考慮一個簡單的情境:一個提供基本月度方案(price_basic_monthly)和年度方案(price_basic_yearly)的訂閱服務。這兩個價格可以歸類在 Paddle 儀表板中的「基本」產品(pro_basic)下。此外,我們的訂閱服務可能還提供專家方案 pro_expert

首先,讓我們探索顧客如何訂閱我們的服務。當然,您可以想像顧客可能會在我們應用程式的定價頁面上點擊「訂閱」按鈕以選擇基本方案。此按鈕將為他們選擇的方案調用 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 的 結帳覆蓋層小工具 或使用 內嵌結帳 進行「結帳」。

在使用 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');

按比例計費

預設情況下,在方案之間切換時,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 作為第二個參數傳遞給 increment / decrement 方法

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 方法將繼續傳回 false 直到 3 月 5 日。這是因為使用者通常被允許繼續使用應用程式直到其計費週期結束。

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

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

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

1$user->subscription()->pauseUntil(now()->addMonth());

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

1$user->subscription()->pauseNowUntil(now()->addMonth());

您可以使用 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 方法將繼續傳回 true 直到 3 月 5 日。這樣做的原因是使用者通常被允許繼續使用應用程式直到其計費週期結束。

您可以使用 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()->addDays(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()->addDays(5));

或者,您可以透過在訂閱上調用 activate 方法來結束其試用期,從而立即啟用訂閱

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

處理 Paddle Webhooks

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

預設情況下,此控制器將自動處理因過多付款失敗而取消的訂閱、訂閱更新和付款方式變更;然而,正如我們很快將發現的,您可以擴展此控制器以處理您想要的任何 Paddle webhook 事件。

為確保您的應用程式可以處理 Paddle webhook,請務必在 Paddle 控制面板中設定 webhook URL。預設情況下,Cashier 的 webhook 控制器會回應 /paddle/webhook URL 路徑。您應該在 Paddle 控制面板中啟用的所有 webhook 的完整列表如下

  • 客戶已更新
  • 交易已完成
  • 交易已更新
  • 訂閱已建立
  • 訂閱已更新
  • 訂閱已暫停
  • 訂閱已取消

請確保使用 Cashier 內建的webhook 簽名驗證中介層來保護傳入的請求。

Webhook 和 CSRF 保護

由於 Paddle webhook 需要繞過 Laravel 的 CSRF 保護,您應確保 Laravel 不會嘗試驗證傳入的 Paddle webhook 的 CSRF 令牌。若要達成此目的,您應將應用程式 bootstrap/app.php 檔案中的 CSRF 保護排除 paddle/*

1->withMiddleware(function (Middleware $middleware) {
2 $middleware->validateCsrfTokens(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 secret 金鑰可以從您的 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 的實例;但是,lastPayment 在 webhook 尚未同步交易時將返回 null,而 nextPayment 在計費週期結束時(例如訂閱已取消時)將返回 null

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

測試

在測試時,您應該手動測試您的計費流程,以確保您的整合運作正常。

對於自動化測試,包括在 CI 環境中執行的測試,您可以使用 Laravel 的 HTTP Client 來模擬對 Paddle 進行的 HTTP 呼叫。雖然這不會測試來自 Paddle 的實際回應,但它確實提供了一種在不實際呼叫 Paddle API 的情況下測試您的應用程式的方法。

Laravel 是最具生產力的方式來
建構、部署和監控軟體。