Laravel 6.0 Laravel Cashier

イントロダクション

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

Note: ブレーキングチェンジを防ぐために、CashierではStripeの固定APIバージョンを使用しています。Cashier10.1では、Stripeの2019-08-14付けAPIバージョンを使用しています。Stripeの新機能や機能向上を利用するため、マイナーリリースでもStripe APIのバージョンを更新することがあります。

Cashierのアップデート

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

インストール

はじめに、Stripe向けCashierパッケージをComposerでインストールしてください。

composer require laravel/cashier

Note: Stripeの全イベントをCashierで確実に処理するために、CashierのWebhook処理の準備を行なってください。

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

CashierサービスプロバーダでCashierのデータベースマイグレーションを登録しています。ですから、パッケージをインストールしたら、データベースのマイグレーションを忘れず実行してください。Cashierマイグレーションはusersテーブルにいくつものカラムを追加し、顧客のサブスクリプションを全て保持するために新しいsubscriptionsテーブルを作成します。

php artisan migrate

Cashierパッケージに始めから含まれているマイグレーションをオーバーライトしたい場合は、vendor:publish Artisanコマンドを使用し公開できます。

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

Cashierのマイグレーション実行を完全に防ぎたい場合は、Cashierが提供しているignoreMigrationsを使います。通常、このメソッドはAppServiceProviderregisterメソッドの中で実行すべきです。

use Laravel\Cashier\Cashier;

Cashier::ignoreMigrations();

Note: StripeはStripeの識別子を保存しておくカラムはケースセンシティブ(大文字小文字区別)にするべきだと勧めています。そのためstripe_idカラムには、たとえばMySQLではutf8_binのように、適切なカラムコレーションを確実に指定してください。詳しい情報は、Stripeのドキュメントをお読みください。

設定

Billableモデル

Cashierを使い始める前に、モデル定義にBillableトレイトを追加します。このトレイトはサブスクリプションの作成やクーポンの適用、支払い情報の更新などのような、共通の支払いタスク実行を提供する数多くのメソッドを提供しています。

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

CashierはLaravelにLaravelに含まれているApp\UserクラスがBillableモデルであると仮定しています。これを変更する場合は、.envファイルでモデルを指定してください。

CASHIER_MODEL=App\User

Note: Laravelの提供するApp\Userモデル以外のモデルを使用する場合は、提供しているマイグレーションを公開し、モデルのテーブル名に一致するように変更する必要があります。

APIキー

次に、.envファイルの中のStripeキーを設定する必要があります。Stripe APIキーは、Stripeのコントロールパネルから取得できます。

STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret

通貨設定

Cashierのデフォルト通貨は米ドル(USD)です。CASHIER_CURRENCY環境変数の指定で、デフォルト通貨を変更可能です。

CASHIER_CURRENCY=eur

Caishierの通貨設定に付け加え、インボイスで表示する金額のフォーマットをローケルを使い指定することも可能です。Cashierは内部で、通貨のローケルを指定するために、PHPのNumberFormatterクラスを利用しています。

CASHIER_CURRENCY_LOCALE=nl_BE

Note: en以外のローケルを指定する場合は、サーバ設定でext-intl PHP拡張がインストールされているのを確認してください。

顧客

顧客の生成

時々サブスクリプションを開始しなくてもStripeで顧客を作成したい場合があります。createAsStripeCustomerを使い、作成できます。

$user->createAsStripeCustomer();

Stripeで顧客を生成しておけば、後からサブスクリプションを開始できます。

支払い方法

支払い方法の保存

Stripeでサブスクリプションを生成するか「一度だけ」の課金を実行するためには、支払い方法を登録し、IDを取得する必要があります。サブスクリプションのための支払いメソッドか、一回だけの課金ためかによりアプローチが異なるため、以下で両方共にみていきましょう。

サブスクリプションの支払い方帆

将来の仕様に備えて、顧客のクレジットカードを登録する場合、顧客の支払いメソッドの詳細を安全に集めるためにStripe Setup Intents APIを使う必要があります。"Setup Intent(意図)"は、Stripeに対し顧客の支払いメソッドを登録する意図を示しています。CashierのBillableトレイトは、新しいSetup Intentを簡単に作成できるcreateSetupIntentを含んでいます。顧客の支払いメソッドの詳細情報を集めるフォームをレンダーしたいルートやコントローラから、このメソッドを呼び出してください。

