コンテンツにスキップ

Laravel Cashier (Paddle)

はじめに

exclamation

このドキュメントは、Paddle BillingとのCashier Paddle 2.xの統合に関するものです。まだPaddle Classicを使用している場合は、Cashier Paddle 1.xを使用する必要があります。

Laravel Cashier Paddleは、Paddleのサブスクリプション請求サービスへの表現力豊かで流暢なインターフェースを提供します。あなたが嫌がるほとんどの定型的なサブスクリプション請求コードを処理します。基本的なサブスクリプション管理に加えて、Cashierは、サブスクリプションの交換、サブスクリプションの「数量」、サブスクリプションの一時停止、キャンセル猶予期間などを処理できます。

Cashier Paddleを詳しく調べる前に、Paddleの概念ガイドAPIドキュメントも確認することをお勧めします。

Cashierのアップグレード

Cashierの新しいバージョンにアップグレードする場合は、アップグレードガイドを注意深く確認することが重要です。

インストール

まず、Composerパッケージマネージャーを使用して、Paddle用のCashierパッケージをインストールします。

composer require laravel/cashier-paddle

次に、vendor:publish Artisanコマンドを使用して、Cashierのマイグレーションファイルを公開する必要があります。

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

次に、アプリケーションのデータベースマイグレーションを実行する必要があります。Cashierのマイグレーションにより、新しいcustomersテーブルが作成されます。さらに、顧客のすべてのサブスクリプションを保存するために、新しいsubscriptionsテーブルとsubscription_itemsテーブルが作成されます。最後に、顧客に関連付けられたすべてのPaddleトランザクションを保存するために、新しいtransactionsテーブルが作成されます。

php artisan migrate
exclamation

CashierがすべてのPaddleイベントを適切に処理するようにするには、CashierのWebhook処理を設定することを忘れないでください。

Paddleサンドボックス

ローカルおよびステージング開発中は、Paddle Sandboxアカウントを登録する必要があります。このアカウントを使用すると、実際の支払いを行うことなく、アプリケーションをテストおよび開発するためのサンドボックス環境が提供されます。Paddleのテストカード番号を使用して、さまざまな支払いシナリオをシミュレートできます。

Paddle Sandbox環境を使用する場合は、アプリケーションの.envファイル内でPADDLE_SANDBOX環境変数をtrueに設定する必要があります。

PADDLE_SANDBOX=true

アプリケーションの開発が完了したら、Paddleベンダーアカウントを申請できます。アプリケーションを本番環境に配置する前に、Paddleはアプリケーションのドメインを承認する必要があります。

設定

課金対象モデル

Cashierを使用する前に、ユーザーモデル定義にBillableトレイトを追加する必要があります。このトレイトは、サブスクリプションの作成や支払い方法情報の更新など、一般的な請求タスクを実行できるようにするためのさまざまなメソッドを提供します。

use Laravel\Paddle\Billable;
 
class User extends Authenticatable
{
use Billable;
}

ユーザーではない課金対象エンティティがある場合は、それらのクラスにトレイトを追加することもできます。

use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
 
class Team extends Model
{
use Billable;
}

APIキー

次に、アプリケーションの.envファイルでPaddleキーを設定する必要があります。Paddle APIキーは、Paddleコントロールパネルから取得できます。

PADDLE_CLIENT_SIDE_TOKEN=your-paddle-client-side-token
PADDLE_API_KEY=your-paddle-api-key
PADDLE_RETAIN_KEY=your-paddle-retain-key
PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret"
PADDLE_SANDBOX=true

PADDLE_SANDBOX環境変数は、PaddleのSandbox環境を使用している場合はtrueに設定する必要があります。アプリケーションを本番環境にデプロイし、Paddleのライブベンダー環境を使用している場合は、PADDLE_SANDBOX変数をfalseに設定する必要があります。

PADDLE_RETAIN_KEYはオプションであり、RetainでPaddleを使用している場合にのみ設定する必要があります。

Paddle JS

Paddleは、Paddleチェックアウトウィジェットを開始するために、独自のJavaScriptライブラリに依存しています。アプリケーションレイアウトの終了</head>タグの直前に@paddleJS Bladeディレクティブを配置することで、JavaScriptライブラリをロードできます。

<head>
...
 
@paddleJS
</head>

通貨設定

請求書に表示される金額の書式設定に使用されるロケールを指定できます。内部的には、CashierはPHPのNumberFormatterクラスを利用して通貨ロケールを設定します。

CASHIER_CURRENCY_LOCALE=nl_BE
exclamation

en以外のロケールを使用するには、サーバーにext-intl PHP拡張機能がインストールおよび設定されていることを確認してください。

デフォルトモデルの上書き

独自のモデルを定義し、対応する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);
}

クイックスタート

製品の販売

lightbulb

Paddle Checkoutを利用する前に、Paddleダッシュボードで固定価格の製品を定義する必要があります。さらに、PaddleのWebhook処理を設定する必要があります。

アプリケーションを介して製品とサブスクリプションの請求を提供することは難しいかもしれません。ただし、CashierとPaddleのチェックアウトオーバーレイのおかげで、最新の堅牢な支払い統合を簡単に構築できます。

