はじめに

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

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

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

Cashier のアップグレード

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

インストール

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

  1. composer require laravel/cashier-paddle

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

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

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

  1. php artisan migrate

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

Paddle サンドボックス

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

Paddle サンドボックス環境を使用する場合は、アプリケーションの PADDLE_SANDBOX 環境変数を true に設定する必要があります:

  1. PADDLE_SANDBOX=true

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

設定

請求可能モデル

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

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

ユーザーでない請求可能エンティティがある場合は、それらのクラスにもトレイトを追加できます:

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

API キー

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

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

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

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

Paddle JS

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

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

通貨設定

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

  1. CASHIER_CURRENCY_LOCALE=nl_BE

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

デフォルトモデルのオーバーライド

Cashier によって内部で使用されるモデルを拡張するために、自分のモデルを定義し、対応する Cashier モデルを拡張することができます:

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

モデルを定義した後、Laravel\Paddle\Cashier クラスを介して Cashier にカスタムモデルを使用するよう指示できます。通常、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドで Cashier にカスタムモデルについて通知する必要があります:

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

クイックスタート

製品の販売

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

アプリケーションを通じて製品およびサブスクリプション請求を提供することは、圧倒されることがあります。しかし、Cashier と Paddle のチェックアウトオーバーレイ のおかげで、現代的で堅牢な支払い統合を簡単に構築できます。

非定期的な単一請求製品に対して顧客に請求するために、Cashier を利用して顧客に Paddle のチェックアウトオーバーレイを通じて請求します。顧客は支払い情報を提供し、購入を確認します。支払いがチェックアウトオーバーレイを通じて行われると、顧客はアプリケーション内の選択した成功 URL にリダイレクトされます:

  1. use Illuminate\Http\Request;
  2. Route::get('/buy', function (Request $request) {
  3. $checkout = $request->user()->checkout('pri_deluxe_album')
  4. ->returnTo(route('dashboard'));
  5. return view('buy', ['checkout' => $checkout]);
  6. })->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 チェックアウトへのメタデータの提供

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

これを実現するために、checkout メソッドにカスタムデータの配列を提供できます。ユーザーがチェックアウトプロセスを開始すると、保留中の Order がアプリケーション内で作成されると想像してみてください。この例の Cart および Order モデルは説明用であり、Cashier によって提供されるものではありません。これらの概念は、アプリケーションのニーズに基づいて自由に実装できます:

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

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

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

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

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

この例では、CompleteOrder リスナーは次のようになります:

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

transaction.completed イベントに含まれるデータ に関する詳細は、Paddle のドキュメントを参照してください。

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

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

アプリケーションを通じて製品およびサブスクリプション請求を提供することは、圧倒されることがあります。しかし、Cashier と Paddle のチェックアウトオーバーレイ のおかげで、現代的で堅牢な支払い統合を簡単に構築できます。

Cashier と Paddle のチェックアウトオーバーレイを使用してサブスクリプションを販売する方法を学ぶために、基本的な月額 (price_basic_monthly) および年額 (price_basic_yearly) プランを持つサブスクリプションサービスのシンプルなシナリオを考えてみましょう。これらの 2 つの価格は、Paddle ダッシュボードの「基本」製品 (pro_basic) の下にグループ化できます。さらに、私たちのサブスクリプションサービスは、pro_expert としてエキスパートプランを提供するかもしれません。

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

  1. use Illuminate\Http\Request;
  2. Route::get('/subscribe', function (Request $request) {
  3. $checkout = $request->user()->checkout('price_basic_monthly')
  4. ->returnTo(route('dashboard'));
  5. return view('subscribe', ['checkout' => $checkout]);
  6. })->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 トレイトによって提供される 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. @if ($user->subscribedToPrice('price_basic_monthly'))
  5. <p>You are subscribed to our monthly Basic plan.</p>
  6. @endif

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

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

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

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

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

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

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

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

プランを交換するだけでなく、顧客がサブスクリプションをキャンセルできるようにする必要があります。プランを交換するのと同様に、以下のルートに導くボタンを提供します:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

次に、Paddle.js を使用してチェックアウトを初期化できます。この例では、paddle_button クラスが割り当てられたリンクを作成します。Paddle.js はこのクラスを検出し、リンクがクリックされるとオーバーレイチェックアウトを表示します:

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

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

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

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

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

