Laravel5.5 LTS 日本語ドキュメント

このリポジトリはPHP Webアプリフレームワークである、LaravelのLTSバージョンである5.5の公式英文ドキュメントを日本語へ翻訳しています。

This project is maintained by okinaka

Laravel Cashier

イントロダクション

Laravel CashierはStripeBraintreeによるサブスクリプション(定期課金)サービスの読みやすく、スラスラと記述できるインターフェイスを提供します。これにより書くのが恐ろしくなるような、サブスクリプション支払いのための決まりきったコードのほとんどが処理できます。基本的なサブスクリプション管理に加え、Cashierはクーポン、サブスクリプションの変更、サブスクリプション数、キャンセル猶予期間、さらにインボイスのPDF発行まで行います。

{note} サブスクリプションを提供せず、「一回だけ」の支払いを取り扱う場合は、Cashierを使用してはいけません。StripeかBraintreeのSDKを直接使用してください。

設定

Stripe

Composer

最初に、Stripe向けのChashierパッケージを依存パッケージへ追加します。

composer require "laravel/cashier":"~7.0"

データベースマイグレーション

Cashierを使用する前に、データベースを準備する必要があります。usersテーブルに、いくつかのカラムを追加し、顧客のサブスクリプション情報すべてを保持する新しいsubscriptionsテーブルを作成します。

Schema::table('users', function ($table) {
    $table->string('stripe_id')->nullable();
    $table->string('card_brand')->nullable();
    $table->string('card_last_four')->nullable();
    $table->timestamp('trial_ends_at')->nullable();
});

Schema::create('subscriptions', function ($table) {
    $table->increments('id');
    $table->integer('user_id');
    $table->string('name');
    $table->string('stripe_id');
    $table->string('stripe_plan');
    $table->integer('quantity');
    $table->timestamp('trial_ends_at')->nullable();
    $table->timestamp('ends_at')->nullable();
    $table->timestamps();
});

マイグレーションを作成したら、migrate Artisanコマンドを実行します。

Billableモデル

次に、モデル定義にBillableトレイトを追加します。このトレイトは、サブスクリプションの作成、クーポンの適用、クレジットカード情報の更新など、一般的な支払いタスクを実行する様々なメソッドを提供しています。

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

APIキー

最後に、Stripeキーをservices.php設定ファイルへ設定します。Stripe APIキーはStripeのコントロールパネルから取得します。

'stripe' => [
    'model'  => App\User::class,
    'key' => env('STRIPE_KEY'),
    'secret' => env('STRIPE_SECRET'),
],

Braintree

Braintreeの注意

多くの操作において、StripeとBraintreeで実装しているCashierの機能は同じものです。両方のサービスはクレジットカードでの支払いを提供していますが、BraintreeはPayPalでの支払いもサポートしています。しかしながら、Braintreeには、Stripeが提供してる機能のいくつかが欠けています。以下の点を考慮し、StripeとBraintreeのどちらを使うのか決めてください。

  • BraintreeはPayPalをサポートしていますが、Stripeはしていません。
  • Braintreeはサブスクリプション数のincrement(追加)とdecrement(減少)メソッドをサポートしていません。これはCashierではなく、Braintreeの制限です。
  • Braintreeはパーセンテージをもとにしたディスカウントはサポートしていません。これはCashierではなく、Braintreeの制限です。

Composer

最初に、Braintree向けのCashierパッケージを追加します。

composer require "laravel/cashier-braintree":"~2.0"

サービスプロバイダ

次にconfig/app.php設定ファイルへ、Laravel\Cashier\CashierServiceProviderサービスプロバイダを登録します。

Laravel\Cashier\CashierServiceProvider::class

クレジットクーポンのプラン

CashierをBraintreeで使用する前に、plan-creditディスカウントをBraintreeのコントロールパネルで定義する必要があります。このディスカウントは、年払いから月払い、もしくは月払いから年払いの変更時に代金を確実に按分するために使用されます。

