Skip to content

Laravel Cashier (Paddle)

はじめに

Warning

このドキュメントは、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パッケージをインストールします:

composer require laravel/cashier-paddle

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

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

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

php artisan migrate

Warning

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

Paddle Sandbox

ローカルおよびステージング開発中に、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コントロールパネルから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はオプションであり、RetainでPaddleを使用している場合にのみ設定する必要があります。

Paddle JS

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

<head>
    ...

    @paddleJS
</head>

通貨設定

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

CASHIER_CURRENCY_LOCALE=nl_BE

Warning

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;

/**
 * 任意のアプリケーションサービスのブートストラップ
 */
public function boot(): void
{
    Cashier::useSubscriptionModel(Subscription::class);
    Cashier::useTransactionModel(Transaction::class);
}

クイックスタート

商品の販売

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メソッドを利用して、指定された「価格識別子」に対して、顧客にPaddleのチェックアウトオーバーレイを提示するためのチェックアウトオブジェクトを作成します。Paddleを使用する場合、「価格」とは、特定の製品に対して定義された価格を指します。

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

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

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    製品を購入
</x-paddle-button>

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

製品を販売する際、完了した注文や購入した製品を、アプリケーションで定義されたCartOrderモデルを通じて追跡するのが一般的です。顧客を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メソッドに提供します。もちろん、これらのアイテムを顧客が追加する際に「ショッピングカート」または注文に関連付けるのは、アプリケーションの責任です。また、注文のIDをcustomDataメソッドを通じてPaddleチェックアウトオーバーレイに提供します。

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

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

use App\Listeners\CompleteOrder;
use Illuminate\Support\Facades\Event;
use Laravel\Paddle\Events\TransactionCompleted;

/**
 * アプリケーションサービスをブートストラップする。
 */
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
{
    /**
     * 着信Cashier Webhookイベントを処理します。
     */
    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のドキュメントを参照してください。

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

Note

Paddleチェックアウトを利用する前に、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">
    サブスクライブ
</x-paddle-button>

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

これで顧客はサブスクリプションを開始できるようになりましたが、アプリケーションの一部をサブスクライブしたユーザーのみがアクセスできるように制限する必要があります。もちろん、CashierのBillableトレイトによって提供されるsubscribedメソッドを介して、ユーザーの現在のサブスクリプションステータスを常に確認できます。

@if ($user->subscribed())
    <p>サブスクライブしています。</p>
@endif

特定の製品や価格にサブスクライブしているかどうかを簡単に確認することもできます。

@if ($user->subscribedToProduct('pro_basic'))
    <p>Basic製品にサブスクライブしています。</p>
@endif

@if ($user->subscribedToPrice('price_basic_monthly'))
    <p>月額Basicプランにサブスクライブしています。</p>
@endif

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

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

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class Subscribed
{
    /**
     * 着信リクエストを処理する。
     */
    public function handle(Request $request, Closure $next): Response
    {
        if (! $request->user()?->subscribed()) {
            // ユーザーを請求ページにリダイレクトしてサブスクライブを促す...
            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); // この例では"$price"は"price_basic_yearly"です。

    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を受信し、アプリケーションのデータベースでそのサブスクリプションを「キャンセル済み」とマークします。

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

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

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

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

Checkout Overlayウィジェットを表示する前に、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-button Bladeコンポーネントが含まれています。このコンポーネントにチェックアウトセッションを「prop」として渡すことができます。そして、このボタンがクリックされると、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を設定することが重要です。

Warning

サブスクリプションの状態が変更された後、対応する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を使用すると、チェックアウトセッションを作成する際に、顧客に対していくつかの便利なデフォルトを定義できます。これらのデフォルトを設定することで、顧客のメールアドレスと名前を事前に入力し、チェックアウトウィジェットの支払い部分にすぐに進むことができます。これらのデフォルトは、請求可能なモデルで以下のメソッドをオーバーライドすることで設定できます。

/**
 * Paddleに関連付ける顧客の名前を取得します。
 */
public function paddleName(): string|null
{
    return $this->name;
}

/**
 * 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ウェブフックがディスパッチされます。Cashierはこのウェブフックを受信し、顧客のサブスクリプションを設定します。アプリケーションがすべてのウェブフックを適切に受信および処理するように、ウェブフックの処理を正しく設定していることを確認してください。

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

ユーザーがアプリケーションにサブスクライブした後、さまざまな便利なメソッドを使用してサブスクリプションステータスを確認できます。まず、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
{
    /**
     * 受信リクエストを処理します。
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->user() && ! $request->user()->subscribed()) {
            // このユーザーは支払い済みの顧客ではありません...
            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状態の場合、ユーザーに支払い情報の更新を指示する必要があります。

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

use Laravel\Paddle\Cashier;

/**
 * 任意のアプリケーションサービスを登録します。
 */
public function register(): void
{
    Cashier::keepPastDueSubscriptionsActive();
}

Warning

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

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

ほとんどのサブスクリプションステータスは、クエリスコープとしても利用できるため、指定された状態のサブスクリプションを簡単にクエリできます。

// すべての有効なサブスクリプションを取得します...
$subscriptions = Subscription::query()->valid()->get();

// ユーザーのキャンセルされたサブスクリプションをすべて取得します...
$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();

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

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

// 単一の価格を請求します...
$response = $user->subscription()->charge('pri_123');
// 一度に複数の価格を請求する...
$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();
});

ユーザーが情報の更新を完了すると、subscription_updatedウェブフックがPaddleによってディスパッチされ、サブスクリプションの詳細がアプリケーションのデータベースで更新されます。

プランの変更

ユーザーがアプリケーションにサブスクライブした後、新しいサブスクリプションプランに変更したい場合があります。ユーザーのサブスクリプションプランを更新するには、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');

日割り計算

デフォルトでは、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の日割り計算ドキュメントを参照してください。

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

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

$user = User::find(1);

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

// サブスクリプションの現在の数量に5を追加...
$user->subscription()->incrementQuantity(5);

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

// サブスクリプションの現在の数量から5を引く...
$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');

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

複数の製品を持つサブスクリプションを使用すると、単一のサブスクリプションに複数の請求製品を割り当てることができます。例えば、顧客サービスの「ヘルプデスク」アプリケーションがあり、月額$10の基本サブスクリプションがあり、ライブチャットアドオン製品が追加で月額$15で提供されているとします。

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

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

Warning

サブスクリプションの最後の価格を削除することはできません。代わりに、サブスクリプションを単にキャンセルしてください。

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

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();

Warning

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

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

サブスクリプションをキャンセルするには、ユーザーのサブスクリプションの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();

Warning

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に指示します。

Warning

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

ユーザーがトライアル期間内にいるかどうかは、ユーザーインスタンスの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()) {
    // ユーザーはトライアル期間内です。
}

