サイトを更新しています。 数日間、レイアウトや翻訳に不具合が出ることがあります。ドキュメントは引き続きご利用いただけます。表示が崩れている場合は、後ほど再読み込みしてください。

ホーム Laravel 10.x Laravel Cashier (Paddle)

Laravel Cashier (Paddle)

10.x 2026年3月7日

#はじめに

Внимание

このドキュメントは Cashier Paddle 2.x の Paddle Billing 連携用です。まだ Paddle Classic を使っている場合は、Cashier Paddle 1.x を使用してください。

Laravel Cashier Paddle は、Paddle のサブスクリプション課金サービスに対して表現力豊かで直感的なインターフェースを提供します。面倒なサブスクリプション課金のボイラープレートコードのほとんどを処理します。基本的なサブスクリプション管理に加え、サブスクリプションの切り替え、数量管理、一時停止、キャンセル猶予期間なども扱えます。

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
Внимание

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

#Paddle サンドボックス

ローカルやステージング環境で開発する際は、Paddle サンドボックスアカウントを登録してください。このアカウントは実際の支払いを行わずにテストや開発ができるサンドボックス環境を提供します。Paddle のテストカード番号を使って様々な支払いシナリオをシミュレートできます。

Paddle サンドボックス環境を使う場合は、アプリケーションの .env ファイル内で PADDLE_SANDBOX 環境変数を true に設定してください:

PADDLE_SANDBOX=true

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

#設定

#Billable モデル

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_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のサンドボックス環境 を使用している場合に true に設定してください。アプリケーションを本番環境にデプロイし、Paddle のライブベンダー環境を使用している場合は、PADDLE_SANDBOX 変数を false に設定してください。

PADDLE_RETAIN_KEY は任意で、Paddle の Retain を使う場合のみ設定してください。

#Paddle JS

Paddle は独自の JavaScript ライブラリを使ってチェックアウトウィジェットを起動します。アプリケーションのレイアウトの閉じる </head> タグ直前に @paddleJS Blade ディレクティブを配置して JavaScript ライブラリを読み込めます:

<head>
    ...

    @paddleJS
</head>

#通貨設定

請求書の表示用に通貨のロケールを指定できます。内部的に Cashier は PHP の NumberFormatter クラスを使って通貨ロケールを設定します:

CASHIER_CURRENCY_LOCALE=nl_BE
Внимание

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

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

Cashier が内部で使うモデルは自由に拡張できます。独自モデルを定義し、対応する Cashier モデルを継承してください:

use Laravel\Paddle\Subscription as CashierSubscription;

class Subscription extends CashierSubscription
{
    // ...
}

モデルを定義したら、Laravel\Paddle\Cashier クラスを使って Cashier にカスタムモデルを使うよう指示できます。通常はアプリケーションの App\Providers\AppServiceProvider クラスの boot メソッド内で設定します:

use App\Models\Cashier\Subscription;
use App\Models\Cashier\Transaction;

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

#クイックスタート

#商品販売

Примечание

Paddle チェックアウトを利用する前に、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 チェックアウトにメタデータを渡す

商品販売時は、完了した注文や購入商品を自分のアプリケーションで定義した 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 イベントをリッスンしてください。通常、このイベントリスナーはアプリケーションのサービスプロバイダーの boot メソッド内で登録します。

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\Cashier\Cashier;
use Laravel\Cashier\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']);
    }
}

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

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

Примечание

Paddle Checkout を利用する前に、Paddle ダッシュボードで固定価格のプロダクトを定義してください。また、Paddle の webhook 処理の設定も行う必要があります。

アプリケーションで商品やサブスクリプションの課金を提供するのは難しく感じるかもしれません。しかし、Cashier と Paddle の Checkout Overlay を使えば、モダンで堅牢な決済連携を簡単に構築できます。

Cashier と Paddle の Checkout Overlay を使ってサブスクリプションを販売する方法を学ぶために、基本的な月額プラン(price_basic_monthly)と年額プラン(price_basic_yearly)を持つサブスクリプションサービスのシンプルな例を考えます。これらの価格は Paddle ダッシュボードの「Basic」プロダクト(pro_basic)にまとめられます。さらに、サブスクリプションサービスは Expert プランとして pro_expert を提供するかもしれません。

まず、顧客がどのようにサービスにサブスクライブできるかを見てみましょう。例えば、顧客がアプリケーションの料金ページで Basic プランの「subscribe」ボタンをクリックすると、そのプランの Paddle Checkout Overlay が起動します。始めるには、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 ビューでは、Checkout Overlay を表示するボタンを含めます。paddle-button Blade コンポーネントは Cashier Paddle に含まれていますが、手動でオーバーレイチェックアウトをレンダリングすることもできます

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

Subscribe ボタンがクリックされると、顧客は支払い情報を入力してサブスクリプションを開始できます。支払い方法によっては処理に数秒かかるため、サブスクリプションが実際に開始されたことを知るには、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

#Subscribed ミドルウェアの作成

利便性のために、リクエストがサブスクライブ済みユーザーからのものか判定する ミドルウェア を作成するとよいでしょう。このミドルウェアを定義すれば、サブスクライブしていないユーザーのルートアクセスを簡単に制限できます。

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

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

Примечание

Cashier の webhook 処理を設定していれば、Paddle からの webhook を監視して Cashier 関連のデータベーステーブルを自動的に同期します。例えば、Paddle ダッシュボードで顧客のサブスクリプションをキャンセルすると、対応する webhook が届き、アプリケーションのデータベースでサブスクリプションが「cancelled」とマークされます。

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

顧客への課金は、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 の設定 を正しく行うことが重要です。

Внимание