ディスカウント値はBraintreeコントロールパネルで好きな値に設定できます。Cashierはクーポンを毎回適用するごとに、自身の定義済みカスタム値でディスカウント値をオーバーライドします。Braintreeは繰り返されるサブスクリプションに渡る、按分をネイティブにサポートしていないため、このクーポンが必要です。

データベースマイグレーション

Cashierを使用する前に、データベースも準備する必要があります。usersテーブルにカラムをいくつか追加し、顧客のサブスクリプション情報を保存するために新しいsubscriptionsテーブルを作成します。

Schema::table('users', function ($table) {
    $table->string('braintree_id')->nullable();
    $table->string('paypal_email')->nullable();
    $table->string('card_brand')->nullable();
    $table->string('card_last_four')->nullable();
    $table->timestamp('trial_ends_at')->nullable();
});

Schema::create('subscriptions', function ($table) {
    $table->increments('id');
    $table->integer('user_id');
    $table->string('name');
    $table->string('braintree_id');
    $table->string('braintree_plan');
    $table->integer('quantity');
    $table->timestamp('trial_ends_at')->nullable();
    $table->timestamp('ends_at')->nullable();
    $table->timestamps();
});

マイグレーションを作成したら、migrate Artisanコマンドを実行するだけです。

Billableモデル

次にBillableトレイをモデルに追加してください。

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

APIキー

次に、以下のオプションをservices.phpファイルで設定します。

'braintree' => [
    'model'  => App\User::class,
    'environment' => env('BRAINTREE_ENV'),
    'merchant_id' => env('BRAINTREE_MERCHANT_ID'),
    'public_key' => env('BRAINTREE_PUBLIC_KEY'),
    'private_key' => env('BRAINTREE_PRIVATE_KEY'),
],

続いて以下のBraintree SDK呼び出しコードをAppServiceProviderサービスプロバイダのbootメソッドに追加します。

\Braintree_Configuration::environment(config('services.braintree.environment'));
\Braintree_Configuration::merchantId(config('services.braintree.merchant_id'));
\Braintree_Configuration::publicKey(config('services.braintree.public_key'));
\Braintree_Configuration::privateKey(config('services.braintree.private_key'));

通貨設定

Cashierのデフォルト通貨は米ドル(USD)です。サービスプロバイダの一つで、bootメソッド中でCashier::useCurrencyメソッドを呼び出し、デフォルト通貨を変更可能です。useCurrencyメソッドは2つの文字列を引数に取ります。通貨と通貨記号です。

use Laravel\Cashier\Cashier;

Cashier::useCurrency('eur', '€');

サブスクリプション

サブスクリプション作成

サブスクリプションを作成するには最初にbillableなモデルのインスタンスを取得しますが、通常はApp\Userのインスタンスでしょう。モデルインスタンスが獲得できたら、モデルのサブスクリプションを作成するために、newSubscriptionメソッドを使います。

$user = User::find(1);

$user->newSubscription('main', 'premium')->create($stripeToken);

newSubscriptionメソッドの最初の引数は、サブスクリプションの名前です。アプリケーションでサブスクリプションを一つしか取り扱わない場合、mainprimaryと名づけましょう。2つ目の引数には、ユーザーが購入したStripe/Braintreeのプランを指定します。この値はStripe/Braintreeのプランの識別子です。

createメソッドはStripeクレジットカード/ソーストークンを引数にとり、サブスクリプションを開始すると同時に、カスタマーIDと関連する支払い情報をデータベースに保存します。

ユーザー詳細情報の指定

ユーザーに関する詳細情報を追加したい場合は、createメソッドの第2引数に渡すことができます。

$user->newSubscription('main', 'monthly')->create($stripeToken, [
    'email' => $email,
]);

Stripe/Braintreeがサポートしている追加のフィールドについての更なる情報は、Stripeの顧客の作成ドキュメントか、Braintreeのドキュメントを確認してください。

クーポン

サブスクリプションの作成時に、クーポンを適用したい場合は、withCouponメソッドを使用してください。