非反復の単一料金製品に対して顧客に課金するために、Cashierを使用してPaddleのチェックアウトオーバーレイで顧客に課金します。顧客は支払い詳細を入力し、購入を確認します。チェックアウトオーバーレイを介して支払いが完了すると、顧客はアプリケーション内でお客様が選択した成功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メソッドを使用して、指定された「価格識別子」に対して顧客にPaddleチェックアウトオーバーレイを表示するためのチェックアウトオブジェクトを作成します。Paddleを使用する場合、「価格」は、特定の製品に対して定義された価格を指します。

必要に応じて、checkoutメソッドはPaddleに顧客を自動的に作成し、そのPaddle顧客レコードをアプリケーションのデータベースの対応するユーザーに接続します。チェックアウトセッションが完了すると、顧客は専用の成功ページにリダイレクトされ、そこで顧客に情報メッセージを表示できます。

buyビューでは、チェックアウトオーバーレイを表示するためのボタンを含めます。paddle-button BladeコンポーネントはCashier Paddleに含まれています。ただし、オーバーレイチェックアウトを手動でレンダリングすることもできます。

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

Paddleチェックアウトにメタデータを提供する

製品を販売する際、完了した注文や購入された製品を、アプリケーションで定義されたCartおよびOrderモデルを使用して追跡するのが一般的です。顧客をPaddleのチェックアウトオーバーレイにリダイレクトして購入を完了させる際、顧客がアプリケーションに戻されたときに、完了した購入を対応する注文に関連付けられるように、既存の注文識別子を提供する必要がある場合があります。

これを実現するには、checkoutメソッドにカスタムデータの配列を提供できます。ユーザーがチェックアウトプロセスを開始するときに、保留中のOrderがアプリケーション内で作成されるとしましょう。この例のCartOrderモデルは例示であり、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');

上記の例でわかるように、ユーザーがチェックアウトプロセスを開始するとき、カート/注文に関連付けられたすべてのPaddle価格識別子をcheckoutメソッドに提供します。もちろん、顧客がアイテムを追加する際に、アプリケーションはこれらのアイテムを「ショッピングカート」または注文に関連付ける必要があります。また、customDataメソッドを介して、注文のIDをPaddleチェックアウトオーバーレイに提供します。

もちろん、顧客がチェックアウトプロセスを完了したら、注文を「完了」としてマークしたいでしょう。これを実現するには、Paddleによってディスパッチされ、Cashierによってイベントを介して発生するWebhookをリッスンして、注文情報をデータベースに保存します。

開始するには、CashierによってディスパッチされるTransactionCompletedイベントをリッスンします。通常、イベントリスナーはアプリケーションのAppServiceProviderbootメソッドで登録する必要があります。

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のドキュメント(https://developer.paddle.com/webhooks/transactions/transaction-completed)を参照してください。

サブスクリプションの販売

lightbulb

Paddle Checkoutを利用する前に、Paddleダッシュボードで固定価格の製品を定義する必要があります。さらに、PaddleのWebhook処理を設定する必要があります。

アプリケーションを介して製品とサブスクリプションの請求を提供することは難しいかもしれません。ただし、CashierとPaddleのチェックアウトオーバーレイのおかげで、最新の堅牢な支払い統合を簡単に構築できます。

CashierとPaddleのチェックアウトオーバーレイを使用してサブスクリプションを販売する方法を学ぶために、基本的な月額(price_basic_monthly)プランと年額(price_basic_yearly)プランを備えたサブスクリプションサービスの簡単なシナリオを考えてみましょう。これらの2つの価格は、Paddleダッシュボードの「Basic」製品(pro_basic)の下にグループ化できます。さらに、サブスクリプションサービスはpro_expertとしてExpertプランを提供する場合があります。

まず、顧客がどのようにサービスをサブスクライブできるかを見てみましょう。もちろん、顧客がアプリケーションの価格ページでBasicプランの「サブスクライブ」ボタンをクリックすると想像できます。このボタンは、選択したプランのPaddleチェックアウトオーバーレイを呼び出します。開始するには、checkoutメソッドを介してチェックアウトセッションを開始しましょう。

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

subscribeビューには、チェックアウトオーバーレイを表示するボタンを含めます。paddle-button BladeコンポーネントはCashier Paddleに含まれていますが、手動でオーバーレイチェックアウトをレンダリングすることもできます。

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

さて、「サブスクライブ」ボタンがクリックされると、顧客は支払い情報を入力してサブスクリプションを開始できます。(一部の支払い方法では処理に数秒かかるため)サブスクリプションが実際に開始されたことを知るには、CashierのWebhook処理も構成する必要があります。

顧客がサブスクリプションを開始できるようになったので、サブスクライブしたユーザーのみがアクセスできるアプリケーションの特定の部分を制限する必要があります。もちろん、CashierのBillableトレイトによって提供される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

サブスクライブされたミドルウェアの構築

便宜上、受信リクエストがサブスクライブされたユーザーからのものかどうかを判断するミドルウェアを作成することをお勧めします。このミドルウェアが定義されたら、サブスクライブしていないユーザーがルートにアクセスできないように、ルートに簡単に割り当てることができます。

<?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);
}
}

ミドルウェアが定義されたら、ルートに割り当てることができます。

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

顧客が請求プランを管理できるようにする