ユーザーのために実際のサブスクリプションを作成する準備ができたら、通常通り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()) {
    // ユーザーは「ジェネリック」トライアル期間内です。
}

トライアルの延長またはアクティベーション

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

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

または、トライアルを終了してサブスクリプションを即座にアクティベートするには、サブスクリプションのactivateメソッドを呼び出します:

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

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署名検証ミドルウェアで保護するようにしてください。

WebhooksとCSRF保護

Paddle WebhooksはLaravelのCSRF保護をバイパスする必要があるため、アプリケーションのbootstrap/app.phpファイルでpaddle/*をCSRF保護から除外するようにしてください:

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

Webhooksとローカル開発

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

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

Cashierは、失敗した請求によるサブスクリプションのキャンセルやその他の一般的なPaddle Webhooksを自動的に処理します。ただし、追加のWebhookイベントを処理したい場合は、Cashierが発行する以下のイベントをリッスンすることができます:

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

両方のイベントには、Paddle Webhookの完全なペイロードが含まれています。例えば、transaction.billed Webhookを処理したい場合は、イベントを処理するリスナーを登録できます:

<?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からの完全なペイロードに加えて、請求可能なモデル、サブスクリプション、またはレシートなど、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

デフォルトの組み込みWebhookルートをオーバーライドするには、アプリケーションの.envファイルでCASHIER_WEBHOOK環境変数を定義します。この値は、Webhookルートへの完全なURLである必要があり、Paddleコントロールパネルで設定されたURLと一致する必要があります:

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

Webhook署名の検証

Webhookを安全にするために、PaddleのWebhook署名検証を使用できます。便利なことに、CashierにはPaddleのWebhookリクエストを検証するミドルウェアが含まれています。

すべての受信WebhookルートにLaravel\Paddle\Http\Middleware\VerifyWebhookSignatureミドルウェアを追加するだけで、Cashierはリクエストの署名を自動的に検証します:

use Laravel\Paddle\Http\Middleware\VerifyWebhookSignature;

Route::post('/paddle/webhook', function (Request $request) {
    // ...
})->middleware(VerifyWebhookSignature::class);

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

Webhookの検証を有効にするには、アプリケーションの.envファイルでPADDLE_WEBHOOK_SECRET環境変数が定義されていることを確認してください。Webhookシークレットは、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', // この価格を全額返金...
    'pri_456' => 200, // この価格を部分的に返金...
]);

上記の例は、トランザクション内の特定の明細項目を返金します。トランザクション全体を返金したい場合は、単に理由を指定します:

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

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

Warning

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

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

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

$transaction = $user->transactions()->first();

// 特定の明細項目を全額クレジット...
$response = $transaction->credit('Compensation', 'pri_123');

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

Warning

クレジットは、手動で収集されたトランザクションに対してのみ適用できます。自動的に収集されたトランザクションは、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のインスタンスを返します。ただし、lastPaymentはトランザクションがまだWebhookによって同期されていない場合にnullを返し、nextPaymentは請求サイクルが終了した場合(サブスクリプションがキャンセルされた場合など)にnullを返します:

次の支払い: {{ $nextPayment->amount() }} 支払期限: {{ $nextPayment->date()->format('d/m/Y') }}

テスト

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

自動テスト(CI環境内で実行されるものを含む)の場合、LaravelのHTTPクライアントを使用して、PaddleへのHTTPリクエストをモックすることができます。これにより、実際にPaddleのAPIを呼び出すことなくアプリケーションをテストする方法が提供されますが、Paddleからの実際のレスポンスはテストされません。

ユーザーノート