$user->newSubscription('main', 'monthly')
     ->withCoupon('code')
     ->create($stripeToken);

サブスクリプション状態の確認

ユーザーがアプリケーションで何かを購入したら、バラエティー豊かで便利なメソッドでサブスクリプション状況を簡単にチェックできます。まず初めにsubscribedメソッドがtrueを返したら、サブスクリプションが現在試用期間であるにしても、そのユーザーはアクティブなサブスクリプションを持っています。

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

subscribedメソッドはルートミドルウェアで使用しても大変役に立つでしょう。ユーザーのサブスクリプション状況に基づいてルートやコントローラへのアクセスをフィルタリングできます。

public function handle($request, Closure $next)
{
    if ($request->user() && ! $request->user()->subscribed('main')) {
        // このユーザーは支払っていない顧客
        return redirect('billing');
    }

    return $next($request);
}

ユーザーがまだ試用期間であるかを調べるには、onTrialメソッドを使用します。このメソッドはまだ使用期間中であるとユーザーに警告を表示するために便利です。

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

subscribedToPlanメソッドは、そのユーザーがStripe/BraintreeのプランIDで指定したプランを購入しているかを確認します。以下の例では、ユーザーのmainサブスクリプションが、購入され有効なmonthlyプランであるかを確認しています。

if ($user->subscribedToPlan('monthly', 'main')) {
    //
}

キャンセルしたサブスクリプションの状態

ユーザーが一度アクティブな購入者になってから、サブスクリプションをキャンセルしたことを調べるには、cancelledメソッドを使用します。

if ($user->subscription('main')->cancelled()) {
    //
}

また、ユーザーがサブスクリプションをキャンセルしているが、まだ完全に期限が切れる前の「猶予期間」中であるかを調べることもできます。例えば、ユーザーが3月5日にサブスクリプションをキャンセルし、3月10日に無効になる場合、そのユーザーは3月10日までは「猶予期間」中です。subscribedメソッドは、この期間中、まだtrueを返すことに注目して下さい。

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

プラン変更

アプリケーションの購入済みユーザーが新しいサブスクリプションプランへ変更したくなるのはよくあるでしょう。ユーザーを新しいサブスクリプションに変更するには、swapメソッドへプランの識別子を渡します。

$user = App\User::find(1);

$user->subscription('main')->swap('provider-plan-id');

ユーザーが試用期間中の場合、試用期間は継続します。また、そのプランに「購入数」が存在している場合、購入個数も継続します。

プランを変更し、ユーザーの現プランの試用期間をキャンセルする場合は、skipTrialメソッドを使用します。

$user->subscription('main')
        ->skipTrial()
        ->swap('provider-plan-id');

購入数

{note} 購入数はStripe版のCashierでのみサポートしています。Braintreeには、Stripeの”quantity”(購入数)にあたる機能がありません。

購入数はサブスクリプションに影響をあたえることがあります。たとえば、あるアプリケーションで「ユーザーごと」に毎月10ドル課金している場合です。購入数を簡単に上げ下げするには、incrementQuantitydecrementQuantityメソッドを使います。

$user = User::find(1);

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

// 現在の購入数を5個増やす
$user->subscription('main')->incrementQuantity(5);

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

// 現在の購入数を5個減らす
$user->subscription('main')->decrementQuantity(5);

もしくは特定の数量を設置するには、updateQuantityメソッドを使ってください。

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

使用期間による支払いの按分を行わずに、サブスクリプション数を変更する場合は、noProrateメソッドを使ってください。

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

サブスクリプション数の詳細については、Stripeドキュメントを読んでください。

サブスクリプションの税金

ユーザーが支払うサブスクリプションに対する税率を指定するには、BillableモデルへtaxPercentageメソッドを実装し、小数点以下が1桁以内で、0から100までの数値を返します。

public function taxPercentage() {
    return 20;
}

taxPercentageメソッドにより、モデルごとに税率を適用できるため、多くの州や国に渡るユーザーベースで税率を決める場合に便利です。