サブスクリプション状態が変わった後、対応する 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 の「オーバーレイ」スタイルのチェックアウトウィジェットを使いたくない場合、ウィジェットをインライン表示するオプションもあります。この方法ではチェックアウトの 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" />

インラインチェックアウトコンポーネントの高さを調整したい場合は、Blade コンポーネントに height 属性を渡せます。

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

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

#手動でインラインチェックアウトをレンダリングする

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::productPrices(['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_title }} - {{ $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のすべてのチェックアウトセッション生成時に使用されます。

#顧客の取得

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

use Laravel\Cashier\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番目の引数は、サブスクリプションのアプリ内での「type」を指定します。アプリでサブスクリプションが1種類しかない場合は、defaultprimary と名付けることができます。このサブスクリプションタイプはアプリケーション内部でのみ使用するもので、ユーザーに表示するものではありません。さらに、スペースを含めてはいけませんし、サブスクリプション作成後に変更してはいけません。

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 状態でもサブスクリプションを有効とみなしたい場合は、Cashierの keepPastDueSubscriptionsActive メソッドを使えます。通常、このメソッドは AppServiceProviderregister メソッド内で呼び出します:

use Laravel\Paddle\Cashier;

/**
 * アプリケーションサービスを登録します。
 */
public function register(): void
{
    Cashier::keepPastDueSubscriptionsActive();
}
Внимание

サブスクリプションが past_due 状態のときは、支払い情報が更新されるまで変更できません。そのため、swapupdateQuantity メソッドは 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();
});

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

#プランの変更

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

日割り計算を無効にしてすぐに請求したい場合は、noProrateswapAndInvoice を組み合わせて使えます。

$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を第2引数に渡してください。

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

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

複数商品のサブスクリプションでは、1つのサブスクリプションに複数の請求商品を割り当てられます。例えば、月額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]);
Внимание

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

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

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();
Внимание

サブスクリプションは一時停止中に変更できません。プランの変更や数量の更新を行う場合は、まず再開してください。

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

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

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 に対してその日時まで請求を開始しないよう指示します。

Внимание

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

ユーザーがトライアル期間中かどうかは、ユーザーインスタンスの 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 はこのタイプのトライアルを「generic trial(汎用トライアル)」と呼びます。既存のサブスクリプションに紐づいていないためです。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();
}

ユーザーが「generic trial(汎用トライアル)」期間中で、まだ実際のサブスクリプションを作成していないことを特に知りたい場合は、onGenericTrial メソッドを使用できます。

if ($user->onGenericTrial()) {
    // ユーザーは「generic trial(汎用トライアル)」期間中です...
}

#トライアルの延長または有効化

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

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

または、activate メソッドを呼び出してトライアルを終了し、サブスクリプションを即座に有効化できます。

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

#Paddle のウェブフックの処理

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

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

Paddle ウェブフックを正しく処理するには、Paddle コントロールパネルでウェブフック URL を設定してください。デフォルトでは、Cashier のウェブフックコントローラーは /paddle/webhook パスに応答します。Paddle コントロールパネルで有効にすべきウェブフックの一覧は以下の通りです。

  • 顧客が更新されました
  • 取引が完了しました
  • 取引が更新されました
  • サブスクリプションが作成されました
  • サブスクリプションが更新されました
  • サブスクリプションが一時停止されました
  • サブスクリプションがキャンセルされました
Внимание

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

#ウェブフックと CSRF 保護

Paddle の webhook は Laravel の CSRF保護 をバイパスする必要があるため、URI を App\Http\Middleware\VerifyCsrfToken ミドルウェアの例外として必ず登録するか、web ミドルウェアグループの外でルートを定義してください:

protected $except = [
    'paddle/*',
];

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

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

#ウェブフックイベントハンドラーの定義

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
{
    /**
     * 受信した Paddle ウェブフックを処理します。
     */
    public function handle(WebhookReceived $event): void
    {
        if ($event->payload['alert_name'] === 'transaction_billed') {
            // 受信イベントを処理します...
        }
    }
}

リスナーを定義したら、アプリケーションの EventServiceProvider に登録します。

<?php

namespace App\Providers;

use App\Listeners\PaddleEventListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Laravel\Paddle\Events\WebhookReceived;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        WebhookReceived::class => [
            PaddleEventListener::class,
        ],
    ];
}

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

#ウェブフック署名の検証

ウェブフックのセキュリティを確保するために、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 メソッドを使います。このメソッドは最初の引数に理由を受け取り、返金する価格 ID を1つ以上、必要に応じて金額を連想配列で指定できます。請求対象モデルのトランザクションは 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, // この価格は一部返金(200セント)...
]);

上記の例はトランザクション内の特定の明細を返金しています。トランザクション全体を返金したい場合は、理由だけを指定してください。

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

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

Внимание

返金は必ず Paddle の承認を得てから処理されます。

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

返金と同様に、トランザクションにクレジットを付与できます。クレジットは顧客の残高に加算され、将来の購入に使えます。クレジットは手動で集金したトランザクションにのみ適用可能で、自動集金(サブスクリプションなど)には適用できません。サブスクリプションのクレジットは Paddle が自動で処理します。

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

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

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

Внимание

クレジットは手動集金トランザクションにのみ適用可能です。自動集金トランザクションは 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\Cashier\Transaction;

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

#過去および今後の支払い

lastPaymentnextPayment メソッドを使って、顧客の過去および今後の定期サブスクリプションの支払いを取得・表示できます。

use App\Models\User;

$user = User::find(1);

$subscription = $user->subscription();

$lastPayment = $subscription->lastPayment();
$nextPayment = $subscription->nextPayment();

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

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

#テスト

テスト中は、請求フローが期待通りに動作するか手動で確認してください。

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