もちろん、顧客はサブスクリプションプランを別の製品または「ティア」に変更したい場合があります。上記の例では、顧客が月額サブスクリプションから年額サブスクリプションにプランを変更できるようにしたいでしょう。これには、以下のルートにつながるボタンのようなものを実装する必要があります。

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

プランの交換に加えて、顧客がサブスクリプションをキャンセルできるようにする必要もあります。プランの交換と同様に、次のルートにつながるボタンを用意します。

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

これで、サブスクリプションは請求期間の終わりにキャンセルされます。

lightbulb

CashierのWebhook処理を構成している限り、CashierはPaddleからの受信Webhookを検査することで、アプリケーションのCashier関連のデータベーステーブルを自動的に同期させます。たとえば、Paddleのダッシュボードを介して顧客のサブスクリプションをキャンセルすると、Cashierは対応するWebhookを受信し、アプリケーションのデータベースでサブスクリプションを「キャンセル済み」としてマークします。

チェックアウトセッション

顧客への請求のほとんどの操作は、Paddleのチェックアウトオーバーレイウィジェットまたはインラインチェックアウトを利用した「チェックアウト」を使用して実行されます。

Paddleを使用してチェックアウト支払いを処理する前に、Paddleチェックアウト設定ダッシュボードでアプリケーションのデフォルト支払いリンクを定義する必要があります。

オーバーレイチェックアウト

チェックアウトオーバーレイウィジェットを表示する前に、Cashierを使用してチェックアウトセッションを生成する必要があります。チェックアウトセッションは、実行する請求操作をチェックアウトウィジェットに通知します。

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

Cashierにはpaddle-buttonBladeコンポーネントが含まれています。チェックアウトセッションを「プロップ」としてこのコンポーネントに渡すことができます。次に、このボタンがクリックされると、Paddleのチェックアウトウィジェットが表示されます。

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

デフォルトでは、これはPaddleのデフォルトのスタイルを使用してウィジェットを表示します。コンポーネントにdata-theme='light'属性のようなPaddleでサポートされている属性を追加することで、ウィジェットをカスタマイズできます。

<x-paddle-button :url="$payLink" class="px-8 py-4" data-theme="light">
Subscribe
</x-paddle-button>

Paddleチェックアウトウィジェットは非同期です。ユーザーがウィジェット内でサブスクリプションを作成すると、PaddleはアプリケーションにWebhookを送信して、アプリケーションのデータベースでサブスクリプションの状態を適切に更新できるようにします。したがって、Paddleからの状態変更に対応するために、Webhookを適切に設定することが重要です。

exclamation

サブスクリプションの状態変更後、対応するWebhookを受信するまでの遅延は通常最小限ですが、チェックアウトが完了した後、ユーザーのサブスクリプションがすぐに利用可能にならない可能性があることを考慮して、アプリケーションでこれを考慮する必要があります。

オーバーレイチェックアウトの手動レンダリング

Laravelの組み込みBladeコンポーネントを使用せずに、オーバーレイチェックアウトを手動でレンダリングすることもできます。開始するには、前の例で示したように、チェックアウトセッションを生成します。

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はこのクラスを検出し、リンクがクリックされるとオーバーレイチェックアウトを表示します。

<?php
$items = $checkout->getItems();
$customer = $checkout->getCustomer();
$custom = $checkout->getCustomData();
?>
 
<a
href='#!'
class='paddle_button'
data-items='{!! json_encode($items) !!}'
@if ($customer) data-customer-id='{{ $customer->paddle_id }}' @endif
@if ($custom) data-custom-data='{{ json_encode($custom) }}' @endif
@if ($returnUrl = $checkout->getReturnUrl()) data-success-url='{{ $returnUrl }}' @endif
>
Buy Product
</a>

インラインチェックアウト

Paddleの「オーバーレイ」スタイルのチェックアウトウィジェットを使用したくない場合、Paddleはウィジェットをインラインで表示するオプションも提供します。このアプローチでは、チェックアウトのHTMLフィールドを調整することはできませんが、アプリケーション内にウィジェットを埋め込むことができます。

インラインチェックアウトを簡単に開始できるようにするために、Cashierにはpaddle-checkout Bladeコンポーネントが含まれています。開始するには、チェックアウトセッションを生成する必要があります。

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

次に、チェックアウトセッションをコンポーネントのcheckout属性に渡すことができます。

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

インラインチェックアウトコンポーネントの高さを調整するには、height属性をBladeコンポーネントに渡すことができます。

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

インラインチェックアウトのカスタマイズオプションの詳細については、Paddleのインラインチェックアウトガイドおよび利用可能なチェックアウト設定を参照してください。

インラインチェックアウトの手動レンダリング

Laravelの組み込みBladeコンポーネントを使用せずに、インラインチェックアウトを手動でレンダリングすることもできます。開始するには、前の例で示したように、チェックアウトセッションを生成します。

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

次に、Paddle.jsを使用してチェックアウトを初期化できます。この例では、Alpine.jsを使用してこれをデモンストレーションします。ただし、自身のフロントエンドスタックに合わせてこの例を自由に変更できます。

<?php
$options = $checkout->options();
 
$options['settings']['frameTarget'] = 'paddle-checkout';
$options['settings']['frameInitialHeight'] = 366;
?>
 