{note} taxPercentageメソッドは、サブスクリプションの課金時のみに適用されます。Cashierで「一回のみ」の支払いを行う場合は、税率を自分で適用する必要があります。

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

サブスクリプションをキャンセルするにはcancelメソッドをユーザーのサブスクリプションに対して使ってください。

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

サブスクリプションがキャンセルされるとCashierは自動的に、データベースのends_atカラムをセットします。このカラムはいつからsubscribedメソッドがfalseを返し始めればよいのか、判定するために使用されています。例えば、顧客が3月1日にキャンセルしたが、そのサブスクリプションが3月5日に終了するようにスケジュールされていれば、subscribedメソッドは3月5日になるまでtrueを返し続けます。

ユーザーがサブスクリプションをキャンセルしたが、まだ「猶予期間」が残っているかどうかを調べるにはonGracePeriodメソッドを使います。

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

サブスクリプションを即時キャンセルしたい場合は、ユーザーのサブスクリプションに対し、cancelNowメソッドを呼び出してください。

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

サブスクリプション再開

ユーザーがキャンセルしたサブスクリプションを、再開したいときには、resumeメソッドを使用してください。サブスクリプションを再開するには、そのユーザーに有効期間が残っている必要があります

$user->subscription('main')->resume();

ユーザーがサブスクリプションをキャンセルし、それからそのサブスクリプションを再開する場合、そのサブスクリプションの有効期日が完全に切れていなければすぐに課金されません。そのサブスクリプションはシンプルに再度有効になり、元々の支払いサイクルにより課金されます

クレジットカード変更

顧客のクレジットカード情報を更新する場合は、updateCardメソッドを使います。このメソッドは、Stripeトークンを受け付け、新しいクレジットカードをデフォルトの支払先として登録します。

$user->updateCard($stripeToken);

サブスクリプションのトレイト

カードの事前登録あり

顧客へ試用期間を提供し、支払情報を事前に登録してもらう場合、サブスクリプションを作成するときにtrialDaysメソッドを使ってください。

$user = User::find(1);

$user->newSubscription('main', 'monthly')
            ->trialDays(10)
            ->create($stripeToken);

このメソッドはデータベースのサブスクリプションレコードへ、試用期間の終了日を設定すると同時に、Stripe/Braintreeへこの期日が過ぎるまで、顧客へ課金を始めないように指示します。

{note} 顧客のサブスクリプションが試用期間の最後の日までにキャンセルされないと、期限が切れると同時に課金されます。そのため、ユーザーに試用期間の終了日を通知しておくべきでしょう。

ユーザーが使用期間中であるかを判定するには、ユーザーインスタンスに対しonTrialメソッドを使うか、サブスクリプションインスタンスに対してonTrialを使用してください。次の2つの例は、同じ目的を達します。

if ($user->onTrial('main')) {
    //
}

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

カードの事前登録なし

事前にユーザーの支払い方法の情報を登録してもらうことなく、試用期間を提供する場合は、そのユーザーのレコードのtrial_ends_atに、試用の最終日を設定するだけです。典型的な使い方は、ユーザー登録時に設定する方法でしょう。

$user = User::create([
    // 他のユーザープロパティの設定…
    'trial_ends_at' => Carbon::now()->addDays(10),
]);

{note} モデル定義のtrial_ends_atに対する、日付ミューテタを付け加えるのを忘れないでください。

既存のサブスクリプションと関連付けが行われていないので、Cashierでは、このタイプの試用を「包括的な試用(generic trial)」と呼んでいます。Userインスタンスに対し、onTrialメソッドがtrueを返す場合、現在の日付はtrial_ends_atの値を過ぎていません。

if ($user->onTrial()) {
    // ユーザーは試用期間中
}

特に、ユーザーが「包括的な試用」期間中であり、まだサブスクリプションが作成されていないことを調べたい場合は、onGenericTrialメソッドが使用できます。

if ($user->onGenericTrial()) {
    // ユーザーは「包括的」な試用期間中
}

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

$user = User::find(1);

