イントロダクション

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

設定

Composer

最初に、composer.jsonファイルに、Cashierパッケージを追加し、composer updateコマンドを実行します。

"laravel/cashier": "~5.0"(2015年2月18日以降のバージョン、およびStripe SDK 2.0まで)
"laravel/cashier": "~4.0"(2015年2月18日以降のバージョン)
"laravel/cashier": "~3.0"(2015年2月16日までのバージョン)

サービスプロバイダー

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

マイグレーション

キャッシャーを使用する前に、データベースにいくつかカラムを追加する必要があります。心配ありません。cashier:table Artisanコマンドで必要なカラムを追加するマイグレーションが生成されます。たとえばusersテーブルにカラムを追加するには、php artisan cashier:table usersを実行します。

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

モデルの準備

次にBillableトレイトと適切な日付ミューテターをモデルで定義してください。

use Laravel\Cashier\Billable;
use Laravel\Cashier\Contracts\Billable as BillableContract;

class User extends Model implements BillableContract
{
    use Billable;

    protected $dates = ['trial_ends_at', 'subscription_ends_at'];
}

モデルの$datesプロパティにカラムを追加することで、Eloquentに文字列ではなくCarbon/DateTimeインスタンスを指定カラムで返すように指示します。

Stripeキー

最後に、services.php設定ファイルへStripeキーを設置します。

'stripe' => [
    'model'  => 'User',
    'secret' => env('STRIPE_API_SECRET'),
],

サブスクリプション

サブスクリプション作成

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

$user = User::find(1);

$user->subscription('monthly')->create($creditCardToken);

createメソッドは自動的にStripeのサブスクリプションを作成すると同時に、StripeのカスタマーIDと関連する支払い情報をデータベースに保存します。Stripeでプランの試用期間を設定している場合、試用終了日も自動的に、ユーザーのレコードに設定されます。

Stripeの中で定義しているものではなく、アプリケーション独自の試用期間を実装したい場合は、試用終了日を自前で設定する必要があります。

$user->trial_ends_at = Carbon::now()->addDays(14);

$user->save();

ユーザー詳細情報の指定

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

$user->subscription('monthly')->create($creditCardToken, [
    'email' => $email, 'description' => 'Our First Customer'
]);

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

クーポン

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

$user->subscription('monthly')
     ->withCoupon('code')
     ->create($creditCardToken);

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

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

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

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

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

    return $next($request);
}

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

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

onPlanメソッドはStripe IDに基づく指定プランのサブスクリプション購入者であるかを調べます。

if ($user->onPlan('monthly')) {
    //
}

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

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

if ($user->cancelled()) {
    //
}

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

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

everSubscribedメソッドにより、そのユーザーがアプリケーションの永久サブスクリプションプランを持っているかを決定することができます。

if ($user->everSubscribed()) {
    //
}

プラン変更

アプリケーションのサブスクリプション済みユーザーが新しいサブスクリプションプランへ変更したくなるのはよくあるでしょう。ユーザーを新しいサブスクリプションに変更するには、swapメソッドを使用します。例としてユーザーをささっとpremiumサブスクリプションプランへ変更してみましょう。

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

$user->subscription('premium')->swap();

ユーザーが試用期間中の場合、試用期間は通常通りに続きます。また、そのサブスクリプションに"quantity"(購入数)が存在する場合、その個数も維持されます。プランの変更時にprorateメソッドを使うことで支払いの按分を指示することもできます。さらにswapAndInvoiceメソッドでプラン変更のインボイスを即座に発行することもできます。

$user->subscription('premium')
            ->prorate()
            ->swapAndInvoice();

購入数

しばしばサブスクリプションは「個数(quantity)」による影響を受けます。たとえばアプリケーションで毎月10ドルアカウントのユーザーごとに課金している場合です。購入数を簡単に増やしたり、減らしたりするには、incrementdecrementメソッドを使用します。

$user = User::find(1);

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

// 現在の購入数に5つ増やす…
 $user->subscription()->increment(5);

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

// 現在の購入数から5つ減らす…
$user->subscription()->decrement(5);

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

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

購入数の詳細については、Stripeドキュメントを読んでください。

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

キャッシャーを使えばStripeへ「税率(tax_percent)」を送るのも簡単です。サブスクリプションに対する顧客の支払いの税率を指定するには、モデルにgetTaxPercentメソッドをbillableモデルへ実装し、小数点以下の桁数が2桁以内で0から100までの数値を返します。

public function getTaxPercent() {
    return 20;
}

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

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

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

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

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

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

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

サブスクリプション再開

ユーザーがキャンセルしたサブスクリプションを、再開したいときには、resumeメソッドを使用してください。

$user->subscription('monthly')->resume($creditCardToken);

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

StripeのWebフック処理

サブスクリプション不可

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

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

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

StripeのWebフックはLaravelのCSRF確認を適用しない必要があるため、VerifyCsrfTokenミドルウェアで除外するURIのリストへ確実に登録してください。

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

その他のWebフック

処理したい追加のStripe Webフックイベントがある場合、シンプルにWebフックコントローラーを拡張してください。メソッド名はCashierが期待する命名規則に従う必要があります。つまりメソッド名はhandleで始まり、取り扱いたいStripeのWebフックを続けます。たとえばinvoice.payment_succeeded Webフックを使用したいのなら、コントローラーにはhandleInvoicePaymentSucceededメソッドを追加しなければなりません。

<?php

namespace App\Http\Controllers;

use Laravel\Cashier\WebhookController as BaseController;

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

一回だけの課金

定期サブスクリプションしている顧客のクレジットカードに対し「一回限り」の課金を行いたい場合は、billableモデルに対しchargeメソッドを使用してください。chargeメソッドは通貨の最低単位の課金を引数に取ります。そのため次の例は100セント(1ドル)をクレジットカードへ課金します。

$user->charge(100);

chargeメソッドは配列で2つ目の引数も取り、裏で動作しているStrip課金の生成にオプションを渡すことができます。

$user->charge(100, [
    'source' => $token,
    'receipt_email' => $user->email,
]);

chargeメソッドがfalseを返した場合は課金失敗です。通常これは課金が拒否されたことを意味します。

if ( ! $user->charge(100)) {
    // 課金は拒否された…
}

課金が成功したら完全なStripeレスポンスがメソッドから返されます。

インボイス

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

$invoices = $user->invoices();

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

<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td>{{ $invoice->dateString() }}</td>
            <td>{{ $invoice->dollars() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">ダウンロード</a></td>
        </tr>
    @endforeach
</table>

インボイスPDF生成

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

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