イントロダクション
Laravel CashierはStripeによるサブスクリプション(定期課金)サービスの読みやすく、スラスラと記述できるインターフェイスを提供します。これにより書くのが恐ろしくなるような、サブスクリプション支払いのための決まりきったコードのほとんどが処理できます。基本的なサブスクリプション管理に加え、Cashierはクーポン、サブスクリプションの変更、サブスクリプション数、キャンセル猶予期間、さらにインボイスのPDF発行まで行います。
Cashierのアップデート
新しいバージョンのCashierへアップグレードする場合は、アップグレードガイドを注意深く確認することが重要です。
Note: ブレーキングチェンジを防ぐために、CashierではStripeの固定APIバージョンを使用しています。Cashier10.1では、Stripeの
2019-08-14
付けAPIバージョンを使用しています。Stripeの新機能や機能向上を利用するため、マイナーリリースでもStripe APIのバージョンを更新することがあります。
インストール
はじめに、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
を使います。通常、このメソッドはAppServiceProvider
のregister
メソッドの中で実行すべきです。
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に含まれている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に関連する例外をすべてログする時に使用できるログチャンネルをCashierでは指定できます。CASHIER_LOGGER
環境変数を使用し、ログチャンネルを指定します。
CASHIER_LOGGER=stack
顧客
顧客の取得
Cashier::findBillable
メソッドによりStripe
IDで顧客を取得できます。Billableモデルのインスタンスを返します。
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
顧客の生成
サブスクリプションを開始しなくてもStripeで顧客を作成したい場合が、ときどき起きるでしょう。createAsStripeCustomer
を使い、作成できます。
$stripeCustomer = $user->createAsStripeCustomer();
一度顧客をStripe上に作成しておき、後日サブスクリプションを開始することもできます。また、Stripe
APIが提供するオプション($options
)を配列として、追加引数に渡すことも可能です。
$stripeCustomer = $user->createAsStripeCustomer($options);
すでにStripe上に顧客が登録されているときは、Billableエンティティとして顧客オブジェクトを返してもらいたい場合、createOrGetStripeCustomer
メソッドが使用できます。
$stripeCustomer = $user->createOrGetStripeCustomer();
顧客の更新
まれに、Stripeの顧客を追加情報と一緒に直接更新したい状況もあります。updateStripeCustomer
メソッドを使用してください。
$stripeCustomer = $user->updateStripeCustomer($options);
メールアドレスのカスタマイズ
Stripeに顧客を作成するために、CashierはデフォルトでBillableモデルのemail
属性を使用します。これはstripeEmail
メソッドを使い、オーバーライドできます。
/**
* Stripeの顧客作成に使用するメールアドレスの取得
*
* @return string|null
*/
public function stripeEmail()
{
return $this->email;
}
Stripeでの顧客生成にメールアドレスが不必要ならば、null
を返すこともできます。メールアドレスを提供しない場合、メールでの督促、支払い失敗のリマインダーなどのメールに関連する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のconfirmCardSetup
メソッドを使用してカードを検証し、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.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
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();
findPaymentMethod
メソッドを使用し、そのBillableモデルが持っている特定の支払いメソッドを取得することもできます。
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
ユーザーが支払い方法を持っているかの判定
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('default', 'premium')->create($paymentMethod);
newSubscription
メソッドの最初の引数は、サブスクリプションの名前です。アプリケーションでサブスクリプションを一つしか取り扱わない場合、default
かprimary
と名づけましょう。2つ目の引数はユーザーが購入しようとしているサブスクリプションのプランを指定します。この値はStripeのプラン識別子に対応させる必要があります。
create
メソッドはStripeの支払い方法識別子、もしくはPaymentMethod
オブジェクトを引数に取り、サブスクリプションを開始するのと同時に、データベースの顧客IDと他の関連する支払い情報を更新します。
Note: サブスクリプションの
create()
へ支払いメソッド識別子を直接渡すと、ユーザーの保存済み支払いメソッドへ自動的に追加します。
ユーザー詳細情報の指定
ユーザーに関する詳細情報を追加したい場合は、create
メソッドの第2引数に渡すことができます。
$user->newSubscription('default', 'monthly')->create($paymentMethod, [
'email' => $email,
]);
Stripeがサポートしている追加のフィールドについてのさらなる情報は、Stripeの顧客の作成ドキュメントを確認してください。
クーポン
サブスクリプションの作成時に、クーポンを適用したい場合は、withCoupon
メソッドを使用してください。
$user->newSubscription('default', 'monthly')
->withCoupon('code')
->create($paymentMethod);
サブスクリプション状態の確認
ユーザーがアプリケーションで何かを購入したら、バラエティー豊かで便利なメソッドでサブスクリプション状況を簡単にチェックできます。まず初めにsubscribed
メソッドがtrue
を返したら、サブスクリプションが現在試用期間であるにしても、そのユーザーはアクティブなサブスクリプションを持っています。
if ($user->subscribed('default')) {
//
}
subscribed
メソッドはルートミドルウェアで使用しても大変役に立つでしょう。ユーザーのサブスクリプション状況に基づいてルートやコントローラへのアクセスをフィルタリングできます。
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed('default')) {
// このユーザーは支払っていない顧客
return redirect('billing');
}
return $next($request);
}
ユーザーがまだ試用期間であるかを調べるには、onTrial
メソッドを使用します。このメソッドはまだ使用期間中であるとユーザーに警告を表示するために便利です。
if ($user->subscription('default')->onTrial()) {
//
}
subscribedToPlan
メソッドは、そのユーザーがStripeのプランIDで指定したプランを購入しているかを確認します。以下の例では、ユーザーのdefault
サブスクリプションが、購入され有効なmonthly
プランであるかを確認しています。
if ($user->subscribedToPlan('monthly', 'default')) {
//
}
subscribedToPlan
メソッドに配列を渡せば、ユーザーのdefault
サブスクリプションが、購入され有効なmonthly
かyearly
プランであるかを判定できます。
if ($user->subscribedToPlan(['monthly', 'yearly'], 'default')) {
//
}
recurring
メソッドはユーザーが現在サブスクリプションを購入中で、試用期間を過ぎていることを判断するために使用します。
if ($user->subscription('default')->recurring()) {
//
}
キャンセルしたサブスクリプションの状態
ユーザーが一度アクティブな購入者になってから、サブスクリプションをキャンセルしたことを調べるには、cancelled
メソッドを使用します。
if ($user->subscription('default')->cancelled()) {
//
}
また、ユーザーがサブスクリプションをキャンセルしているが、まだ完全に期限が切れる前の「猶予期間」中であるかを調べることもできます。たとえば、ユーザーが3月5日にサブスクリプションをキャンセルし、3月10日で無効になる場合、そのユーザーは3月10日までは「猶予期間」中です。subscribed
メソッドは、この期間中、まだtrue
を返すことに注目してください。
if ($user->subscription('default')->onGracePeriod()) {
//
}
ユーザーがサブスクリプションをキャンセルし、「猶予期間」を過ぎていることを調べるには、ended
メソッドを使ってください。
if ($user->subscription('default')->ended()) {
//
}
不十分と期日超過の状態
サブスクリプション作成後、そのサブクリプションが2つ目の支払いアクションを要求している場合、incomplete
(不十分)として印がつけられます。サブスクリプションの状態は、Cashierのsubscriptions
データベーステーブルのstripe_status
カラムに保存されます。
同様に、サブスクリプションの変更時に第2の支払いアクションが要求されている場合は、past_due
(期日超過)として印がつけられます。サブスクリプションが2つのどちらかである時、顧客が支払いを受領するまで状態は有効になりません。あるサブクリプションに不十分な支払いがあるかを確認する場合は、Billableモデルかサブクリプションインスタンス上のhasIncompletePayment
メソッドを使用します。
if ($user->hasIncompletePayment('default')) {
//
}
if ($user->subscription('default')->hasIncompletePayment()) {
//
}
サブクリプションに不完全な支払いがある場合、latestPayment
(最後の支払い)識別子を渡したCashierの支払い確認ページをそのユーザーへ表示すべきです。この識別子を取得するには、サブクリプションインスタンスのlatestPayment
メソッドが使用できます。
<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">
Please confirm your payment.
</a>
past_due
状態のときでも、特定のサブスクリプションをアクティブと見なしたい場合は、Cashierが提供するkeepPastDueSubscriptionsActive
メソッドを使用します。通常このメソッドは、AppServiceProvider
のregister
メソッドの中で呼び出すべきです。
use Laravel\Cashier\Cashier;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Cashier::keepPastDueSubscriptionsActive();
}
Note: あるサブクリプションに
incomplete
状態がある場合、支払いを確認するまでは変更できません。そのためサブクリプションがincomplete
状態では、swap
やupdateQuantity
メソッドは例外を投げます。
プラン変更
アプリケーションの購入済みユーザーが新しいサブスクリプションプランへ変更したくなるのはよくあるでしょう。ユーザーを新しいサブスクリプションに変更するには、swap
メソッドへプランの識別子を渡します。
$user = App\User::find(1);
$user->subscription('default')->swap('provider-plan-id');
ユーザーが試用期間中の場合、試用期間は継続します。また、そのプランに「購入数」が存在している場合、購入個数も継続します。
プランを変更し、ユーザーの現プランの試用期間をキャンセルする場合は、skipTrial
メソッドを使用します。
$user->subscription('default')
->skipTrial()
->swap('provider-plan-id');
次の支払いサイクルまで待つ代わりに、プランを変更時即時にインボイスを発行したい場合は、swapAndInvoice
メソッドを使用します。
$user = App\User::find(1);
$user->subscription('default')->swapAndInvoice('provider-plan-id');
按分課金
デフォルトでStripeはプランの変更時に按分課金(prorate)を行います。noProrate
メソッドは按分課金を行わずにサブスクリプションの更新を指定するために使用します。
$user->subscription('default')->noProrate()->swap('provider-plan-id');
サブスクリプションの按分課金についての情報は、Stripeドキュメントで確認してください。
購入数
購入数はサブスクリプションに影響をあたえることがあります。たとえば、あるアプリケーションで「ユーザーごと」に毎月10ドル課金している場合です。購入数を簡単に上げ下げするには、incrementQuantity
とdecrementQuantity
メソッドを使います。
$user = User::find(1);
$user->subscription('default')->incrementQuantity();
// 現在の購入数を5個増やす
$user->subscription('default')->incrementQuantity(5);
$user->subscription('default')->decrementQuantity();
// 現在の購入数を5個減らす
$user->subscription('default')->decrementQuantity(5);
もしくは特定の数量を設置するには、updateQuantity
メソッドを使ってください。
$user->subscription('default')->updateQuantity(10);
noProrate
メソッドは、按分課金を行わずにサブスクリプションの購入数を更新するために使用します。
$user->subscription('default')->noProrate()->updateQuantity(10);
サブスクリプション数の詳細については、Stripeドキュメントを読んでください。
サブスクリプションの税金
ユーザーが支払うサブスクリプションに対する税率を指定するには、BillableモデルへtaxPercentage
メソッドを実装し、小数点以下が1桁以内で、0から100までの数値を返します。
public function taxPercentage()
{
return 20;
}
taxPercentage
メソッドにより、モデルごとに税率を適用できるため、多くの州や国に渡るユーザーベースで税率を決める場合に便利です。
Note:
taxPercentage
メソッドは、サブスクリプションの課金時のみに適用されます。Cashierで「一回のみ」の支払いを行う場合は、税率を自分で適用する必要があります。
税率の同期
taxPercentage
が返すハードコードした値を変更する場合、ユーザーに対する既存のサブスクリプションは以前のままになります。taxPercentage
が返す値に既存のサブスクリプションも更新したい場合は、ユーザーのサブスクリプションインスタンスに対し、syncTaxPercentage
メソッドを呼び出す必要があります。
$user->subscription('default')->syncTaxPercentage();
サブスクリプション課金日付け
デフォルトで課金日はサブスクリプションが生成された日付け、もしくは使用期間を使っている場合は、使用期間の終了日です。課金日付を変更したい場合は、anchorBillingCycleOn
メソッドを使用します。
use App\User;
use Carbon\Carbon;
$user = User::find(1);
$anchor = Carbon::parse('first day of next month');
$user->newSubscription('default', 'premium')
->anchorBillingCycleOn($anchor->startOfDay())
->create($paymentMethod);
サブスクリプションの課金間隔を管理する情報は、Stripeの課金サイクルのドキュメントをお読みください。
サブスクリプションキャンセル
サブスクリプションをキャンセルするにはcancel
メソッドをユーザーのサブスクリプションに対して使ってください。
$user->subscription('default')->cancel();
サブスクリプションがキャンセルされるとCashierは自動的に、データベースのends_at
カラムをセットします。このカラムはいつからsubscribed
メソッドがfalse
を返し始めればよいのか、判定するために使用されています。たとえば、顧客が3月1日にキャンセルしたが、そのサブスクリプションが3月5日に終了するようにスケジュールされていれば、subscribed
メソッドは3月5日になるまでtrue
を返し続けます。
ユーザーがサブスクリプションをキャンセルしたが、まだ「猶予期間」が残っているかどうかを調べるにはonGracePeriod
メソッドを使います。
if ($user->subscription('default')->onGracePeriod()) {
//
}
サブスクリプションを即時キャンセルしたい場合は、ユーザーのサブスクリプションに対し、cancelNow
メソッドを呼び出してください。
$user->subscription('default')->cancelNow();
サブスクリプション再開
ユーザーがキャンセルしたサブスクリプションを、再開したいときには、resume
メソッドを使用してください。サブスクリプションを再開するには、そのユーザーに有効期間が残っている必要があります。
$user->subscription('default')->resume();
ユーザーがサブスクリプションをキャンセルし、それからそのサブスクリプションを再開する場合、そのサブスクリプションの有効期日が完全に切れていなければすぐに課金されません。そのサブスクリプションはシンプルに再度有効になり、元々の支払いサイクルにより課金されます。
サブスクリプションのトレイト
Note: Cashierのサブスクリプション試用期日はStripeのプランを使用せず、独自に管理しています。Cashierで試用期間を管理するため、Stripeのプランでは試用期間を0に設定する必要があります。
支払いの事前登録あり
顧客へ試用期間を提供し、支払情報を事前に登録してもらう場合、サブスクリプションを作成するときにtrialDays
メソッドを使ってください。
$user = User::find(1);
$user->newSubscription('default', 'monthly')
->trialDays(10)
->create($paymentMethod);
このメソッドはデータベースのサブスクリプションレコードへ、試用期間の終了日を設定すると同時に、Stripeへこの期日が過ぎるまで、顧客へ課金を始めないように指示します。trialDays
メソッドを使用する場合、Stripeでそのプランに対して設定したデフォルトの試用期間はオーバーライドされます。
Note: 顧客のサブスクリプションが試用期間の最後の日までにキャンセルされないと、期限が切れると同時に課金されます。そのため、ユーザーに試用期間の終了日を通知しておくべきでしょう。
trialUntil
メソッドにより、使用期間の終了時を指定する、DateTime
インスタンスを渡せます。
use Carbon\Carbon;
$user->newSubscription('default', 'monthly')
->trialUntil(Carbon::now()->addDays(10))
->create($paymentMethod);
ユーザーが使用期間中であるかを判定するには、ユーザーインスタンスに対しonTrial
メソッドを使うか、サブスクリプションインスタンスに対してonTrial
を使用してください。次の2つの例は、同じ目的を達します。
if ($user->onTrial('default')) {
//
}
if ($user->subscription('default')->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('default', 'monthly')->create($paymentMethod);
試用期限の延長
一度作成したあとに、サブスクリプションの試用期間を延長したい場合は、extendTrial
メソッドを使用します。
// 今から7日後に試用期限を終える
$subscription->extendTrial(
now()->addDays(7)
);
// 使用期限を5日間延長する
$subscription->extendTrial(
$subscription->trial_ends_at->addDays(5)
);
試用期間が過ぎ、顧客がサブスクリプションをすでに購入している場合でも、追加の試用期限を与えられます。試用期間で費やされた時間は、その顧客の次回のインボイスから差し引かれます。
StripeのWebフック処理
Tip!! ローカル環境でWebhooksのテストするには、Stripe CLIが役立つでしょう。
StripeはWebフックにより、アプリケーションへさまざまなイベントを通知できます。デフォルトで、CashierのWebhookを処理するルートのコントローラは、Cashierのサービスプロバイダで設定されています。このコントローラはWebhookの受信リクエストをすべて処理します。
デフォルトでこのコントローラは、課金に多く失敗し続ける(Stripeの設定で定義している回数)、顧客の更新、顧客の削除、サブスクリプションの変更、支払い方法の変更があると、自動的にサブスクリプションをキャンセル処理します。しかしながら、すぐに見つけることができるようにこのコントローラを拡張し、どんなWebhookイベントでもお好きに処理できます
アプリケーションでStripeのWebhookを処理するためには、StripeのコントロールパネルでWebhook URLを確実に設定してください。Stripeのコントロールパネルで設定する必要のあるWebhookの全リストは、以下のとおりです。
customer.subscription.updated
customer.subscription.deleted
customer.updated
customer.deleted
invoice.payment_action_required
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を受け取ると、Laravel\Cashier\Events\WebhookReceived
イベントを発行します。そして、WebhookがCashierにより処理されると、Laravel\Cashier\Events\WebhookHandled
イベントを発行します。両方のイベント共にStripeのWebhookの完全なペイロードを持っています。
サブスクリプション不可
顧客のクレジットカードが有効期限切れだったら? 心配いりません。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
のユーザーに対するcharge
、invoiceFor
、invoice
メソッドで投げられる可能性があります。スクリプションが処理される時、SubscriptionBuilder
のcreate
メソッドと、Subscription
モデルのincrementAndInvoice
、swapAndInvoice
メソッドは、例外を投げるでしょう。
不十分と期日超過の状態
支払いが追加の確認を必要とする場合そのサブクリプションは、stripe_status
データベースカラムにより表されるincomplete
かpast_due
状態になります。Cashierは支払いの確認が完了するとすぐに、Webhookによりその顧客のサブスクリプションを自動的に有効にします。
incomplete
とpast_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度行い、二重に課金されるアクシデントに陥ることを防ぐことができるでしょう。
Stripe SDK
CashierのオブジェクトはStripe
SDKオブジェクト上にラップされています。Stripe
SDKオブジェクトを直接操作したい場合は、asStripe
メソッドを使い簡単に取得できます。
$stripeSubscription = $subscription->asStripeSubscription();
$stripeSubscription->update($subscription->stripe_id, ['application_fee_percent' => 5]);