$user->newSubscription('main', 'monthly')->create($stripeToken);

StripeのWebフック処理

StripeとBraintree、両方共にWebフックによりアプリケーションへ様々なイベントを通知できます。StripeのWebフックを処理するには、CashierのWebフックコントローラへのルートを定義する必要があります。このコントローラは通知されるWebフックリクエストをすべて処理し、正しいコントローラメソッドをディスパッチします。

Route::post(
    'stripe/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

{note} ルートを登録したら、Stripeコントロールパネル設定のWebフックURLも、合わせて設定してください。

このコントローラはデフォルトで、(Stripeの設定により決まる)課金の失敗が多すぎる場合、サブスクリプションを自動的にキャンセル処理します。しかしながら、処理したいWebフックイベントをどれでも処理できるようにするために、このコントローラを拡張する方法を以降で学びます。

WebフックとCSRF保護

StripeのWebフックでは、Laravelの CSRFバリデーションをバイパスする必要があるため、VerifyCsrfTokenミドルウェアのURIを例外としてリストしておくか、ルート定義をwebミドルウェアグループのリストから外しておきましょう。

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

Webフックハンドラの定義

Cashierは課金の失敗時にサブスクリプションを自動的に処理しますが、他にもStripeのWebフックイベントを処理したい場合は、Webフックコントローラをシンプルに拡張します。メソッド名はCashierの命名規則に沿う必要があります。メソッドはhandleのプレフィックスで始まり、処理したいStripeのWebフックの名前を「キャメルケース」にします。たとえば、invoice.payment_succeeded Webフックを処理する場合は、handleInvoicePaymentSucceededメソッドをコントローラに追加します。

<?php

namespace App\Http\Controllers;

use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * StripeのWebフック処理
     *
     * @param  array  $payload
     * @return Response
     */
    public function handleInvoicePaymentSucceeded($payload)
    {
        // イベントの処理
    }
}

サブスクリプション不可

顧客のクレジットカードが有効期限切れだったら? 心配いりません。Cashierは顧客のサブスクリプションを簡単にキャンセルできるWebフックを用意しています。前記と同じように、コントローラのルートを指定するだけです。