return view('update-payment-method', [
    'intent' => $user->createSetupIntent()
]);

Setup Intentを作成したらそれをビューに渡し、支払い方法を集める要素にsecretを付け加える必要があります。例えば、このような「支払い方法更新」フォームを考えてください。

<input id="card-holder-name" type="text">

<!-- Stripe要素のプレースホルダ -->
<div id="card-element"></div>

<button id="card-button" data-secret="{{ $intent->client_secret }}">
    Update Payment Method
</button>

Stripe.jsライブラリを使い、Stripe要素をフォームに付け加え、顧客の支払いの詳細を安全に収集します。

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

これでStripeのhandleCardSetupメソッドを使用してカードを検証し、Stripeから安全な「支払い方法識別子」を取得できます。

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

cardButton.addEventListener('click', async (e) => {
    const { setupIntent, error } = await stripe.handleCardSetup(
        clientSecret, cardElement, {
            payment_method_data: {
                billing_details: { name: cardHolderName.value }
            }
        }
    );

    if (error) {
        // ユーザーに"error.message"を表示する…
    } else {
        // カードの検証に成功した…
    }
});

Stripeによりカードが検証されたら、顧客に付け加えたsetupIntent.payment_methodの結果をLaravelアプリケーションへ渡すことができます。支払い方法は新しい支払い方法を追加するのと、デフォルトの支払い方法を使用する、どちらかが選べます。新しい支払い方法を追加の支払いメソッド識別子を即時に使用することもできます。

Tip!! Setup Intentsと顧客支払いの詳細情報の収集に関するより詳しい情報は、Stripeが提供している概要をご覧ください。

一回のみの課金に対する支払い方法

顧客の支払いメソッドに対し一回のみの課金を作成する場合、ワンタイムの支払いメソッド識別子を使う必要があるだけで済みます。Stripeの制限により、保存されている顧客のデフォルト支払い方法は使用できません。Stripe.jsライブラリを使用し、顧客に支払い方法の詳細を入力してもらえるようにする必要があります。例として、以降のフォームを考えてみましょう。

<input id="card-holder-name" type="text">

<!-- Stripe要素のプレースホルダ -->
<div id="card-element"></div>

<button id="card-button">
    Process Payment
</button>

Stripe.jsライブラリを使い、Stripe要素をフォームに付け加え、顧客の支払いの詳細を安全に収集します。

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

StripeのcreatePaymentMethodメソッドを活用し、Stripeによりカードが検証し、安全な「支払い方法識別子」をSrtipeから取得します。

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');

cardButton.addEventListener('click', async (e) => {
    const { paymentMethod, error } = await stripe.createPaymentMethod(
        'card', cardElement, {
            billing_details: { name: cardHolderName.value }
        }
    );

    if (error) {
        // ユーザーに"error.message"を表示する…
    } else {
        // カードの検証に成功した…
    }
});

カードの検証が成功すれば、paymentMethod.idをLaravelアプリケーションに渡し、1回限りの支払いを処理できます。

支払い方法の取得

BillableモデルインスタンスのpaymentMethodsメソッドは、Laravel\Cashier\PaymentMethodインスタンスのコレクションを返します。

$paymentMethods = $user->paymentMethods();

デフォルト支払いメソッドを取得する場合は、defaultPaymentMethodメソッドを使用してください。

$paymentMethod = $user->defaultPaymentMethod();

ユーザーが支払い方法を持っているかの判定

Billableモデルが自身のアカウントに付加されている支払いメソッドを持っているかを判定するには、hasPaymentMethodメソッドを使用します。

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

デフォルト支払い方法の更新

updateDefaultPaymentMethodメソッドは顧客のデフォルト支払い方法の情報を更新するために使用します。このメソッドはStripe支払いメソッド識別子を引数に取り、その新しい支払い方法がデフォルト支払い方法として設定されます。

$user->updateDefaultPaymentMethod($paymentMethod);

その顧客のデフォルト支払い方法情報をStripeの情報と同期したい場合は、updateDefaultPaymentMethodFromStripeメソッドを使用してください。

$user->updateDefaultPaymentMethodFromStripe();

Note: 顧客のデフォルト支払い方法は、インボイス発行処理と新しいサブスクリプションの生成にだけ使用されます。Stripeの制限により、一回だけの課金には使用されません。

支払い方法の追加

新しい支払い方法を追加するには、Billableのユーザーに対し、addPaymentMethodを呼び出します。支払いメソッド識別子を渡してください。