次に、チェックアウトセッションをコンポーネントの 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 コンポーネントを使用せずに、インラインチェックアウトを手動でレンダリングすることもできます。始めるには、前の例で示したように チェックアウトセッションを生成します:

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

次に、Paddle.js を使用してチェックアウトを初期化できます。この例では、Alpine.js を使用してこれを示しますが、独自のフロントエンドスタックに合わせてこの例を変更することもできます:

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

ゲストチェックアウト

時には、アカウントを必要としないユーザーのためにチェックアウトセッションを作成する必要があるかもしれません。そのためには、guest メソッドを使用できます:

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

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

価格プレビュー

Paddle は通貨ごとに価格をカスタマイズできるため、異なる国に対して異なる価格を設定できます。Cashier Paddle を使用すると、previewPrices メソッドを使用してこれらのすべての価格を取得できます。このメソッドは、価格を取得したい価格 ID を受け入れます:

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

通貨はリクエストの IP アドレスに基づいて決定されますが、特定の国を提供して価格を取得することもできます:

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

価格を取得した後、任意の方法で表示できます:

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

顧客価格プレビュー

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

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

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

割引

割引後の価格を表示することもできます。previewPrices メソッドを呼び出すときに、discount_id オプションを介して割引 ID を提供します:

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

次に、計算された価格を表示します:

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

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

顧客の取得

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

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

顧客の作成

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

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

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

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