<div class="paddle-checkout" x-data="{}" x-init="
Paddle.Checkout.open(@json($options));
">
</div>

ゲストチェックアウト

アプリケーションのアカウントを必要としないユーザーのチェックアウトセッションを作成する必要がある場合があります。これを行うには、guestメソッドを使用できます。

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

次に、チェックアウトセッションをPaddleボタンまたはインラインチェックアウトBladeコンポーネントに提供できます。

価格プレビュー

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ドキュメントを確認してください

顧客価格プレビュー

ユーザーが既に顧客であり、その顧客に適用される価格を表示する場合は、顧客インスタンスから直接価格を取得することでそれを行うことができます。

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

内部的には、Cashierはユーザーの顧客IDを使用して、その通貨で価格を取得します。たとえば、米国に住んでいるユーザーには米ドルで価格が表示され、ベルギーのユーザーにはユーロで価格が表示されます。一致する通貨が見つからない場合は、製品のデフォルト通貨が使用されます。Paddleコントロールパネルで製品またはサブスクリプションプランのすべての価格をカスタマイズできます。

割引

割引後の価格を表示することもできます。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>

顧客

顧客デフォルト

Cashierを使用すると、チェックアウトセッションを作成する際に、顧客に役立つデフォルトをいくつか定義できます。これらのデフォルトを設定すると、顧客のメールアドレスと名前が事前に入力され、チェックアウトウィジェットの支払い部分にすぐに移動できるようになります。請求可能モデルで次のメソッドをオーバーライドすることで、これらのデフォルトを設定できます。

/**
* Get the customer's name to associate with Paddle.
*/
public function paddleName(): string|null
{
return $this->name;
}
 
/**
* Get the customer's email address to associate with Paddle.
*/
public function paddleEmail(): string|null
{
return $this->email;
}

これらのデフォルトは、チェックアウトセッションを生成するCashierのすべてのアクションで使用されます。

顧客の取得

Cashier::findBillableメソッドを使用して、Paddle顧客IDで顧客を取得できます。このメソッドは、請求可能モデルのインスタンスを返します。

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

顧客の作成

場合によっては、サブスクリプションを開始せずにPaddle顧客を作成したい場合があります。これは、createAsCustomerメソッドを使用して実行できます。

$customer = $user->createAsCustomer();

Laravel\Paddle\Customerのインスタンスが返されます。Paddleで顧客が作成されたら、後でサブスクリプションを開始できます。オプションの$options配列を提供して、Paddle APIでサポートされている追加の顧客作成パラメーターを渡すことができます。

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

サブスクリプション

サブスクリプションの作成

サブスクリプションを作成するには、まずデータベースから請求可能モデルのインスタンスを取得します。これは通常App\Models\Userのインスタンスになります。モデルインスタンスを取得したら、subscribeメソッドを使用してモデルのチェックアウトセッションを作成できます。

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe($premium = 12345, 'default')
->returnTo(route('home'));
 
return view('billing', ['checkout' => $checkout]);
});

subscribeメソッドに渡される最初の引数は、ユーザーがサブスクライブする特定の価格です。この値は、Paddleの価格の識別子に対応する必要があります。returnToメソッドは、チェックアウトが正常に完了した後にユーザーがリダイレクトされるURLを受け入れます。subscribeメソッドに渡される2番目の引数は、サブスクリプションの内部「タイプ」である必要があります。アプリケーションが単一のサブスクリプションのみを提供している場合は、これをdefaultまたはprimaryと呼ぶことができます。このサブスクリプションタイプは、内部アプリケーションでのみ使用することを目的としており、ユーザーに表示することを目的としたものではありません。さらに、スペースを含めず、サブスクリプションを作成した後で変更しないでください。

customDataメソッドを使用して、サブスクリプションに関するカスタムメタデータの配列を提供することもできます。

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

サブスクリプションのチェックアウトセッションが作成されると、そのチェックアウトセッションは、Cashier Paddle に含まれているpaddle-button Bladeコンポーネントに提供できます。

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

ユーザーがチェックアウトを完了すると、Paddleからsubscription_created Webhookがディスパッチされます。Cashier はこのWebhookを受け取り、顧客のサブスクリプションを設定します。アプリケーションがすべてのWebhookを正しく受信して処理するように、Webhook処理の設定が適切に行われていることを確認してください。

サブスクリプションステータスの確認

ユーザーがアプリケーションをサブスクライブすると、さまざまな便利なメソッドを使用してサブスクリプションのステータスを確認できます。まず、subscribedメソッドは、サブスクリプションがトライアル期間中であっても、ユーザーが有効なサブスクリプションを持っている場合はtrueを返します。

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

アプリケーションが複数のサブスクリプションを提供している場合、subscribedメソッドを呼び出すときにサブスクリプションを指定できます。

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

subscribedメソッドは、ルートミドルウェアの優れた候補にもなり、ユーザーのサブスクリプションステータスに基づいてルートやコントローラーへのアクセスをフィルタリングできます。

<?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メソッドを使用できます。

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