$user->addPaymentMethod($paymentMethod);

Tip!! 支払い方法の識別子の取得方法を学ぶには、支払い方法保持のドキュメントを確認してください。

支払い方法の削除

支払い方法を削除するには、削除したいLaravel\Cashier\PaymentMethodインスタンス上のdeleteメソッドを呼び出します。

$paymentMethod->delete();

deletePaymentMethodsメソッドは、そのBillableモデルの全支払いメソッド情報を削除します。

$user->deletePaymentMethods();

Note: アクティブなサブスクリプションがあるユーザーでは、デフォルト支払いメソッドが削除されないようにする必要があるでしょう。

サブスクリプション

サブスクリプション作成

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

$user = User::find(1);

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

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

createメソッドはStripeの支払い方法識別子、もしくはPaymentMethodオブジェクトを引数に取り、サブスクリプションを開始するのと同時に、データベースの顧客IDと他の関連する支払い情報を更新します。

Note: サブスクリプションのcreate()へ支払いメソッド識別子を直接渡すと、ユーザーの保存済み支払いメソッドへ自動的に追加します。

ユーザー詳細情報の指定

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

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

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

クーポン

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

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

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

ユーザーがアプリケーションで何かを購入したら、バラエティー豊かで便利なメソッドでサブスクリプション状況を簡単にチェックできます。まず初めに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のプランIDで指定したプランを購入しているかを確認します。以下の例では、ユーザーのmainサブスクリプションが、購入され有効なmonthlyプランであるかを確認しています。

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

recurringメソッドはユーザーが現在サブスクリプションを購入中で、試用期間を過ぎていることを判断するために使用します。

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

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

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

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

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

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

ユーザーがサブスクリプションをキャンセルし、「猶予期間」を過ぎていることを調べるには、endedメソッドを使ってください。

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

不十分と期日超過の状態

サブスクリプション作成後、そのサブクリプションが2つ目の支払いアクションを要求している場合、incomplete(不十分)として印がつけられます。サブスクリプションの状態は、Cashierのsubscriptionsデータベーステーブルのstripe_statusカラムに保存されます。

同様に、サブスクリプションの変更時に第2の支払いアクションが要求されている場合は、past_due(期日超過)として印がつけられます。サブスクリプションが2つのどちらかである時、顧客が支払いを受領するまで状態は有効になりません。あるサブクリプションに不十分な支払いがあるかを確認する場合は、Billableモデルかサブクリプションインスタンス上のhasIncompletePaymentメソッドを使用します。

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

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

サブクリプションに不完全な支払いがある場合、latestPayment(最後の支払い)識別子を渡したCashierの支払い確認ページをそのユーザーへ表示すべきです。この識別子を取得するには、サブクリプションインスタンスのlatestPaymentメソッドが使用できます。

<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">
    Please confirm your payment.
</a>

Note: あるサブクリプションにincomplete状態がある場合、支払いを確認するまでは変更できません。そのためサブクリプションがincomplete状態では、swapupdateQuantityメソッドは例外を投げます。

プラン変更

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

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

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

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

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

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

次の支払いサイクルまで待つ代わりに、プランを変更時即時にインボイスを発行したい場合は、swapAndInvoiceメソッドを使用します。

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

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

購入数

購入数はサブスクリプションに影響をあたえることがあります。たとえば、あるアプリケーションで「ユーザーごと」に毎月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で「一回のみ」の支払いを行う場合は、税率を自分で適用する必要があります。

税率の同期

taxPercentageが返すハードコードした値を変更する場合、ユーザーに対する既存のサブスクリプションは以前のままになります。taxPercentageが返す値に既存のサブスクリプションも更新したい場合は、ユーザーのサブスクリプションインスタンスに対し、syncTaxPercentageメソッドを呼び出す必要があります。

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

サブスクリプション課金日付け

デフォルトで課金日はサブスクリプションが生成された日付け、もしくは使用期間を使っている場合は、使用期間の終了日です。課金日付を変更したい場合は、anchorBillingCycleOnメソッドを使用します。

use App\User;
use Carbon\Carbon;

$user = User::find(1);

$anchor = Carbon::parse('first day of next month');

$user->newSubscription('main', 'premium')
            ->anchorBillingCycleOn($anchor->startOfDay())
            ->create($paymentMethod);

サブスクリプションの課金間隔を管理する情報は、Stripeの課金サイクルのドキュメントをお読みください。

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

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

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

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