Route::post(
    'stripe/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

これだけです! 課金の失敗はコントローラにより捉えられ、処理されます。コントローラはStripeによりサブスクリプションが不能だと判断されると(通常は課金に3回失敗時)、その顧客のサブスクリプションをキャンセルします。

BraintreeのWebフック処理

StripeとBraintree、両方共にWebフックによりアプリケーションへ様々なイベントを通知できます。BraintreeのWebフックを処理するには、CashierのWebフックコントローラへのルートを定義する必要があります。このコントローラは通知されるWebフックリクエストをすべて処理し、正しいコントローラメソッドをディスパッチします。

Route::post(
    'braintree/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

{note} ルートを登録したら、Braintreeコントロールパネル設定のWebフックURLも、合わせて設定してください。

このコントローラはデフォルトで、(Braintreeの設定により決まる)課金の失敗が多すぎる場合、サブスクリプションを自動的にキャンセル処理します。しかしながら、処理したいWebフックイベントをどれでも処理できるようにするために、このコントローラを拡張する方法を以降で学びます。

WebフックとCSRF保護

BraintreeのWebフックでは、Laravelの CSRFバリデーションをバイパスする必要があるため、VerifyCsrfTokenミドルウェアのURIを例外としてリストしておくか、ルート定義をwebミドルウェアグループのリストから外しておきましょう。

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

Webフックハンドラの定義

Braintreeは課金の失敗時にサブスクリプションを自動的に処理しますが、他にもStripeのWebフックイベントを処理したい場合は、Webフックコントローラをシンプルに拡張します。メソッド名はCashierの命名規則に沿う必要があります。メソッドはhandleのプレフィックスで始まり、処理したいStripeのWebフックの名前を「キャメルケース」にします。たとえば、dispute_opened Webフックを処理する場合は、handleDisputeOpenedメソッドをコントローラに追加します。

<?php

namespace App\Http\Controllers;

use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * Braintree Webフックの処理
     *
     * @param  WebhookNotification  $webhook
     * @return Response
     */
    public function handleDisputeOpened(WebhookNotification $notification)
    {
        // イベントの処理
    }
}

サブスクリプション不可

顧客のクレジットカードが有効期限切れだったら? 心配いりません。Cashierは顧客のサブスクリプションを簡単にキャンセルできるWebフックコントローラを含んでいます。コントローラのルートを指定するだけです。

Route::post(
    'braintree/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

これだけです。支払いが失敗すると、コントローラにより捉えられ、処理されます。Braintreeが購入に失敗したと判断すると(通常は支払いに3回失敗した場合)、その顧客のサブスクリプションはキャンセルされます。BraintreeのコントロールパネルでWebフックのURIを設定する必要があることを忘れないで下さい。

一回だけの課金

課金のみ

{note} Stripeを使用している場合、chargeメソッドにはアプリケーションで使用している通貨の最低単位で金額を指定しますが、Braintreeのchargeメソッドには、ドル単位の金額を指定します。

すでに何かを購入している顧客のクレジットカードに、「一回だけ」の課金をしたい場合は、billableモデルのインスタンスに対し、chargeメソッドを使います。

// Stripeはセント単位で課金する
$user->charge(100);

// Braintreeはドル単位で課金する
$user->charge(1);

chargeメソッドは第2引数に配列を受け付け、裏で作成されるStripe/Braintreeの課金作成に対するオプションを指定できます。StripeとBraintreeの課金作成時に使用できるオプションについては、ドキュメントを参照してください。

$user->charge(100, [
    'custom_option' => $value,
]);

chargeメソッドは、課金に失敗した場合に例外を投げます。課金に成功すると、メソッドはtripe/Braintreeレスポンスをそのまま返します。

try {
    $response = $user->charge(100);
} catch (Exception $e) {
    //
}

インボイス付き課金

一回だけ課金をしつつ、顧客へ発行するPDFのレシートとしてインボイスも生成したいことがあります。invoiceForメソッドは、まさにそのために存在しています。例として、「一回だけ」の料金を5ドル課金してみましょう。

// Stripeはセント単位で課金する
$user->invoiceFor('One Time Fee', 500);

// Braintreeはドル単位で課金する
$user->invoiceFor('One Time Fee', 5);

金額は即時にユーザーのクレジットカードへ課金されます。invoiceForメソッドは第3引数として配列を受け付け、裏で作成されるStripe/Braintreeの課金オプションを指定できます。

$user->invoiceFor('One Time Fee', 500, [
    'custom-option' => $value,
]);

{note} invoiceForメソッドは、課金失敗時にリトライするStripeインボイスを生成します。リトライをしてほしくない場合は、最初に課金に失敗した時点で、Stripe APIを使用し、生成したインボイスを閉じる必要があります。

インボイス

invoicesメソッドにより、billableモデルのインボイスの配列を簡単に取得できます。

$invoices = $user->invoices();

// 結果にペンディング中のインボイスも含める
$invoices = $user->invoicesIncludingPending();

顧客へインボイスを一覧表示するとき、そのインボイスに関連する情報を表示するために、インボイスのヘルパメソッドを表示に利用できます。ユーザーが簡単にダウンロードできるように、テーブルで全インボイスを一覧表示する例を見てください。

<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td></td>
            <td></td>
            <td><a href="/user/invoice/">Download</a></td>
        </tr>
    @endforeach
</table>

インボイスPDF生成

ルートやコントローラの中でdownloadInvoiceメソッドを使うと、そのインボイスのPDFダウンロードを生成できます。このメソッドはブラウザへダウンロードのHTTPレスポンスを正しく行うHTTPレスポンスを生成します。

use Illuminate\Http\Request;

Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
    return $request->user()->downloadInvoice($invoiceId, [
        'vendor'  => 'Your Company',
        'product' => 'Your Product',
    ]);
});