また、ユーザーがサブスクリプションをキャンセルしたが、サブスクリプションが完全に期限切れになるまで「猶予期間」内であるかどうかを確認することもできます。たとえば、3月5日にサブスクリプションをキャンセルしたユーザーが、もともと3月10日に期限切れになる予定だった場合、ユーザーは3月10日まで「猶予期間」になります。さらに、この期間中もsubscribedメソッドはtrueを返します。

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

支払い期限超過ステータス

サブスクリプションの支払いが失敗した場合、past_dueとしてマークされます。サブスクリプションがこの状態にある場合、顧客が支払い情報を更新するまでアクティブになりません。サブスクリプションが期限切れになっているかどうかは、サブスクリプションインスタンスのpastDueメソッドを使用して判断できます。

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

サブスクリプションの支払いが期限切れになった場合は、ユーザーに支払い情報の更新を指示する必要があります。

past_dueの場合でもサブスクリプションを有効とみなしたい場合は、Cashierが提供するkeepPastDueSubscriptionsActiveメソッドを使用できます。通常、このメソッドはAppServiceProviderregisterメソッドで呼び出す必要があります。

use Laravel\Paddle\Cashier;
 
/**
* Register any application services.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
}
exclamation

サブスクリプションがpast_due状態の場合、支払い情報が更新されるまで変更できません。したがって、サブスクリプションがpast_due状態の場合、swapメソッドとupdateQuantityメソッドは例外をスローします。

サブスクリプションスコープ

ほとんどのサブスクリプション状態はクエリスコープとしても使用できるため、特定の状態にあるサブスクリプションをデータベースで簡単にクエリできます。

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

利用可能なスコープの完全なリストは以下にあります。

Subscription::query()->valid();
Subscription::query()->onTrial();
Subscription::query()->expiredTrial();
Subscription::query()->notOnTrial();
Subscription::query()->active();
Subscription::query()->recurring();
Subscription::query()->pastDue();
Subscription::query()->paused();
Subscription::query()->notPaused();
Subscription::query()->onPausedGracePeriod();
Subscription::query()->notOnPausedGracePeriod();
Subscription::query()->canceled();
Subscription::query()->notCanceled();
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();

サブスクリプションの単一課金

サブスクリプションの単一料金を使用すると、サブスクリプションに加えて、サブスクライバーに1回限りの料金を請求できます。chargeメソッドを呼び出すときに、1つまたは複数の価格IDを提供する必要があります。

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

chargeメソッドは、サブスクリプションの次の請求間隔まで顧客に実際に請求しません。顧客にすぐに請求したい場合は、代わりにchargeAndInvoiceメソッドを使用できます。

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

支払い情報の更新

Paddleは常にサブスクリプションごとに支払い方法を保存します。サブスクリプションのデフォルトの支払い方法を更新したい場合は、サブスクリプションモデルの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がディスパッチされ、アプリケーションのデータベースでサブスクリプションの詳細が更新されます。

プランの変更

ユーザーがアプリケーションをサブスクライブした後、新しいサブスクリプションプランに変更したい場合があります。ユーザーのサブスクリプションプランを更新するには、サブスクリプションのswapメソッドにPaddleの価格の識別子を渡す必要があります。

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

プランを交換し、次の請求サイクルを待たずにユーザーにすぐに請求したい場合は、swapAndInvoiceメソッドを使用できます。

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

日割計算

デフォルトでは、Paddleはプランを切り替える際に料金を日割り計算します。noProrateメソッドを使用すると、料金を日割り計算せずにサブスクリプションを更新できます。

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

日割り計算を無効にして、顧客にすぐに請求する場合は、noProrateと組み合わせてswapAndInvoiceメソッドを使用できます。

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

または、サブスクリプションの変更について顧客に請求しない場合は、doNotBillメソッドを使用できます。

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

Paddleの日割り計算ポリシーの詳細については、Paddleの日割り計算に関するドキュメントを参照してください。

サブスクリプションの数量

サブスクリプションが「数量」の影響を受ける場合があります。たとえば、プロジェクト管理アプリケーションでは、プロジェクトごとに月額10ドルを請求する場合があります。サブスクリプションの数量を簡単に増減するには、incrementQuantityメソッドとdecrementQuantityメソッドを使用します。

$user = User::find(1);
 
$user->subscription()->incrementQuantity();
 
// Add five to the subscription's current quantity...
$user->subscription()->incrementQuantity(5);
 
$user->subscription()->decrementQuantity();
 
// Subtract five from the subscription's current quantity...
$user->subscription()->decrementQuantity(5);

または、updateQuantityメソッドを使用して特定の数量を設定することもできます。

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

noProrateメソッドを使用すると、料金を日割り計算せずにサブスクリプションの数量を更新できます。

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

複数の製品を持つサブスクリプションの数量

サブスクリプションが複数の製品を持つサブスクリプションの場合、増減する数量の価格のIDをincrement / decrementメソッドの2番目の引数として渡す必要があります。

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

複数の製品を含むサブスクリプション

複数の製品を持つサブスクリプションを使用すると、複数の請求製品を1つのサブスクリプションに割り当てることができます。たとえば、月額基本料金が10ドルのカスタマーサービス「ヘルプデスク」アプリケーションを構築していて、月額15ドルの追加料金でライブチャットアドオン製品を提供しているとします。

サブスクリプションのチェックアウトセッションを作成する際、subscribeメソッドの最初の引数として価格の配列を渡すことで、特定のサブスクリプションに対して複数の製品を指定できます。

use Illuminate\Http\Request;
 
Route::post('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe([
'price_monthly',
'price_chat',
]);
 
return view('billing', ['checkout' => $checkout]);
});

上記の例では、顧客のdefaultサブスクリプションに2つの価格が添付されます。両方の価格は、それぞれの請求間隔で請求されます。必要に応じて、キー/値ペアの連想配列を渡して、各価格の特定の数量を示すことができます。

$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]);
exclamation

サブスクリプションの最後の価格を削除することはできません。代わりに、サブスクリプションをキャンセルする必要があります。

複数のサブスクリプション

Paddleを使用すると、顧客は複数のサブスクリプションを同時に持つことができます。たとえば、水泳サブスクリプションとウェイトリフティングサブスクリプションを提供するジムを運営している場合、各サブスクリプションは異なる価格設定を持つ場合があります。もちろん、顧客はどちらかのプランまたは両方のプランをサブスクライブできる必要があります。

アプリケーションがサブスクリプションを作成するとき、subscribeメソッドの2番目の引数として、サブスクリプションのタイプを提供できます。タイプは、ユーザーが開始しているサブスクリプションのタイプを表す任意の文字列にできます。

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

この例では、顧客の月額水泳サブスクリプションを開始しました。ただし、顧客は後で年間サブスクリプションに切り替えたい場合があります。顧客のサブスクリプションを調整するときは、swimmingサブスクリプションの価格を簡単に変更できます。

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

もちろん、サブスクリプションを完全にキャンセルすることもできます。

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

サブスクリプションの一時停止

サブスクリプションを一時停止するには、ユーザーのサブスクリプションでpauseメソッドを呼び出します。

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

サブスクリプションが一時停止されると、Cashierはデータベースのpaused_at列を自動的に設定します。この列は、pausedメソッドがtrueを返すタイミングを判断するために使用されます。たとえば、顧客が3月1日にサブスクリプションを一時停止したが、サブスクリプションは3月5日まで再開するようにスケジュールされていなかった場合、pausedメソッドは3月5日までfalseを返し続けます。これは、ユーザーが通常、請求サイクルの終了までアプリケーションの使用を許可されているためです。

デフォルトでは、一時停止は次の請求間隔で行われるため、顧客は支払った期間の残りの部分を使用できます。サブスクリプションをすぐに一時停止する場合は、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();
exclamation

サブスクリプションは一時停止中は変更できません。別のプランに切り替えたり、数量を更新したりする場合は、最初にサブスクリプションを再開する必要があります。

サブスクリプションのキャンセル

サブスクリプションをキャンセルするには、ユーザーのサブスクリプションでcancelメソッドを呼び出します。

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

サブスクリプションがキャンセルされると、Cashierはデータベースのends_at列を自動的に設定します。この列は、subscribedメソッドがfalseを返すタイミングを判断するために使用されます。たとえば、顧客が3月1日にサブスクリプションをキャンセルしたが、サブスクリプションは3月5日まで終了するようにスケジュールされていなかった場合、subscribedメソッドは3月5日までtrueを返し続けます。これは、ユーザーが通常、請求サイクルの終了までアプリケーションの使用を許可されているためです。

ユーザーがサブスクリプションをキャンセルしたが、onGracePeriodメソッドを使用して、まだ「猶予期間」内であるかどうかを判断できます。

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

サブスクリプションをすぐにキャンセルする場合は、サブスクリプションでcancelNowメソッドを呼び出すことができます。

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

猶予期間中のサブスクリプションのキャンセルを停止するには、stopCancelationメソッドを呼び出すことができます。

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

Paddleのサブスクリプションは、キャンセル後に再開することはできません。顧客がサブスクリプションを再開したい場合は、新しいサブスクリプションを作成する必要があります。

サブスクリプションのトライアル

前払いでの支払い方法

支払い方法の情報を事前に収集しながら、顧客にトライアル期間を提供したい場合は、顧客がサブスクライブする価格のPaddleダッシュボードでトライアル時間を設定する必要があります。その後、通常どおりチェックアウトセッションを開始します。

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$checkout = $request->user()->subscribe('pri_monthly')
->returnTo(route('home'));
 
return view('billing', ['checkout' => $checkout]);
});

アプリケーションがsubscription_createdイベントを受信すると、Cashierは、アプリケーションのデータベース内のサブスクリプションレコードにトライアル期間の終了日を設定し、この日まで顧客への請求を開始しないようにPaddleに指示します。

exclamation

顧客のサブスクリプションがトライアル終了日までにキャンセルされない場合、トライアルが終了するとすぐに請求されるため、ユーザーにトライアル終了日を通知するようにしてください。

ユーザーがトライアル期間内であるかどうかを判断するには、ユーザーインスタンスのonTrialメソッドまたはサブスクリプションインスタンスのonTrialメソッドのいずれかを使用できます。以下の2つの例は同等です。

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

既存のトライアルが期限切れになったかどうかを判断するには、hasExpiredTrialメソッドを使用できます。

if ($user->hasExpiredTrial()) {
// ...
}
 
if ($user->subscription()->hasExpiredTrial()) {
// ...
}

特定のサブスクリプションタイプのトライアルをユーザーが利用中かどうかを判断するには、onTrial メソッドまたは hasExpiredTrial メソッドにタイプを指定できます。

if ($user->onTrial('default')) {
// ...
}
 
if ($user->hasExpiredTrial('default')) {
// ...
}

前払いなしでの支払い方法

ユーザーの支払い方法情報を事前に収集せずにトライアル期間を提供したい場合は、ユーザーに関連付けられた顧客レコードの trial_ends_at カラムに、希望するトライアル終了日を設定できます。これは通常、ユーザー登録時に行われます。

use App\Models\User;
 
$user = User::create([
// ...
]);
 
$user->createAsCustomer([
'trial_ends_at' => now()->addDays(10)
]);

Cashier は、このタイプのトライアルを「ジェネリックトライアル」と呼びます。これは、既存のサブスクリプションには関連付けられていないためです。 User インスタンスの onTrial メソッドは、現在の日付が trial_ends_at の値を超えていない場合に true を返します。

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

ユーザーの実際のサブスクリプションを作成する準備ができたら、通常どおり subscribe メソッドを使用できます。

use Illuminate\Http\Request;
 
Route::get('/user/subscribe', function (Request $request) {
$checkout = $user->subscribe('pri_monthly')
->returnTo(route('home'));
 
return view('billing', ['checkout' => $checkout]);
});

ユーザーのトライアル終了日を取得するには、trialEndsAt メソッドを使用できます。このメソッドは、ユーザーがトライアル中の場合は Carbon の日付インスタンスを返し、そうでない場合は null を返します。デフォルト以外の特定のサブスクリプションのトライアル終了日を取得する場合は、オプションのサブスクリプションタイプパラメーターを渡すこともできます。

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

ユーザーが「ジェネリック」トライアル期間内で、まだ実際のサブスクリプションを作成していないことを具体的に知りたい場合は、onGenericTrial メソッドを使用できます。

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

トライアルの延長またはアクティブ化

サブスクリプションの既存のトライアル期間を延長するには、extendTrial メソッドを呼び出し、トライアルを終了する時点を指定します。

$user->subscription()->extendTrial(now()->addDays(5));

または、サブスクリプションのトライアルを終了する activate メソッドを呼び出すことで、サブスクリプションをすぐに有効にすることもできます。

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

Paddle Webhookの処理

Paddle は、ウェブフックを介してさまざまなイベントをアプリケーションに通知できます。デフォルトでは、Cashier のウェブフックコントローラーを指すルートが Cashier サービスプロバイダーによって登録されます。このコントローラーは、すべての着信ウェブフックリクエストを処理します。

デフォルトでは、このコントローラーは、料金請求が多すぎるためにキャンセルされたサブスクリプション、サブスクリプションの更新、支払い方法の変更を自動的に処理します。ただし、後で説明するように、このコントローラーを拡張して、任意の Paddle ウェブフックイベントを処理できます。

アプリケーションが Paddle ウェブフックを処理できるようにするには、Paddle コントロールパネルでウェブフック URL を構成してください。デフォルトでは、Cashier のウェブフックコントローラーは /paddle/webhook URL パスに応答します。Paddle コントロールパネルで有効にする必要があるすべてのウェブフックの完全なリストは次のとおりです。

  • 顧客が更新されました
  • トランザクションが完了しました
  • トランザクションが更新されました
  • サブスクリプションが作成されました
  • サブスクリプションが更新されました
  • サブスクリプションが一時停止されました
  • サブスクリプションがキャンセルされました
exclamation

受信リクエストを、Cashier に含まれる ウェブフック署名検証 ミドルウェアで保護するようにしてください。

ウェブフックと CSRF 保護

Paddle ウェブフックは Laravel の CSRF 保護をバイパスする必要があるため、着信する Paddle ウェブフックに対して Laravel が CSRF トークンの検証を試みないようにする必要があります。これを実現するには、アプリケーションの bootstrap/app.php ファイルで CSRF 保護から paddle/* を除外する必要があります。

->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'paddle/*',
]);
})

ウェブフックとローカル開発

ローカル開発中に Paddle がアプリケーションにウェブフックを送信できるようにするには、NgrokExpose などのサイト共有サービスを介してアプリケーションを公開する必要があります。 Laravel Sail を使用してアプリケーションをローカルで開発している場合は、Sail の サイト共有コマンドを使用できます。

Webhookイベントハンドラーの定義

Cashier は、料金請求の失敗やその他の一般的な Paddle ウェブフックでのサブスクリプションのキャンセルを自動的に処理します。ただし、追加のウェブフックイベントを処理する場合は、Cashier によってディスパッチされる次のイベントをリッスンすることで処理できます。

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

どちらのイベントにも、Paddle ウェブフックの完全なペイロードが含まれています。たとえば、transaction.billed ウェブフックを処理する場合は、イベントを処理する リスナーを登録できます。

<?php
 
namespace App\Listeners;
 
use Laravel\Paddle\Events\WebhookReceived;
 
class PaddleEventListener
{
/**
* Handle received Paddle webhooks.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['event_type'] === 'transaction.billed') {
// Handle the incoming event...
}
}
}

Cashier は、受信したウェブフックのタイプ専用のイベントも発行します。Paddle からの完全なペイロードに加えて、請求可能モデル、サブスクリプション、領収書など、ウェブフックの処理に使用された関連モデルも含まれます。

  • Laravel\Paddle\Events\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 環境変数を定義することにより、デフォルトの組み込みウェブフックルートをオーバーライドすることもできます。この値は、ウェブフックルートの完全な URL である必要があり、Paddle コントロールパネルで設定された URL と一致する必要があります。

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

Webhook署名の検証

ウェブフックを保護するには、Paddle のウェブフック署名を使用できます。便宜上、Cashier には、着信する Paddle ウェブフックリクエストが有効であることを検証するミドルウェアが自動的に含まれています。

ウェブフック検証を有効にするには、アプリケーションの .env ファイルで PADDLE_WEBHOOK_SECRET 環境変数が定義されていることを確認してください。ウェブフックシークレットは、Paddle アカウントのダッシュボードから取得できます。

単一課金

製品の課金

顧客の製品購入を開始する場合は、請求可能モデルインスタンスの checkout メソッドを使用して、購入用のチェックアウトセッションを生成できます。 checkout メソッドは、1 つまたは複数の価格 ID を受け入れます。必要に応じて、連想配列を使用して、購入する製品の数量を指定できます。

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

チェックアウトセッションを生成したら、Cashier が提供する paddle-button Blade コンポーネントを使用して、ユーザーが Paddle チェックアウトウィジェットを表示し、購入を完了できるようにすることができます。

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

チェックアウトセッションには customData メソッドがあり、基礎となるトランザクションの作成に希望するカスタムデータを渡すことができます。カスタムデータを渡すときに利用できるオプションの詳細については、Paddle のドキュメントを参照してください。

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

トランザクションの払い戻し

トランザクションを払い戻すと、購入時に使用された顧客の支払い方法に払い戻し金額が返されます。 Paddle での購入を払い戻す必要がある場合は、Cashier\Paddle\Transaction モデルの refund メソッドを使用できます。このメソッドは、最初の引数として理由を受け取り、払い戻す 1 つ以上の価格 ID をオプションの金額を連想配列として受け取ります。 transactions メソッドを使用して、特定の請求可能モデルのトランザクションを取得できます。

たとえば、価格 pri_123pri_456 の特定のトランザクションを払い戻したいとします。 pri_123 は全額払い戻し、pri_456 は 2 ドルのみ払い戻したいとします。

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 の払い戻しに関するドキュメントを参照してください。

exclamation

払い戻しは、完全に処理される前に、常に Paddle によって承認される必要があります。

トランザクションのクレジット

払い戻しと同様に、トランザクションにクレジットを付与することもできます。トランザクションにクレジットを付与すると、顧客の残高に資金が追加され、将来の購入に使用できるようになります。トランザクションのクレジットは、手動で収集されたトランザクションでのみ実行でき、自動的に収集されたトランザクション(サブスクリプションなど)では実行できません。これは、Paddle がサブスクリプションクレジットを自動的に処理するためです。

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

詳細については、クレジットに関する Paddle のドキュメントを参照してください。

exclamation

クレジットは、手動で収集されたトランザクションにのみ適用できます。自動的に収集されたトランザクションは、Paddle 自体がクレジットを付与します。

トランザクション

transactions プロパティを介して、請求可能モデルのトランザクションの配列を簡単に取得できます。

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

トランザクションは、製品および購入の支払いを示し、請求書が添付されています。完了したトランザクションのみがアプリケーションのデータベースに保存されます。

顧客のトランザクションを一覧表示するときは、トランザクションインスタンスのメソッドを使用して、関連する支払い情報を表示できます。たとえば、ユーザーが請求書を簡単にダウンロードできるように、テーブル内のすべてのトランザクションをリスト表示したい場合があります。

<table>
@foreach ($transactions as $transaction)
<tr>
<td>{{ $transaction->billed_at->toFormattedDateString() }}</td>
<td>{{ $transaction->total() }}</td>
<td>{{ $transaction->tax() }}</td>
<td><a href="{{ route('download-invoice', $transaction->id) }}" target="_blank">Download</a></td>
</tr>
@endforeach
</table>

download-invoice ルートは、次のようになります。

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

過去および今後の支払い

lastPayment および nextPayment メソッドを使用して、定期的なサブスクリプションの顧客の過去または今後の支払いを取得して表示できます。

use App\Models\User;
 
$user = User::find(1);
 
$subscription = $user->subscription();
 
$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();

これらのメソッドはどちらも Laravel\Paddle\Payment のインスタンスを返します。ただし、トランザクションがウェブフックによってまだ同期されていない場合、lastPaymentnull を返し、請求サイクルが終了した場合(サブスクリプションがキャンセルされた場合など)、nextPaymentnull を返します。

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

テスト

テスト中は、請求フローを手動でテストして、統合が期待どおりに機能することを確認する必要があります。

CI 環境内で実行されるテストを含む自動テストの場合は、Laravel の HTTP クライアントを使用して、Paddle に行われた HTTP 呼び出しを偽装できます。これは Paddle からの実際に応答をテストするものではありませんが、Paddle の API を実際に呼び出さずにアプリケーションをテストする方法を提供します。