支払いの事前登録あり

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

$user = User::find(1);

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

このメソッドはデータベースのサブスクリプションレコードへ、試用期間の終了日を設定すると同時に、Stripeへこの期日が過ぎるまで、顧客へ課金を始めないように指示します。trialDaysメソッドを使用する場合、Stripeでそのプランに対して設定したデフォルトの試用期間はオーバーライドされます。

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

trialUntilメソッドにより、使用期間の終了時を指定する、DateTimeインスタンスを渡せます。

use Carbon\Carbon;

$user->newSubscription('main', 'monthly')
            ->trialUntil(Carbon::now()->addDays(10))
            ->create($paymentMethod);

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

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

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

支払いの事前登録なし

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

$user = User::create([
    // 他のユーザープロパティの設定…
    'trial_ends_at' => 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($paymentMethod);

StripeのWebフック処理

Tip!! ローカル環境でWebhooksのテストの手助けをするために、Laravel Valetvalet shareコマンドが使用できます。

StripeはWebフックにより、アプリケーションへ様々なイベントを通知できます。デフォルトで、CashierのWebhookを処理するルートのコントローラは、Cashierのサービスプロバイダで設定されています。このコントローラはWebhookの受信リクエストをすべて処理します。

デフォルトでこのコントローラは、課金に多く失敗し続ける(Stripeの設定で定義している回数)、顧客の更新、顧客の削除、サブスクリプションの変更、支払い方法の変更があると、自動的にサブスクリプションをキャンセル処理します。しかしながら、すぐに見つけることができるようにこのコントローラを拡張し、どんなWebhookイベントでもお好きに処理できます

アプリケーションでStripeのWebhookを処理するためには、StripeのコントロールパネルでWebhook URLを確実に設定してください。Stripeのコントロールパネルで設定する必要のあるWebhookの全リストは、以下のとおりです。

Note: Cashierに含まれる、Webフック署名の確認ミドルウェアを使用し、受信リクエストを確実に保護してください。

WebフックとCSRF保護

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

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

Webフックハンドラの定義

Cashierは課金の失敗時に、サブスクリプションを自動的にキャンセル処理しますが、他の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
{
    /**
     * インボイス支払い成功時の処理
     *
     * @param  array  $payload
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function handleInvoicePaymentSucceeded($payload)
    {
        // イベントの処理…
    }
}

次に、routes/web.phpの中で、キャッシャーコントローラへのルートを定義します。これにより、デフォルトのルートが上書きされます。

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

サブスクリプション不可

顧客のクレジットカードが有効期限切れだったら? 心配いりません。CashierのWebhookコントローラが顧客のサブスクリプションをキャンセルします。失敗した支払いは自動的に捉えられ、コントローラにより処理されます。このコントローラはStripeがサブスクリプションに失敗したと判断した場合、顧客のサブスクリプションを取り消します。(通常、3回の課金失敗)

Webフック署名の確認

Webフックを安全にするため、StripeのWebフック著名が利用できます。便利に利用できるように、Cashierは送信されてきたWebフックリクエストが有効なものか確認するミドルウェアをあらかじめ用意しています。

Webhookの確認を有効にするには、.envファイル中のSTRIPE_WEBHOOK_SECRET環境変数を確実に設定してください。Stripeアカウントのダッシュボードから取得される、Webhookのsecretを指定します。

一回だけの課金

課金のみ

Note: chargeメソッドにはアプリケーションで使用している通貨の最低単位で金額を指定します。

サブスクリプションを購入している顧客の支払いメソッドに対して、「一回だけ」の課金を行いたい場合は、Billableモデルインスタンス上のchargeメソッドを使用します。第2引数に支払い方法識別子を渡してください。

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

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

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

課金に失敗すると、chargeメソッドは例外を投げます。課金に成功すれば、メソッドはLaravel\Cashier\Paymentのインスタンスを返します。

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

インボイス付き課金

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

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

金額は即時にユーザーのデフォルト支払い方法へ課金されます。invoiceForメソッドは第3引数に配列を受け付けます。この配列はインボイスアイテムへの支払いオプションを含みます。第4引数も配列で、インボイス自身に対する支払いオプションを指定します。

$user->invoiceFor('Stickers', 500, [
    'quantity' => 50,
], [
    'tax_percent' => 21,
]);

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

払い戻し

Stripeでの課金を払い戻す必要がある場合は、refundメソッドを使用します。このメソッドの第1引数は、Stripe Payment Intent IDです。

$payment = $user->charge(100, $paymentMethod);

$user->refund($payment->id);

インボイス

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

$invoices = $user->invoices();

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

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

<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td>{{ $invoice->date()->toFormattedDateString() }}</td>
            <td>{{ $invoice->total() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">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',
    ]);
});

堅牢な顧客認証

皆さんのビジネスがヨーロッパを基盤とするものであるなら、堅牢な顧客認証 (SCA)規制を守る必要があります。これらのレギュレーションは支払い詐欺を防ぐためにEUにより2019年9月に課せられたものです。幸運なことに、StripeとCashierはSCA準拠のアプリケーション構築のために準備をしてきました。

Note: 始める前に、StripeのPSD2とSCAのガイドと、新SCA APIのドキュメントを確認してください。

支払い要求の追加確認

SCA規制は支払いの確認と処理を行うため、頻繁に追加の検証を要求しています。これが起きるとCashierはIncompletePayment例外を投げ、この追加の検証が必要であるとあなたに知らせます。この例外を捉えたら、処理の方法は2つあります。

最初の方法は、その顧客をCashierに含まれている支払い確認専門ページへリダイレクトする方法です。このページに紐つけたルートは、Cashierのサービスプロバイダで登録済みです。そのため、IncompletePayment例外を捉えたら、支払い確認ページへリダイレクトします。

use Laravel\Cashier\Exceptions\IncompletePayment;

try {
    $subscription = $user->newSubscription('default', $planId)
                            ->create($paymentMethod);
} catch (IncompletePayment $exception) {
    return redirect()->route(
        'cashier.payment',
        [$exception->payment->id, 'redirect' => route('home')]
    );
}

支払い確認ページで顧客はクレジットカード情報の入力を再度促され、「3Dセキュア」のような追加のアクションがStripeにより実行されます。支払いが確認されたら、上記のようにredirect引数で指定されたURLへユーザーはリダイレクトされます。

別の方法として、Stripeに支払いの処理を任せることもできます。この場合、支払い確認ページへリダイレクトする代わりに、StripeダッシュボードでStripeの自動支払いメールを瀬一定する必要があります。しかしながら、IncompletePayment例外を捉えたら、支払い確認方法の詳細がメールで送られることをユーザーへ知らせる必要があります。

不完全な支払いの例外は、Billableのユーザーに対するchargeinvoiceForinvoiceメソッドで投げられる可能性があります。スクリプションが処理される時、SubscriptionBuildercreateメソッドと、SusbcriptionモデルのincrementAndInvoiceswapAndInvoiceメソッドは、例外を投げるでしょう。

不十分と期日超過の状態

支払いが追加の確認を必要とする場合そのサブクリプションは、stripe_statusデータベースカラムにより表されるincompletepast_due状態になります。Cashierは支払いの確認が完了するとすぐに、Webhookによりその顧客のサブスクリプションが自動的に有効にします。

incompletepast_due状態の詳細は、追加のドキュメントを参照してください。

非セッション確立時の支払い通知

SCA規制は、サブスクリプションが有効なときにも、時々支払いの詳細を確認することを顧客に求めています。Cashierではセッションが確立していない時に支払いの確認が要求された場合に、顧客へ支払いの通知を送ることができます。例えば、サブスクリプションを更新する時にこれが起きます。Cashierの支払い通知はCASHIER_PAYMENT_NOTIFICATION環境変数へ通知クラスをセットすることで有効になります。デフォルトでは、この通知は無効です。もちろん、Cashierにはこの目的に使うための通知クラスが含まれていますが、必要であれば自作の通知クラスを自由に指定できます。

CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment

非セッション時の支払い確認通知が確実に届くように、StripeのWebhookが設定されており、Stripeのダッシュボードでinvoice.payment_action_required Webhookが有効になっていることを確認してください。さらに、BillableモデルがLaravelのIlluminate\Notifications\Notifiableトレイトを使用していることも確認してください。

Note: 定期課金でなく、顧客が自分で支払った場合でも追加の確認が要求された場合は、その顧客に通知が送られます。残念ながら、Stripeはその支払いが手動や「非セッション時」であることを知る方法がありません。しかし、顧客は支払いを確認した後に支払いページを閲覧したら、「支払いが完了しました」メッセージを確認できます。その顧客は同じ支払いを2度行い、二重に課金されるアクシデントに陥ることを防ぐことができるでしょう。

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる