簡介 (Introduction)
[!WARNING] 本文件適用於 Cashier Paddle 2.x 與 Paddle Billing 的整合。如果你仍在使用 Paddle Classic,應使用 Cashier Paddle 1.x。
Laravel Cashier Paddle 為 Paddle 的訂閱計費服務提供了一個語意化且流暢的介面。它幾乎處理了所有你害怕編寫的訂閱計費樣板程式碼。除了基本的訂閱管理外,Cashier 還可以處理:更換訂閱、訂閱「數量」、暫停訂閱、取消寬限期等等。
在深入研究 Cashier Paddle 之前,我們建議你也閱讀 Paddle 的 概念指南 和 API 文件。
升級 Cashier (Upgrading Cashier)
升級到新版本的 Cashier 時,請務必仔細閱讀 升級指南。
安裝 (Installation)
首先,使用 Composer 套件管理器安裝 Paddle 的 Cashier 套件:
composer require laravel/cashier-paddle
接下來,你應該使用 vendor:publish Artisan 指令發布 Cashier 的遷移檔案:
php artisan vendor:publish --tag="cashier-migrations"
然後,你應該執行應用程式的資料庫遷移。Cashier 遷移將建立一個新的 customers 資料表。此外,還將建立新的 subscriptions 和 subscription_items 資料表來儲存所有客戶的訂閱。最後,將建立一個新的 transactions 資料表來儲存與你的客戶相關的所有 Paddle 交易:
php artisan migrate
[!WARNING] 為了確保 Cashier 正確處理所有 Paddle 事件,請記得 設定 Cashier 的 Webhook 處理。
Paddle Sandbox
在本地和預備環境開發期間,你應該 註冊一個 Paddle Sandbox 帳號。此帳號將為你提供一個沙盒環境來測試和開發你的應用程式,而無需進行實際付款。你可以使用 Paddle 的 測試卡號 來模擬各種付款情境。
使用 Paddle Sandbox 環境時,你應該在應用程式的 .env 檔案中將 PADDLE_SANDBOX 環境變數設定為 true:
PADDLE_SANDBOX=true
完成應用程式開發後,你可以 申請 Paddle 供應商帳號。在你的應用程式投入生產環境之前,Paddle 需要核准你的應用程式網域。
設定 (Configuration)
Billable 模型 (Billable Model)
在使用 Cashier 之前,你必須將 Billable trait 新增到你的使用者模型定義中。這個 trait 提供了各種方法,讓你執行常見的計費任務,例如建立訂閱和更新付款方式資訊:
use Laravel\Paddle\Billable;
class User extends Authenticatable
{
use Billable;
}
如果你有非使用者的計費實體,你也可以將該 trait 新增到這些類別中:
use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
{
use Billable;
}
API Keys
接下來,你應該在應用程式的 .env 檔案中設定你的 Paddle 金鑰。你可以從 Paddle 控制面板取得你的 Paddle API 金鑰:
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 環境 時,PADDLE_SANDBOX 環境變數應設定為 true。如果你將應用程式部署到生產環境並使用 Paddle 的即時供應商環境,則 PADDLE_SANDBOX 變數應設定為 false。
PADDLE_RETAIN_KEY 是選用的,只有在你使用 Paddle 與 Retain 時才需要設定。
Paddle JS
Paddle 依賴其自己的 JavaScript 函式庫來啟動 Paddle 結帳小工具。你可以透過將 @paddleJS Blade 指令放置在應用程式版面配置的結束 </head> 標籤之前來載入 JavaScript 函式庫:
<head>
...
@paddleJS
</head>
貨幣設定 (Currency Configuration)
你可以指定一個語系,用於在發票上顯示貨幣值時進行格式化。Cashier 內部使用 PHP 的 NumberFormatter 類別 來設定貨幣語系:
CASHIER_CURRENCY_LOCALE=nl_BE
[!WARNING] 為了使用
en以外的語系,請確保你的伺服器上已安裝並設定ext-intlPHP 擴充功能。
覆寫預設模型 (Overriding Default Models)
你可以自由擴充 Cashier 內部使用的模型,方法是定義你自己的模型並擴充相應的 Cashier 模型:
use Laravel\Paddle\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
定義模型後,你可以透過 Laravel\Paddle\Cashier 類別指示 Cashier 使用你的自訂模型。通常,你應該在應用程式的 App\Providers\AppServiceProvider 類別的 boot 方法中通知 Cashier 關於你的自訂模型:
use App\Models\Cashier\Subscription;
use App\Models\Cashier\Transaction;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useTransactionModel(Transaction::class);
}
快速開始 (Quickstart)
銷售產品 (Selling Products)
[!NOTE] 在使用 Paddle Checkout 之前,你應該在 Paddle 儀表板中定義具有固定價格的產品。此外,你應該 設定 Paddle 的 Webhook 處理。
透過你的應用程式提供產品和訂閱計費可能會令人望而生畏。然而,感謝 Cashier 和 Paddle 的 Checkout Overlay,你可以輕鬆建立現代化、強大的付款整合。
為了向客戶收取非經常性、單次收費產品的費用,我們將利用 Cashier 透過 Paddle 的 Checkout Overlay 向客戶收費,他們將在其中提供付款詳細資訊並確認購買。透過 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 物件,以便為給定的「價格識別碼」向客戶顯示 Paddle Checkout Overlay。使用 Paddle 時,「價格」是指 特定產品的定義價格。
如有必要,checkout 方法將自動在 Paddle 中建立一個客戶,並將該 Paddle 客戶記錄連接到你應用程式資料庫中的相應使用者。完成結帳 session 後,客戶將被重新導向到專用的成功頁面,你可以在該頁面向客戶顯示資訊訊息。
在 buy 視圖中,我們將包含一個按鈕來顯示 Checkout Overlay。Cashier Paddle 包含 paddle-button Blade 元件;但是,你也可以 手動渲染 overlay checkout:
<x-paddle-button :checkout="$checkout" className="px-8 py-4">
Buy Product
</x-paddle-button>
提供 Meta Data 給 Paddle Checkout (Providing Meta Data To Paddle Checkout)
銷售產品時,通常會透過應用程式定義的 Cart 和 Order 模型來追蹤已完成的訂單和購買的產品。當將客戶重新導向到 Paddle 的 Checkout Overlay 以完成購買時,你可能需要提供現有的訂單識別碼,以便在客戶被重新導向回你的應用程式時,將已完成的購買與相應的訂單關聯起來。
為此,你可以向 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 價格識別碼。當然,你的應用程式負責在客戶新增項目時將這些項目與「購物車」或訂單關聯起來。我們還透過 customData 方法將訂單 ID 提供給 Paddle Checkout Overlay。
當然,一旦客戶完成結帳流程,你可能會希望將訂單標記為「已完成」。為此,你可以監聽 Paddle 發送的 webhook 並由 Cashier 透過事件引發,以將訂單資訊儲存在你的資料庫中。
首先,監聽 Cashier 發送的 TransactionCompleted 事件。通常,你應該在應用程式的 AppServiceProvider 的 boot 方法中註冊事件監聽器:
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 的文件。
銷售訂閱 (Selling Subscriptions)
[!NOTE] 在使用 Paddle Checkout 之前,你應該在 Paddle 儀表板中定義具有固定價格的產品。此外,你應該 設定 Paddle 的 Webhook 處理。
透過你的應用程式提供產品和訂閱計費可能會令人望而生畏。然而,感謝 Cashier 和 Paddle 的 Checkout Overlay,你可以輕鬆建立現代化、強大的付款整合。
要學習如何使用 Cashier 和 Paddle 的 Checkout Overlay 銷售訂閱,讓我們考慮一個簡單的訂閱服務情境,其中包含基本的月繳 (price_basic_monthly) 和年繳 (price_basic_yearly) 方案。這兩個價格可以在我們的 Paddle 儀表板中歸類為「Basic」產品 (pro_basic)。此外,我們的訂閱服務可能提供「Expert」方案作為 pro_expert。
首先,讓我們了解客戶如何訂閱我們的服務。當然,你可以想像客戶可能會點擊我們應用程式定價頁面上 Basic 方案的「訂閱」按鈕。此按鈕將為他們選擇的方案呼叫 Paddle Checkout Overlay。首先,讓我們透過 checkout 方法啟動一個結帳 session:
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。Cashier Paddle 包含 paddle-button Blade 元件;但是,你也可以 手動渲染 overlay checkout:
<x-paddle-button :checkout="$checkout" className="px-8 py-4">
Subscribe
</x-paddle-button>
現在,當點擊訂閱按鈕時,客戶將能夠輸入他們的付款詳細資訊並啟動他們的訂閱。要知道他們的訂閱何時實際開始(因為某些付款方式需要幾秒鐘來處理),你還應該 設定 Cashier 的 Webhook 處理。
既然客戶可以開始訂閱,我們需要限制應用程式的某些部分,以便只有訂閱的使用者才能存取它們。當然,我們始終可以透過 Cashier 的 Billable trait 提供的 subscribed 方法來確定使用者的目前訂閱狀態:
@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 (Quickstart Building A Subscribed 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]);
允許客戶管理他們的計費方案 (Quickstart Allowing Customers To Manage Their Billing Plan)
當然,客戶可能希望將其訂閱方案變更為其他產品或「層級」。在上面的範例中,我們希望允許客戶將其方案從月繳訂閱變更為年繳訂閱。為此,你需要實作類似按鈕的東西,該按鈕導向以下路由:
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');
現在,你的訂閱將在其計費週期結束時取消。
[!NOTE] 只要你已設定 Cashier 的 Webhook 處理,Cashier 將透過檢查來自 Paddle 的傳入 Webhook 自動保持應用程式的 Cashier 相關資料庫資料表同步。因此,例如,當你透過 Paddle 的儀表板取消客戶的訂閱時,Cashier 將收到相應的 Webhook 並將應用程式資料庫中的訂閱標記為「已取消」。
結帳 Session (Checkout Sessions)
大多數向客戶收費的操作都是透過 Paddle 的 Checkout Overlay 小工具 或利用 inline checkout 使用「checkouts」執行的。
在使用 Paddle 處理結帳付款之前,你應該在 Paddle 結帳設定儀表板中定義應用程式的 預設付款連結。
Overlay 結帳 (Overlay Checkout)
在顯示 Checkout Overlay 小工具之前,你必須使用 Cashier 產生一個結帳 session。結帳 session 將通知結帳小工具應執行的計費操作:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
Cashier 包含一個 paddle-button Blade 元件。你可以將結帳 session 作為「prop」傳遞給此元件。然後,當點擊此按鈕時,將顯示 Paddle 的結帳小工具:
<x-paddle-button :checkout="$checkout" className="px-8 py-4">
Subscribe
</x-paddle-button>
預設情況下,這將使用 Paddle 的預設樣式顯示小工具。你可以透過將 Paddle 支援的屬性(如 data-theme='light' 屬性)新增到元件來自訂小工具:
<x-paddle-button :checkout="$checkout" className="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>
Paddle 結帳小工具是非同步的。一旦使用者在小工具中建立訂閱,Paddle 將向你的應用程式發送一個 webhook,以便你可以正確更新應用程式資料庫中的訂閱狀態。因此,正確 設定 webhook 以適應 Paddle 的狀態變更非常重要。
[!WARNING] 訂閱狀態變更後,接收相應 webhook 的延遲通常很短,但你應該在應用程式中考慮到這一點,即使用者的訂閱可能不會在完成結帳後立即生效。
手動渲染 Overlay Checkout (Manually Rendering An Overlay Checkout)
你也可以手動渲染 overlay checkout,而不使用 Laravel 內建的 Blade 元件。首先,如前面的範例所示 產生結帳 session:
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 checkout:
<?php
$items = $checkout->getItems();
$customer = $checkout->getCustomer();
$custom = $checkout->getCustomData();
?>
<a
href='#!'
className='paddle_button'
data-items='{!! json_encode($items) !!}'
@if ($customer) data-customer-id='{{ $customer->paddle_id }}' @endif
@if ($custom) data-custom-data='{{ json_encode($custom) }}' @endif
@if ($returnUrl = $checkout->getReturnUrl()) data-success-url='{{ $returnUrl }}' @endif
>
Buy Product
</a>
Inline 結帳 (Inline Checkout)
如果你不想使用 Paddle 的「overlay」樣式結帳小工具,Paddle 也提供了內嵌顯示小工具的選項。雖然這種方法不允許你調整任何結帳的 HTML 欄位,但它允許你將小工具嵌入到你的應用程式中。
為了讓你輕鬆開始使用 inline checkout,Cashier 包含一個 paddle-checkout Blade 元件。首先,你應該 產生一個結帳 session:
use Illuminate\Http\Request;
Route::get('/buy', function (Request $request) {
$checkout = $user->checkout('pri_34567')
->returnTo(route('dashboard'));
return view('billing', ['checkout' => $checkout]);
});
然後,你可以將結帳 session 傳遞給元件的 checkout 屬性:
<x-paddle-checkout :checkout="$checkout" className="w-full" />
要調整 inline checkout 元件的高度,你可以將 height 屬性傳遞給 Blade 元件:
<x-paddle-checkout :checkout="$checkout" className="w-full" height="500" />
有關 inline checkout 自訂選項的更多詳細資訊,請參閱 Paddle 的 Inline Checkout 指南 和 可用的結帳設定。
手動渲染 Inline Checkout (Manually Rendering An Inline Checkout)
你也可以手動渲染 inline checkout,而不使用 Laravel 內建的 Blade 元件。首先,如前面的範例所示 產生結帳 session:
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 className="paddle-checkout" x-data="{}" x-init="
Paddle.Checkout.open(@json($options));
">
</div>
訪客結帳 (Guest Checkouts)
有時,你可能需要為不需要應用程式帳號的使用者建立結帳 session。為此,你可以使用 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]);
});
然後,你可以將結帳 session 提供給 Paddle 按鈕 或 inline checkout Blade 元件。
價格預覽 (Price Previews)
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>
有關更多資訊,請 查看 Paddle 關於價格預覽的 API 文件。
客戶價格預覽 (Customer Price Previews)
如果使用者已經是客戶,並且你想顯示適用於該客戶的價格,你可以直接從客戶實例取得價格:
use App\Models\User;
$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);
Cashier 內部將使用使用者的客戶 ID 來取得其貨幣的價格。因此,例如,居住在美國的使用者將看到以美元為單位的價格,而居住在比利時的使用者將看到以歐元為單位的價格。如果找不到相符的貨幣,將使用產品的預設貨幣。你可以在 Paddle 控制面板中自訂產品或訂閱方案的所有價格。
折扣 (Discounts)
你也可以選擇顯示折扣後的價格。呼叫 previewPrices 方法時,透過 discount_id 選項提供折扣 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>
客戶 (Customers)
客戶預設值 (Customer Defaults)
Cashier 允許你在建立結帳 session 時為客戶定義一些有用的預設值。設定這些預設值允許你預先填寫客戶的電子郵件地址和姓名,以便他們可以立即進入結帳小工具的付款部分。你可以透過覆寫 billable 模型上的以下方法來設定這些預設值:
/**
* 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 中產生 結帳 session 的每個操作。
取得客戶 (Retrieving Customers)
你可以使用 Cashier::findBillable 方法透過 Paddle 客戶 ID 取得客戶。此方法將回傳 billable 模型的實例:
use Laravel\Paddle\Cashier;
$user = Cashier::findBillable($customerId);
建立客戶 (Creating Customers)
有時,你可能希望建立一個 Paddle 客戶而不開始訂閱。你可以使用 createAsCustomer 方法來完成此操作:
$customer = $user->createAsCustomer();
將回傳 Laravel\Paddle\Customer 的實例。一旦在 Paddle 中建立了客戶,你可以在以後開始訂閱。你可以提供一個選用的 $options 陣列來傳入 Paddle API 支援的任何其他客戶建立參數:
$customer = $user->createAsCustomer($options);
訂閱 (Subscriptions)
建立訂閱 (Creating Subscriptions)
要建立訂閱,首先從資料庫中取得 billable 模型的實例,通常是 App\Models\User 的實例。取得模型實例後,你可以使用 subscribe 方法來建立模型的結帳 session:
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,使用者在成功完成結帳後將被重新導向到該 URL。傳遞給 subscribe 方法的第二個參數應該是訂閱的內部「類型」。如果你的應用程式僅提供單一訂閱,你可以將其稱為 default 或 primary。此訂閱類型僅供內部應用程式使用,不適合向使用者顯示。此外,它不應包含空格,並且在建立訂閱後絕不應更改。
你也可以使用 customData 方法提供有關訂閱的自訂中繼資料陣列:
$checkout = $request->user()->subscribe($premium = 'pri_123', 'default')
->customData(['key' => 'value'])
->returnTo(route('home'));
建立訂閱結帳 session 後,可以將結帳 session 提供給 Cashier Paddle 包含的 paddle-button Blade 元件:
<x-paddle-button :checkout="$checkout" className="px-8 py-4">
Subscribe
</x-paddle-button>
使用者完成結帳後,Paddle 將發送 subscription_created webhook。Cashier 將接收此 webhook 並為你的客戶設定訂閱。為了確保應用程式正確接收和處理所有 webhook,請確保你已正確 設定 webhook 處理。
檢查訂閱狀態 (Checking Subscription Status)
一旦使用者訂閱了你的應用程式,你可以使用各種方便的方法檢查他們的訂閱狀態。首先,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 方法可用於根據給定的 Paddle 價格 ID 確定使用者是否訂閱了給定的方案。在此範例中,我們將確定使用者的 default 訂閱是否主動訂閱了月繳價格:
if ($user->subscribedToPrice($monthly = 'pri_123', 'default')) {
// ...
}
recurring 方法可用於確定使用者目前是否處於有效訂閱中,並且不再處於試用期或寬限期內:
if ($user->subscription()->recurring()) {
// ...
}
已取消訂閱狀態 (Canceled Subscription Status)
要確定使用者是否曾經是活躍訂閱者但已取消訂閱,可以使用 canceled 方法:
if ($user->subscription()->canceled()) {
// ...
}
你也可以確定使用者是否已取消訂閱,但仍處於「寬限期」,直到訂閱完全過期。例如,如果使用者在 3 月 5 日取消了原定於 3 月 10 日過期的訂閱,則使用者在 3 月 10 日之前處於「寬限期」。此外,在此期間 subscribed 方法仍將回傳 true:
if ($user->subscription()->onGracePeriod()) {
// ...
}
逾期狀態 (Past Due Status)
如果訂閱付款失敗,它將被標記為 past_due。當你的訂閱處於此狀態時,直到客戶更新其付款資訊之前,它將不會處於啟用狀態。你可以使用訂閱實例上的 pastDue 方法確定訂閱是否逾期:
if ($user->subscription()->pastDue()) {
// ...
}
當訂閱逾期時,你應該指示使用者 更新他們的付款資訊。
如果你希望訂閱在 past_due 狀態下仍被視為有效,你可以使用 Cashier 提供的 keepPastDueSubscriptionsActive 方法。通常,此方法應在 AppServiceProvider 的 register 方法中呼叫:
use Laravel\Paddle\Cashier;
/**
* Register any application services.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
}
[!WARNING] 當訂閱處於
past_due狀態時,直到付款資訊更新之前,皆無法變更。因此,當訂閱處於past_due狀態時,swap和updateQuantity方法將會拋出例外。
訂閱 Scopes (Subscription Scopes)
大多數訂閱狀態也可用作查詢 scope,以便你可以輕鬆查詢資料庫中處於給定狀態的訂閱:
// Get all valid subscriptions...
$subscriptions = Subscription::query()->valid()->get();
// Get all of the canceled subscriptions for a user...
$subscriptions = $user->subscriptions()->canceled()->get();
可用 scope 的完整列表如下:
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();
訂閱單次收費 (Subscription Single Charges)
訂閱單次收費允許你在訂閱之外向訂閱者收取一次性費用。呼叫 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');
更新付款資訊 (Updating Payment Information)
Paddle 總是為每個訂閱儲存一種付款方式。如果你想更新訂閱的預設付款方式,你應該使用訂閱模型上的 redirectToUpdatePaymentMethod 方法將客戶重新導向到 Paddle 的託管付款方式更新頁面:
use Illuminate\Http\Request;
Route::get('/update-payment-method', function (Request $request) {
$user = $request->user();
return $user->subscription()->redirectToUpdatePaymentMethod();
});
當使用者完成資訊更新後,Paddle 將發送 subscription_updated webhook,並且訂閱詳細資訊將在你的應用程式資料庫中更新。
變更方案 (Changing Plans)
當使用者訂閱你的應用程式後,他們偶爾可能希望變更為新的訂閱方案。要更新使用者的訂閱方案,你應該將 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');
按比例計算 (Prorations)
預設情況下,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 的 按比例計算文件。
訂閱數量 (Subscription Quantity)
有時訂閱會受到「數量」的影響。例如,專案管理應用程式可能會對每個專案每月收取 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);
多產品訂閱的數量 (Quantities for Subscriptions With Multiple Products)
如果你的訂閱是 多產品訂閱,你應該將希望增加或減少數量的價格 ID 作為第二個參數傳遞給 increment / decrement 方法:
$user->subscription()->incrementQuantity(1, 'price_chat');
多產品訂閱 (Subscriptions With Multiple Products)
多產品訂閱 允許你將多個計費產品指派給單個訂閱。例如,想像你正在建立一個客戶服務「helpdesk」應用程式,該應用程式的基本訂閱價格為每月 10 美元,但提供每月額外 15 美元的即時聊天附加產品。
建立訂閱結帳 session 時,你可以透過將價格陣列作為第一個參數傳遞給 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]);
[!WARNING] 你不能刪除訂閱中的最後一個價格。相反,你應該直接取消訂閱。
多重訂閱 (Multiple Subscriptions)
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();
暫停訂閱 (Pausing Subscriptions)
要暫停訂閱,請呼叫使用者訂閱上的 pause 方法:
$user->subscription()->pause();
當訂閱暫停時,Cashier 將自動設定資料庫中的 paused_at 欄位。此欄位用於確定 paused 方法何時應開始回傳 true。例如,如果客戶在 3 月 1 日暫停訂閱,但訂閱原定於 3 月 5 日才再次循環,則 paused 方法將繼續回傳 false 直到 3 月 5 日。這是因為使用者通常被允許繼續使用應用程式,直到其計費週期結束。
預設情況下,暫停發生在下一個計費週期,因此客戶可以使用他們已支付的剩餘期間。如果你想立即暫停訂閱,可以使用 pauseNow 方法:
$user->subscription()->pauseNow();
使用 pauseUntil 方法,你可以將訂閱暫停到特定時間點:
$user->subscription()->pauseUntil(now()->addMonth());
或者,你可以使用 pauseNowUntil 方法立即暫停訂閱直到給定時間點:
$user->subscription()->pauseNowUntil(now()->addMonth());
你可以使用 onPausedGracePeriod 方法確定使用者是否暫停了訂閱但仍處於「寬限期」:
if ($user->subscription()->onPausedGracePeriod()) {
// ...
}
要恢復暫停的訂閱,你可以在訂閱上呼叫 resume 方法:
$user->subscription()->resume();
[!WARNING] 暫停時無法修改訂閱。如果你想更換到不同的方案或更新數量,必須先恢復訂閱。
取消訂閱 (Canceling Subscriptions)
要取消訂閱,請呼叫使用者訂閱上的 cancel 方法:
$user->subscription()->cancel();
當訂閱被取消時,Cashier 將自動設定資料庫中的 ends_at 欄位。此欄位用於確定 subscribed 方法何時應開始回傳 false。例如,如果客戶在 3 月 1 日取消訂閱,但訂閱原定於 3 月 5 日結束,則 subscribed 方法將繼續回傳 true 直到 3 月 5 日。這樣做是因為使用者通常被允許繼續使用應用程式,直到其計費週期結束。
你可以使用 onGracePeriod 方法確定使用者是否已取消訂閱但仍處於「寬限期」:
if ($user->subscription()->onGracePeriod()) {
// ...
}
如果你想立即取消訂閱,可以呼叫訂閱上的 cancelNow 方法:
$user->subscription()->cancelNow();
要停止取消處於寬限期的訂閱,你可以呼叫 stopCancelation 方法:
$user->subscription()->stopCancelation();
[!WARNING] Paddle 的訂閱在取消後無法恢復。如果你的客戶希望恢復他們的訂閱,他們必須建立一個新的訂閱。
訂閱試用 (Subscription Trials)
需預先提供付款方式 (With Payment Method Up Front)
如果你想為客戶提供試用期,同時仍需預先收集付款方式資訊,你應該在 Paddle 儀表板中為客戶訂閱的價格設定試用時間。然後,像往常一樣啟動結帳 session:
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 在此日期之後才開始向客戶收費。
[!WARNING] 如果客戶的訂閱在試用結束日期之前未取消,他們將在試用期滿後立即被收費,因此你應該確保通知使用者試用結束日期。
你可以使用使用者實例的 onTrial 方法確定使用者是否在試用期內:
if ($user->onTrial()) {
// ...
}
要確定現有試用是否已過期,可以使用 hasExpiredTrial 方法:
if ($user->hasExpiredTrial()) {
// ...
}
要確定使用者是否正在對特定訂閱類型進行試用,你可以將類型提供給 onTrial 或 hasExpiredTrial 方法:
if ($user->onTrial('default')) {
// ...
}
if ($user->hasExpiredTrial('default')) {
// ...
}
無需預先提供付款方式 (Without Payment Method Up Front)
如果你想提供試用期而不預先收集使用者的付款方式資訊,你可以將附加到使用者的客戶記錄上的 trial_ends_at 欄位設定為你想要的試用結束日期。這通常在使用者註冊期間完成:
use App\Models\User;
$user = User::create([
// ...
]);
$user->createAsCustomer([
'trial_ends_at' => now()->addDays(10)
]);
Cashier 將這種類型的試用稱為「一般試用(Generic Trial)」,因為它未附加到任何現有訂閱。如果當前日期未超過 trial_ends_at 的值,User 實例上的 onTrial 方法將回傳 true:
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...
}
延長或啟用試用 (Extend or Activate a Trial)
你可以透過呼叫 extendTrial 方法並指定試用結束的時刻來延長訂閱的現有試用期:
$user->subscription()->extendTrial(now()->addDays(5));
或者,你可以透過呼叫訂閱上的 activate 方法來結束試用並立即啟用訂閱:
$user->subscription()->activate();
處理 Paddle Webhooks (Handling Paddle Webhooks)
Paddle 可以透過 webhooks 通知你的應用程式各種事件。預設情況下,Cashier 服務提供者會註冊一個指向 Cashier 的 webhook 控制器的路由。此控制器將處理所有傳入的 webhook 請求。
預設情況下,此控制器將自動處理取消失敗次數過多的訂閱、訂閱更新和付款方式變更;然而,正如我們很快就會發現的,你可以擴充此控制器來處理任何你喜歡的 Paddle webhook 事件。
為了確保你的應用程式可以處理 Paddle webhooks,請務必 在 Paddle 控制面板中設定 webhook URL。預設情況下,Cashier 的 webhook 控制器回應 /paddle/webhook URL 路徑。你應該在 Paddle 控制面板中啟用的所有 webhooks 完整列表如下:
- Customer Updated
- Transaction Completed
- Transaction Updated
- Subscription Created
- Subscription Updated
- Subscription Paused
- Subscription Canceled
[!WARNING] 確保使用 Cashier 包含的 webhook 簽章驗證 middleware 來保護傳入的請求。
Webhooks 與 CSRF 保護 (Webhooks and CSRF Protection)
由於 Paddle webhooks 需要繞過 Laravel 的 CSRF 保護,你應該確保 Laravel 不會試圖驗證傳入的 Paddle webhooks 的 CSRF token。為此,你應該在應用程式的 bootstrap/app.php 檔案中將 paddle/* 排除在 CSRF 保護之外:
->withMiddleware(function (Middleware $middleware): void {
$middleware->validateCsrfTokens(except: [
'paddle/*',
]);
})
Webhooks 與本地開發 (Webhooks and Local Development)
為了讓 Paddle 在本地開發期間能夠向你的應用程式發送 webhooks,你需要透過 Ngrok 或 Expose 等網站共用服務來公開你的應用程式。如果你使用 Laravel Sail 在本地開發應用程式,你可以使用 Sail 的 網站共用指令。
定義 Webhook 事件處理器 (Defining Webhook Event Handlers)
Cashier 自動處理失敗收費和其他常見 Paddle webhooks 的訂閱取消。但是,如果你有其他希望處理的 webhook 事件,你可以透過監聽 Cashier 發送的以下事件來實現:
Laravel\Paddle\Events\WebhookReceivedLaravel\Paddle\Events\WebhookHandled
這兩個事件都包含 Paddle webhook 的完整payload。例如,如果你希望處理 transaction.billed webhook,你可以註冊一個 listener 來處理該事件:
<?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 也會發送專用於收到的 webhook 類型的事件。除了來自 Paddle 的完整 payload 外,它們還包含用於處理 webhook 的相關模型,例如 billable 模型、訂閱或收據:
Laravel\Paddle\Events\CustomerUpdatedLaravel\Paddle\Events\TransactionCompletedLaravel\Paddle\Events\TransactionUpdatedLaravel\Paddle\Events\SubscriptionCreatedLaravel\Paddle\Events\SubscriptionUpdatedLaravel\Paddle\Events\SubscriptionPausedLaravel\Paddle\Events\SubscriptionCanceled
你還可以透過在應用程式的 .env 檔案中定義 CASHIER_WEBHOOK 環境變數來覆寫預設的內建 webhook 路由。此值應該是你的 webhook 路由的完整 URL,並且需要與你在 Paddle 控制面板中設定的 URL 相符:
CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url
驗證 Webhook 簽章 (Verifying Webhook Signatures)
為了保護你的 webhooks,你可以使用 Paddle 的 webhook 簽章。為了方便起見,Cashier 自動包含一個 middleware,用於驗證傳入的 Paddle webhook 請求是否有效。
要啟用 webhook 驗證,請確保在應用程式的 .env 檔案中定義了 PADDLE_WEBHOOK_SECRET 環境變數。可以從你的 Paddle 帳號儀表板取得 webhook secret。
單次收費 (Single Charges)
產品收費 (Charging for Products)
如果你想為客戶發起產品購買,你可以在 billable 模型實例上使用 checkout 方法來產生購買的結帳 session。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]);
});
產生結帳 session 後,你可以使用 Cashier 提供的 paddle-button Blade 元件 允許使用者檢視 Paddle 結帳小工具並完成購買:
<x-paddle-button :checkout="$checkout" className="px-8 py-4">
Buy
</x-paddle-button>
結帳 session 有一個 customData 方法,允許你將任何自訂資料傳遞給底層交易建立。請參閱 Paddle 文件 以了解有關在傳遞自訂資料時可用的選項的更多資訊:
$checkout = $user->checkout('pri_tshirt')
->customData([
'custom_option' => $value,
]);
退款交易 (Refunding Transactions)
退款交易將把退款金額退還到客戶購買時使用的付款方式。如果你需要退款 Paddle 購買,可以在 Cashier\Paddle\Transaction 模型上使用 refund 方法。此方法接受原因作為第一個參數,一個或多個要退款的價格 ID 以及選用的金額作為關聯陣列。你可以使用 transactions 方法取得給定 billable 模型的交易。
例如,想像我們想對價格 pri_123 和 pri_456 的特定交易進行退款。我們想全額退款 pri_123,但只退款 2 美元給 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 的退款文件。
[!WARNING] 退款在完全處理之前必須始終由 Paddle 核准。
信用交易 (Crediting Transactions)
就像退款一樣,你也可以信用交易。信用交易將資金新增到客戶的餘額中,以便將來用於購買。信用交易只能針對手動收集的交易進行,不能針對自動收集的交易(如訂閱)進行,因為 Paddle 會自動處理訂閱信用:
$transaction = $user->transactions()->first();
// Credit a specific line item fully...
$response = $transaction->credit('Compensation', 'pri_123');
有關更多資訊,請 參閱 Paddle 關於信用的文件。
[!WARNING] 信用只能應用於手動收集的交易。自動收集的交易由 Paddle 自己信用。
交易 (Transactions)
你可以透過 transactions 屬性輕鬆取得 billable 模型的交易陣列:
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');
過去與即將到來的付款 (Past and Upcoming Payments)
你可以使用 lastPayment 和 nextPayment 方法來取得並顯示客戶定期訂閱的過去或即將到來的付款:
use App\Models\User;
$user = User::find(1);
$subscription = $user->subscription();
$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();
這兩個方法都將回傳 Laravel\Paddle\Payment 的實例;然而,當交易尚未透過 webhooks 同步時,lastPayment 將回傳 null,而當計費週期結束時(例如訂閱已取消),nextPayment 將回傳 null:
Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}
測試 (Testing)
在測試時,你應該手動測試你的計費流程,以確保你的整合按預期工作。
對於自動化測試,包括在 CI 環境中執行的測試,你可以使用 Laravel 的 HTTP Client 來偽造對 Paddle 進行的 HTTP 呼叫。雖然這不會測試來自 Paddle 的實際回應,但它提供了一種在不實際呼叫 Paddle API 的情況下測試應用程式的方法。