サブスクリプション

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

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

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

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

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

  1. $checkout = $request->user()->subscribe($premium = 12345, '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 を返します。たとえそのサブスクリプションが現在トライアル期間内であっても:

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

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

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

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

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

ユーザーがまだトライアル期間内にいるかどうかを判断するには、onTrial メソッドを使用できます。このメソッドは、ユーザーにトライアル期間中であることを警告するメッセージを表示する必要があるかどうかを判断するのに役立ちます:

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

subscribedToPrice メソッドは、ユーザーが特定のプランにサブスクライブしているかどうかを、特定の Paddle 価格 ID に基づいて判断するために使用できます。この例では、ユーザーの default サブスクリプションが月額価格にアクティブにサブスクライブされているかどうかを判断します:

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

recurring メソッドは、ユーザーが現在アクティブなサブスクリプションを持っており、トライアル期間や猶予期間を過ぎているかどうかを判断するために使用できます:

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

キャンセルされたサブスクリプションステータス

ユーザーがかつてアクティブなサブスクライバーであったが、サブスクリプションをキャンセルしたかどうかを判断するには、canceled メソッドを使用できます:

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

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

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

過去の支払い状況

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

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

サブスクリプションが過去の支払い状況にある場合、ユーザーに 支払い情報を更新する よう指示する必要があります。

サブスクリプションが past_due の場合でも有効と見なしたい場合は、Cashier によって提供される keepPastDueSubscriptionsActive メソッドを使用できます。通常、このメソッドは register メソッドの中で呼び出されるべきです:

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

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

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

ほとんどのサブスクリプション状態は、特定の状態にあるサブスクリプションをデータベースで簡単にクエリできるように、クエリスコープとしても利用可能です:

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

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

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

サブスクリプション単一請求

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

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

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

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

支払い情報の更新

Paddle は常にサブスクリプションごとに支払い方法を保存します。サブスクリプションのデフォルト支払い方法を更新したい場合は、サブスクリプションモデルの redirectToUpdatePaymentMethod メソッドを使用して、顧客を Paddle のホストされた支払い方法更新ページにリダイレクトする必要があります:

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

ユーザーが情報の更新を完了すると、Paddle から subscription_updated Webhook が送信され、サブスクリプションの詳細がアプリケーションのデータベースに更新されます。

プランの変更

ユーザーがアプリケーションにサブスクライブした後、時折新しいサブスクリプションプランに変更したいと思うかもしれません。ユーザーのサブスクリプションプランを更新するには、Paddle 価格の識別子をサブスクリプションの swap メソッドに渡す必要があります:

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

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

  1. $user = User::find(1);
  2. $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ドルを請求するかもしれません。サブスクリプションの数量を簡単に増減させるには、incrementQuantityおよびdecrementQuantityメソッドを使用します:

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

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

  1. $user->subscription()->updateQuantity(10);
  1. ``````php
  2. $user->subscription()->noProrate()->updateQuantity(10);
  3. `

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

サブスクリプションが複数製品を持つサブスクリプションの場合、増減させたい数量の価格のIDを増減メソッドの第2引数として渡す必要があります:

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

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

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

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

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

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

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

既存のサブスクリプションに別の価格を追加したい場合は、サブスクリプションのswapメソッドを使用する必要があります。swapメソッドを呼び出す際には、サブスクリプションの現在の価格と数量も含める必要があります:

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

上記の例では新しい価格が追加されますが、顧客は次の請求サイクルまでその価格に対して請求されません。顧客にすぐに請求したい場合は、swapAndInvoiceメソッドを使用できます:

  1. $user->subscription()->swapAndInvoice(['price_chat', 'price_original' => 2]);
  1. ``````php
  2. $user->subscription()->swap(['price_original' => 2]);
  3. `

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

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

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

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

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

この例では、顧客のために月額のプールサブスクリプションを開始しました。しかし、後で年額サブスクリプションに切り替えたいかもしれません。顧客のサブスクリプションを調整する際、swimmingサブスクリプションの価格を単純に切り替えることができます:

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

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

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

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

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

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

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

デフォルトでは、一時停止は次の請求間隔で発生するため、顧客は支払った期間の残りを使用できます。サブスクリプションをすぐに一時停止したい場合は、pauseNowメソッドを使用できます:

  1. $user->subscription()->pauseNow();
  1. ``````php
  2. $user->subscription()->pauseUntil(now()->addMonth());
  3. `

または、pauseNowUntilメソッドを使用して、特定の時点までサブスクリプションを即座に一時停止できます:

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

ユーザーがサブスクリプションを一時停止しているが、まだ「猶予期間」にあるかどうかをonPausedGracePeriodメソッドを使用して確認できます:

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

一時停止されたサブスクリプションを再開するには、サブスクリプションに対してresumeメソッドを呼び出します:

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

サブスクリプションが一時停止されている間は、変更することはできません。異なるプランに切り替えたり、数量を更新したりするには、まずサブスクリプションを再開する必要があります。

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

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

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

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

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

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

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

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

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

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

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

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

前払いの支払い方法あり

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

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

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

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

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

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

既存のトライアルが期限切れかどうかを確認するには、hasExpiredTrialメソッドを使用できます:

  1. if ($user->hasExpiredTrial()) {
  2. // ...
  3. }
  4. if ($user->subscription()->hasExpiredTrial()) {
  5. // ...
  6. }

特定のサブスクリプションタイプのトライアルにいるかどうかを確認するには、onTrialまたはhasExpiredTrialメソッドにタイプを提供できます:

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

前払いの支払い方法なし

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

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

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

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

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

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

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

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

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

  1. if ($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 Webhookの処理

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ファイルでpaddle/*をCSRF保護から除外する必要があります:

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

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と一致する必要があります:

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

Webhook署名の検証

Webhookを保護するために、PaddleのWebhook署名を使用できます。便利なことに、Cashierは受信したPaddle Webhookリクエストが有効であることを検証するミドルウェアを自動的に含めています。

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

単一の請求

製品の請求

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

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

チェックアウトセッションを生成した後、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の購入を返金する必要がある場合は、refundメソッドをCashier\Paddle\Transactionモデルに対して使用できます。このメソッドは、理由を最初の引数として受け入れ、返金する価格IDを1つ以上、オプションの金額を連想配列として受け入れます。transactionsメソッドを使用して、特定の請求モデルの取引を取得できます。

たとえば、価格pri_123pri_456の特定の取引を返金したいと想像してください。pri_123を完全に返金したいが、pri_456については2ドルだけ返金したいとします:

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

上記の例は、取引の特定の行項目を返金します。取引全体を返金したい場合は、理由を提供するだけです:

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

返金に関する詳細は、Paddleの返金ドキュメントを参照してください。

返金は常にPaddleによって完全に処理される前に承認される必要があります。

取引のクレジット

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

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

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

クレジットは手動で収集された取引にのみ適用できます。自動的に収集された取引は、Paddle自身によってクレジットされます。

取引

請求モデルの取引の配列をtransactionsプロパティを介して簡単に取得できます:

  1. use App\Models\User;
  2. $user = User::find(1);
  3. $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>
  1. ``````php
  2. use Illuminate\Http\Request;
  3. use Laravel\Paddle\Transaction;
  4. Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) {
  5. return $transaction->redirectToInvoicePdf();
  6. })->name('download-invoice');
  7. `

過去および今後の支払い

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

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

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

テスト

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

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