イントロダクション
Laravel Cashierは、Stripeのサブスクリプション課金サービスに対する表現力豊かで流暢なインターフェースを提供します。それはあなたが書くことを恐れている定型的なサブスクリプション請求コードのほとんどすべてを処理します。キャッシャーは、基本的なサブスクリプション管理に加えて、クーポン、サブスクリプションの交換、サブスクリプションの「数量」、キャンセルの猶予期間を処理し、請求書のPDFを生成することもできます。
Cashierのアップデート
キャッシャーの新しいバージョンにアップグレードするときは、アップグレードガイドを注意深く確認することが重要です。
Note: 重大な変更を防ぐために、Cashierは固定のStripe APIバージョンを使用します。キャッシャー12はStripeAPIバージョン
2020-03-02
を利用しています。StripeAPIバージョンは、新しいStripe機能と改善点を利用するために、マイナーリリースで更新されます。
インストール
まず、Composerパッケージマネージャーを使用して、StripeのCashierパッケージをインストールします。
composer require laravel/cashier
Note: CashierにすべてのStripeイベントを適切に処理させるには、キャッシャーのWebhook処理を忘れずに設定してください。
データベースマイグレーション
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;
/**
* 全アプリケーションサービスの登録
*
* @return void
*/
public function register()
{
Cashier::ignoreMigrations();
}
Note: Stripeは、Stripe識別子の格納に使用される列では大文字と小文字を区別することを推奨しています。したがって、MySQLを使用する場合は、
stripe_id
列の列照合順序を確実にutf8_bin
へ設定する必要があります。これに関する詳細は、Stripeドキュメントにあります。
設定
Billableモデル
Cashierを使用する前に、Billableなモデルの定義へBillable
トレイトを追加してください。通常、これはApp\Models\User
モデルになるでしょう。このトレイトはサブスクリプションの作成、クーポンの適用、支払い方法情報の更新など、一般的な請求タスクを実行できるようにするさまざまなメソッドを提供します。
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Cashierは、BlillableなモデルがLaravelに同梱されているApp\Models\User
クラスであると想定しています。これを変更したい場合は、.env
ファイルで別のモデルを指定できます。
CASHIER_MODEL=App\Models\User
Note: Laravelが提供する
App\Models\User
モデル以外のモデルを使用する場合は、代替モデルのテーブル名と一致するように、提供されているCashierマイグレーションをリソース公開し、変更する必要があります。
APIキー
次に、アプリケーションの.env
ファイルでStripeAPIキーを設定する必要があります。StripeAPIキーはStripeコントロールパネルから取得できます。
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
通貨設定
デフォルトのCashier通貨は米ドル(USD)です。アプリケーションの.env
ファイル内でCASHIER_CURRENCY
環境変数を設定することにより、デフォルトの通貨を変更できます。
CASHIER_CURRENCY=eur
Cashierの通貨の設定に加え、請求書に表示するの金額の値をフォーマットするときに使用するロケールを指定することもできます。内部的には、CashierはPHPのNumberFormatter
クラスを利用して通貨ロケールを設定します。
CASHIER_CURRENCY_LOCALE=nl_BE
Note:
en
以外のロケールを使用するには、ext-intl
PHP拡張機能を確実にサーバーにインストールおよび設定してください。
ログ
Cashierを使用すると、Stripe関連のすべての例外をログに記録するときに使用するログチャネルを指定できます。アプリケーションの.env
ファイル内でCASHIER_LOGGER
環境変数を定義することにより、ログチャネルを指定できます。
CASHIER_LOGGER=stack
顧客
顧客の取得
Cashier::findBillable
メソッドを使用して、StripeIDで顧客を取得できます。このメソッドは、Billableなモデルのインスタンスを返します。
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
顧客の作成
まれに、サブスクリプションを開始せずにStripeの顧客を作成したいことも起きるでしょう。これは、createAsStripeCustomer
メソッドを使用して実行できます。
$stripeCustomer = $user->createAsStripeCustomer();
Stripeで顧客を作成しておき、あとからサブスクリプションを開始できます。オプションの$options
配列を指定して、追加のStripeAPIでサポートされている顧客作成パラメーターを渡せます。
$stripeCustomer = $user->createAsStripeCustomer($options);
BillableなモデルのStripe顧客オブジェクトを返す場合は、asStripeCustomer
メソッドを使用できます。
$stripeCustomer = $user->asStripeCustomer();
createOrGetStripeCustomer
メソッドは、特定のBillableモデルのStripe顧客オブジェクトを取得したいが、BillableモデルがすでにStripe内の顧客であるかわからない場合に使用できます。このメソッドは、Stripeに新しい顧客がまだ存在しない場合、新しい顧客を作成します。
$stripeCustomer = $user->createOrGetStripeCustomer();
顧客の更新
たまに、Stripeの顧客を追加情報と一緒に直接更新したい状況が起こるでしょう。これは、updateStripeCustomer
メソッドを使用して実行できます。このメソッドは、StripeAPIでサポートされている顧客更新オプションの配列を引数に取ります。
$stripeCustomer = $user->updateStripeCustomer($options);
請求ポータル
Stripeは、請求ポータルを設定する簡単な方法を提供しており、顧客はサブスクリプション、支払い方法を管理し、請求履歴を表示できます。コントローラまたはルートからBillableモデルでredirectToBillingPortal
メソッドを呼び出すことにより、ユーザーを請求ポータルにリダイレクトできます。
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});
デフォルトでは、ユーザーがサブスクリプションの管理を終了すると、Stripe課金ポータル内のリンクを介してアプリケーションのhome
ルートに戻ることができます。redirectToBillingPortal
メソッドに引数としてURLを渡すことにより、ユーザーが戻る必要のあるカスタムURLを指定できます。
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('billing'));
});
HTTPリダイレクトレスポンスを生成せずに課金ポータルへのURLを生成したい場合は、billingPortalUrl
メソッドを呼び出してください。
$url = $request->user()->billingPortalUrl(route('billing'));
支払い方法
支払い方法の保存
Stripeでサブスクリプションを作成したり、「1回限りの」料金を実行したりするには、支払い方法を保存し、Stripeからその識別子を取得する必要があります。これを実現するアプローチは、サブスクリプションに支払い方法を使用するか、単一料金を使用するかにより異なるため、以降で両方とも説明します。
サブスクリプションの支払い方法
サブスクリプションで将来使用するときのため顧客のクレジットカード情報を保存する場合、Stripeの"Setup Intents" APIを使用して、顧客の支払い方法の詳細を安全に収集する必要があります。"Setup Intent "は、顧客の支払い方法により課金する意図をStripeに示します。CashierのBillable
トレイトには、新しいセットアップインテントを簡単に作成するためのcreateSetupIntent
メソッドを用意しています。顧客の支払い方法の詳細を収集するフォームをレンダーするルートまたはコントローラーからこのメソッドを呼び出す必要があります。
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
]);
セットアップインテントを作成してビューに渡したら、支払い方法を収集する要素にそのシークレットを添付する必要があります。たとえば、次の「支払い方法の更新」フォームについて考えてみます。
<input id="card-holder-name" type="text">
<!-- ストライプ要素のプレースホルダ -->
<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!! セットアップインテントと顧客の支払いの詳細の収集について詳しく知りたい場合は、Stripeが提供しているこの概要を確認してください。
一回限りの支払いの支払い方法
もちろん、お客様の支払い方法に対して1回請求を行う場合、支払い方法IDを使用する必要があるのは1回だけです。Stripeの制約により、顧客が保存したデフォルトの支払い方法を単一の請求に使用することはできません。Stripe.jsライブラリを使用して、顧客が支払い方法の詳細を入力できるようにする必要があります。たとえば、次のフォームについて考えてみます。
<input id="card-holder-name" type="text">
<!-- ストライプ要素プレースホルダ -->
<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から安全な「支払い方法識別子」を取得します。
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アプリケーションに渡して、一回限りの請求を処理できます。
支払い方法の取得
BillableなモデルインスタンスのpaymentMethods
メソッドは、Laravel\Cashier\PaymentMethod
インスタンスのコレクションを返します。
$paymentMethods = $user->paymentMethods();
顧客のデフォルトの支払い方法を取得するには、defaultPaymentMethod
メソッドを使用します。
$paymentMethod = $user->defaultPaymentMethod();
findPaymentMethod
メソッドを使用して、Billableモデルに関連付けている特定の支払い方法を取得できます。
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
顧客に支払い方法があるか判定
Billableなモデルのアカウントにデフォルトの支払い方法が関連付けられているかどうかを確認するには、hasDefaultPaymentMethod
メソッドを呼び出します。
if ($user->hasDefaultPaymentMethod()) {
//
}
hasPaymentMethod
メソッドを使用して、Billableなモデルのアカウントに少なくとも1つの支払い方法が関連付けられているかを判定できます。
if ($user->hasPaymentMethod()) {
//
}
デフォルト支払い方法の変更
updateDefaultPaymentMethod
メソッドは、顧客のデフォルトの支払い方法情報を更新するために使用します。このメソッドは、Stripe支払い方法識別子を受け入れ、新しい支払い方法をデフォルトの請求支払い方法として割り当てます。
$user->updateDefaultPaymentMethod($paymentMethod);
デフォルトの支払い方法情報をStripeの顧客のデフォルトの支払い方法情報と同期するには、updateDefaultPaymentMethodFromStripe
メソッドを使用できます。
$user->updateDefaultPaymentMethodFromStripe();
Note: 顧客のデフォルトの支払い方法は、新しいサブスクリプションの請求と作成にのみ使用できます。Stripeの制約により、1回だけの請求には使用できません。
支払い方法の追加
新しい支払い方法を追加するには、支払い方法IDを渡して、BillableモデルでaddPaymentMethod
メソッドを呼び出してください。
$user->addPaymentMethod($paymentMethod);
Tip!! 支払い方法の識別子を取得する方法については、支払い方法の保存に関するドキュメントを確認してください。
支払い方法の削除
支払い方法を削除するには、削除したいLaravel\Cashier\PaymentMethod
インスタンスでdelete
メソッドを呼び出してください。
$paymentMethod->delete();
deletePaymentMethods
メソッドは、Billableなモデルのすべての支払い方法情報を削除します。
$user->deletePaymentMethods();
Note: ユーザーがアクティブなサブスクリプションを持っている場合、アプリケーションはユーザーがデフォルトの支払い方法を削除することを許してはいけません。
サブスクリプション
サブスクリプションは、顧客に定期的な支払いを設定する方法を提供します。Cashierが管理するStripeサブスクリプションは、複数のサブスクリプションプラン、サブスクリプション数量、トライアルなどをサポートします。
サブスクリプションの作成
サブスクリプションを作成するには、最初にBillableなモデルのインスタンスを取得します。これは通常、App\Models\User
のインスタンスになります。モデルインスタンスを取得したら、newSubscription
メソッドを使用してモデルのサブスクリプションを作成できます。
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription(
'default', 'price_premium'
)->create($request->paymentMethodId);
// ...
});
newSubscription
メソッドに渡す最初の引数は、サブスクリプションの名前です。アプリケーションが単一のサブスクリプションのみを提供する場合は、これをdefault
またはprimary
と付けるのが良いでしょう。2番目の引数は、ユーザーがサブスクライブしている特定のプランです。この値は、Stripeのプランの価格識別子に対応している必要があります。
Stripe支払い方法識別子またはStripePaymentMethod
オブジェクトを引数に取るcreate
メソッドは、サブスクリプションを開始するのと同時に、BillableなモデルのStripe顧客IDおよびその他の関連する課金情報でデータベースを更新します。
Note: 支払い方法識別子を
create
サブスクリプションメソッドへ直接渡すと、ユーザーの保存済み支払い方法にも自動的に追加されます。
サブスクリプション数
サブスクリプションの作成時にプランに数量を指定する場合は、サブスクリプションを作成する前にビルダーでquantity
メソッドを呼び出してください。
$user->newSubscription('default', 'price_monthly')
->quantity(5)
->create($paymentMethod);
詳細の追加
Stripeがサポートしている顧客やサブスクリプションオプションを指定する場合、create
メソッドの2番目と3番目の引数に指定することで、追加できます。
$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
], [
'metadata' => ['note' => 'Some extra information.'],
]);
クーポン
サブスクリプションの作成時にクーポンを適用する場合は、withCoupon
メソッドを使用できます。
$user->newSubscription('default', 'price_monthly')
->withCoupon('code')
->create($paymentMethod);
または、Stripeプロモーションコードを適用する場合は、withPromotionCode
メソッドを使用します。
$user->newSubscription('default', 'price_monthly')
->withPromotionCode('promo_code')
->create($paymentMethod);
サブスクリプションの追加
すでにデフォルトの支払い方法を持っている顧客へサブスクリプションを追加する場合は、サブスクリプションビルダーでadd
メソッドを呼び出してください。
use App\Models\User;
$user = User::find(1);
$user->newSubscription('default', 'price_premium')->add();
サブスクリプション状態のチェック
顧客がアプリケーションでサブスクリプションを購入すると、さまざまな便利なメソッドを使用して、サブスクリプションの状態を簡単に確認できます。まず、subscribed
メソッドは、そのサブスクリプションが現在試用期間内であっても、顧客がアクティブなサブスクリプションを持っている場合は、true
を返します。subscribed
メソッドは、最初の引数としてサブスクリプションの名前を受け入れます。
if ($user->subscribed('default')) {
//
}
subscribed
メソッドはルートミドルウェアの優れた候補にもなり、ユーザーのサブスクリプション状態に基づいてルートとコントローラーへのアクセスをフィルタリングできます。
<?php
namespace App\Http\Middleware;
use Closure;
class EnsureUserIsSubscribed
{
/**
* 受信リクエストの処理
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
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プランの価格識別子に基づいてユーザーが特定プランのサブスクリプションを購入しているかを判定できます。この例では、ユーザーのdefault
サブスクリプションがアプリケーションの「月次(monthly)」プランにアクティブにサブスクライブされているかどうかを判別しています。指定したStripeプランの価格IDは、Stripeダッシュボードのプランの価格IDの1つに対応している必要があります。
if ($user->subscribedToPlan('price_monthly', 'default')) {
//
}
配列をsubscribedToPlan
メソッドに渡し、ユーザーのdefault
サブスクリプションがアプリケーションの「月次(monthly)」か「年次(yearly)」プランにアクティブにサブスクリプション購入されているかを判定できます。
if ($user->subscribedToPlan(['price_monthly', 'price_yearly'], 'default')) {
//
}
recurring
メソッドを使用して、ユーザーが現在サブスクリプションを購入しており、試用期間でないことを判定します。
if ($user->subscription('default')->recurring()) {
//
}
Note: ユーザーが同じ名前のサブスクリプションを2つ持っている場合、
subscription
メソッドは最新のサブスクリプションを常に返します。たとえば、ユーザーがdefault
という名前のサブスクリプションレコードを2つ持っているとします。この場合、サブスクリプションの1つは古い期限切れのサブスクリプションであり、もう1つは現在のアクティブなサブスクリプションである可能性があります。最新のサブスクリプションを常に返しますが、古いサブスクリプションは履歴レビューのためにデータベースに保持されます。
キャンセル済みサブスクリプション状態
ユーザーがかつてアクティブなサブスクリプション購入者であったが、そのサブスクリプションをキャンセルしたかを判定するには、cancelled
メソッドを使用します。
if ($user->subscription('default')->cancelled()) {
//
}
また、ユーザーがサブスクリプションをキャンセルしたが、サブスクリプションが完全に期限切れになるまで「猶予期間」にあるかを判定することもできます。たとえば、ユーザーが3月10日に期限切れになる予定のサブスクリプションを3月5日にキャンセルした場合、ユーザーは3月10日まで「猶予期間」になります。この間、subscribed
メソッドは引き続きtrue
を返すことに注意してください。
if ($user->subscription('default')->onGracePeriod()) {
//
}
ユーザーがサブスクリプションをキャンセルし、「猶予期間」も過ぎていることを判断するには、ended
メソッドを使用します。
if ($user->subscription('default')->ended()) {
//
}
不完全および延滞ステータス
サブスクリプションの作成後に二次支払いアクションが必要な場合、サブスクリプションは「未完了(incomplete
)」としてマークされます。サブスクリプションステータスは、Cashierのsubscriptions
データベーステーブルのstripe_status
列に保存されます。
同様に、プランを変更するときに二次支払いアクションが必要な場合、サブスクリプションは「延滞(past_due
)」としてマークされます。サブスクリプションがこれらの状態のどちらかにある場合、顧客が支払いを確認するまでサブスクリプションはアクティブになりません。サブスクリプションの支払いが不完全であるかの判定は、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
メソッドを使用します。通常、このメソッドは、App\Providers\AppServiceProvider
のregister
メソッドで呼び出す必要があります。
use Laravel\Cashier\Cashier;
/**
* 全アプリケーションサービスの登録
*
* @return void
*/
public function register()
{
Cashier::keepPastDueSubscriptionsActive();
}
Note: サブスクリプションが
incomplete
状態の場合、支払いが確認されるまで変更できません。したがって、サブスクリプションがincomplete
状態の場合、swap
メソッドとupdateQuantity
メソッドは例外を投げます。
サブスクリプションのスコープ
ほとんどのサブスクリプション状態はクエリスコープとしても利用できるため、特定の状態にあるサブスクリプションはデータベースで簡単にクエリできます。
// すべてのアクティブなサブスクリプションを取得
$subscriptions = Subscription::query()->active()->get();
// ユーザーのキャンセルしたサブスクリプションをすべて取得
$subscriptions = $user->subscriptions()->cancelled()->get();
使用可能なスコープの完全なリストは、以下のとおりです。
Subscription::query()->active();
Subscription::query()->cancelled();
Subscription::query()->ended();
Subscription::query()->incomplete();
Subscription::query()->notCancelled();
Subscription::query()->notOnGracePeriod();
Subscription::query()->notOnTrial();
Subscription::query()->onGracePeriod();
Subscription::query()->onTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();
プランの変更
顧客がアプリケーションでサブスクリプションを購入した後に、新しいサブスクリプションプランへ変更したい場合もあるでしょう。顧客を新しいプランへスワップするには、Stripeプランの価格識別子をswap
メソッドに渡します。指定された価格IDは、Stripeダッシュボードで使用可能なStripeプランの価格IDに対応している必要があります。
use App\Models\User;
$user = App\Models\User::find(1);
$user->subscription('default')->swap('price_id');
その顧客が試用中の場合、試用期間は維持されます。また、サブスクリプションに「数量」が存在する場合、その数量も維持されます。
プランを交換して、顧客が現在行っている試用期間をキャンセルしたい場合は、skipTrial
メソッドを呼び出してください。
$user->subscription('default')
->skipTrial()
->swap('price_id');
プランを交換して、次の請求サイクルを待たずにすぐに顧客に請求する場合は、swapAndInvoice
メソッドを使用します。
$user = User::find(1);
$user->subscription('default')->swapAndInvoice('price_id');
按分
デフォルトで、Stripeはプランを変更するときに料金を按分します。noProrate
メソッドを使用すると、料金を按分せずにサブスクリプションのプランを更新できます。
$user->subscription('default')->noProrate()->swap('price_id');
サブスクリプションの按分について詳しくは、Stripeドキュメントを参照してください。
Note:
swapAndInvoice
メソッドの前にnoProrate
メソッドを実行しても、按分には影響しません。請求書は常に発行されます。
サブスクリプション数
サブスクリプションは「数量」の影響を受ける場合があります。たとえば、プロジェクト管理アプリケーションは、プロジェクトごとに月額$10を請求する場合があります。incrementQuantity
メソッドとdecrementQuantity
メソッドを使用して、サブスクリプション数量を簡単に増減できます。
use App\Models\User;
$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ドキュメントを参照してください。
マルチプランサブスクリプションの数量
サブスクリプションがマルチプランサブスクリプションの場合、増減時のの2番目の引数として、数量をインクリメントまたはデクリメントするプランの名前を渡す必要があります。
$user->subscription('default')->incrementQuantity(1, 'chat-plan');
マルチプランサブスクリプション
マルチプランサブスクリプションでは、1つのサブスクリプションに複数の課金プランを割り当てることができます。たとえば、基本サブスクリプションプランが月額$10のカスタマーサービス「ヘルプデスク」アプリケーションを構築しているとします。月額$15のライブチャットアドオン追加プランも提供しているとしましょう。マルチプランのサブスクリプション情報は、Cashierのsubscription_items
データベーステーブルへ保存されます。
newSubscription
メソッドの2番目の引数にプランの配列を渡すことにより、特定のサブスクリプションへ複数のプランを指定できます。
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription('default', [
'price_monthly',
'chat-plan',
])->create($request->paymentMethodId);
// ...
});
上記の例では、顧客はdefault
サブスクリプションに2つのプランを添付しています。どちらのプランも、それぞれの請求間隔で請求されます。必要に応じて、quantity
メソッドを使用して、各プランの個数を指定できます。
$user = User::find(1);
$user->newSubscription('default', ['price_monthly', 'chat-plan'])
->quantity(5, 'chat-plan')
->create($paymentMethod);
既存のサブスクリプションに別のプランを追加する場合は、サブスクリプションのaddPlan
メソッドを呼び出すことができます。
$user = User::find(1);
$user->subscription('default')->addPlan('chat-plan');
上記の例では、新しいプランを追加し、顧客は次の請求サイクルでその料金を請求されます。すぐに顧客に請求したい場合は、addPlanAndInvoice
メソッドを使用できます。
$user->subscription('default')->addPlanAndInvoice('chat-plan');
プランを追加する個数も指定する場合は、addPlan
またはaddPlanAndInvoice
メソッドの2番目の引数として数量を渡すことができます。
$user = User::find(1);
$user->subscription('default')->addPlan('chat-plan', 5);
removePlan
メソッドを使用してサブスクリプションからプランを削除できます。
$user->subscription('default')->removePlan('chat-plan');
Note: サブスクリプションの最後のプランを削除することはできません。代わりに、単にサブスクリプションをキャンセルする必要があります。
プランの変更
マルチプランサブスクリプションに添付されているプランを変更することもできます。たとえば、顧客がchat-plan
アドオンプランを備えたbasic-plan
サブスクリプションを持っていて、顧客をbasic-plan
からpro-plan
プランにアップグレードしたいとします。
use App\Models\User;
$user = User::find(1);
$user->subscription('default')->swap(['pro-plan', 'chat-plan']);
上記の例を実行すると、basic-plan
を含む基になるサブスクリプションアイテムが削除され、chat-plan
を含むサブスクリプションアイテムが保持されます。さらに、pro-plan
の新しいサブスクリプションアイテムが作成されます。
キー/値ペアの配列をswap
メソッドに渡すことで、サブスクリプションアイテムのオプションを指定することもできます。たとえば、サブスクリプションプランの個数を指定する必要があるとしましょう。
$user = User::find(1);
$user->subscription('default')->swap([
'pro-plan' => ['quantity' => 5],
'chat-plan'
]);
あるサブスクリプションを単一のプランへ交換する場合は、そのサブスクリプションアイテム自体に対しswap
メソッドを使用できます。このアプローチは、そのサブスクリプションの他のプランが持つ既存のメタデータをすべて保持する場合に特に役立ちます。
$user = User::find(1);
$user->subscription('default')
->findItemOrFail('basic-plan')
->swap('pro-plan');
按分
デフォルトで、Stripeはマルチプランサブスクリプションにプランを追加または削除するとき、料金を按分します。按分せずに調整したい場合は、noProrate
メソッドをプランの操作へチェーンする必要があります。
$user->subscription('default')->noProrate()->removePlan('chat-plan');
個数
個々のサブスクリプションプランの個数を変更する場合は、既存の個数メソッドを使用して、プランの名前をメソッドの追加引数として渡すことで更新できます。
$user = User::find(1);
$user->subscription('default')->incrementQuantity(5, 'chat-plan');
$user->subscription('default')->decrementQuantity(3, 'chat-plan');
$user->subscription('default')->updateQuantity(10, 'chat-plan');
Note: サブスクリプションに複数のプランがある場合、
Subscription
モデルのstripe_plan
属性とquantity
属性はnull
になります。個々のプラン属性にアクセスするには、Subscription
モデルで使用可能なitems
リレーションを使用する必要があります。
サブスクリプションアイテム
サブスクリプションに複数のプランがある場合、データベースのsubscription_items
テーブルに複数のサブスクリプション「アイテム」が保存されます。サブスクリプションのitems
リレーションを介してこれらにアクセスできます。
use App\Models\User;
$user = User::find(1);
$subscriptionItem = $user->subscription('default')->items->first();
// 特定のアイテムのStripeプランと個数を取得
$stripePlan = $subscriptionItem->stripe_plan;
$quantity = $subscriptionItem->quantity;
findItemOrFail
メソッドを使用して特定のプランを取得できます。
$user = User::find(1);
$subscriptionItem = $user->subscription('default')->findItemOrFail('chat-plan');
サブスクリプションの税率
ユーザーがサブスクリプションで支払う税率を指定するには、BillableなモデルにtaxRates
メソッドを実装し、Stripe税率IDを含む配列を返す必要があります。これらの税率は、Stripeダッシュボードで定義します。
/**
* 顧客のサブスクリプションに適用する税率
*
* @return array
*/
public function taxRates()
{
return ['tax-rate-id'];
}
taxRates
メソッドを使用すると、顧客ごとに税率を適用できます。これは、ユーザーベースが複数の国と税率にまたがる場合に役立つでしょう。
マルチプランサブスクリプションを提供している場合は、BillableなモデルにplanTaxRates
メソッドを実装することで、プランごとに異なる税率を定義できます。
/**
* 顧客のサブスクリプションに適用する税率
*
* @return array
*/
public function planTaxRates()
{
return [
'plan-id' => ['tax-rate-id'],
];
}
Note:
taxRates
メソッドはサブスクリプション料金にのみ適用されます。Cashierを使用して「1回限り」の料金を請求する場合は、その時点で税率を手動で指定する必要があります。
税率の同期
taxRates
メソッドから返すハードコードした税率IDを変更する場合、ユーザーの既存サブスクリプションの設定税率は同じままです。既存のサブスクリプションの税額を新しいtaxRates
値で更新する場合は、ユーザーのサブスクリプションインスタンスでsyncTaxRates
メソッドを呼び出す必要があります。
$user->subscription('default')->syncTaxRates();
これにより、マルチプランサブスクリプションアイテムの税率も同期されます。アプリケーションがマルチプランサブスクリプションを提供している場合は、前で説明したように、Billableなモデルで確実にplanTaxRates
メソッドを実装する必要があります。
非課税
Cashierは、顧客が非課税であるかどうかを判断するために、isNotTaxExempt
、isTaxExempt
、reverseChargeApplies
メソッドも提供しています。これらのメソッドは、StripeAPIを呼び出して、顧客の免税ステータスを判定します。
use App\Models\User;
$user = User::find(1);
$user->isTaxExempt();
$user->isNotTaxExempt();
$user->reverseChargeApplies();
Note: これらのメソッドは、すべての
Laravel\Cashier\Invoice
オブジェクトでも使用できます。ただし、Invoice
オブジェクトで呼び出されると、メソッドは請求書が作成されたときの免除ステータスを用います。
サブスクリプション基準日
デフォルトの請求サイクル基準日は、サブスクリプションが作成された日付、または試用期間が使用されている場合は試用が終了した日付です。請求基準日を変更する場合は、anchorBillingCycleOn
メソッドを使用します。
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$anchor = Carbon::parse('first day of next month');
$request->user()->newSubscription('default', 'price_premium')
->anchorBillingCycleOn($anchor->startOfDay())
->create($request->paymentMethodId);
// ...
});
サブスクリプションの請求サイクルの管理の詳細については、Stripe請求サイクルのドキュメントを参照してください。
サブスクリプションの取り消し
サブスクリプションをキャンセルするには、ユーザーのサブスクリプションでcancel
メソッドを呼び出します。
$user->subscription('default')->cancel();
サブスクリプションがキャンセルされると、Cashierは自動的にsubscriptions
データベーステーブルの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();
顧客がサブスクリプションをキャンセルし、サブスクリプションが完全に期限切れになる前にそのサブスクリプションを再開する場合、請求はすぐに顧客へ課せられれません。代わりに、サブスクリプションを再アクティブ化し、元の請求サイクルで請求します。
サブスクリプションの試用期間
支払い方法の事前登録
事前に支払い方法情報を収集し、顧客に試用期間を提供したい場合は、サブスクリプションの作成時にtrialDays
メソッドを使用します。
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription('default', 'price_monthly')
->trialDays(10)
->create($request->paymentMethodId);
// ...
});
このメソッドは、データベース内のサブスクリプションレコードに試用期間の終了日を設定し、この日付が過ぎるまで顧客への請求を開始しないようにStripeに指示します。trialDays
メソッドを使用すると、CashierはStripeのプランに設定しているデフォルトの試用期間を上書きします。
Note: 試用期間の終了日より前に顧客のサブスクリプションがキャンセルされなかった場合、試用期間の終了時にすぐ課金されるため、試用期間の終了日をユーザーに必ず通知する必要があります。
trialUntil
メソッドを使用すると、試用期間をいつ終了するかを指定するDateTime
インスタンスを渡せます。
use Carbon\Carbon;
$user->newSubscription('default', 'price_monthly')
->trialUntil(Carbon::now()->addDays(10))
->create($paymentMethod);
ユーザーインスタンスのonTrial
メソッドまたはサブスクリプションインスタンスのonTrial
メソッドのいずれかを使用して、ユーザーが試用期間内にあるかどうか判定できます。以下の2例は同じ働きです。
if ($user->onTrial('default')) {
//
}
if ($user->subscription('default')->onTrial()) {
//
}
ストライプ/Cashierでの試用期間日数の定義
プランが受け取るトライアル日数をStripeダッシュボードで定義するか、常にCashierを使用して明示的に渡すかを選択できます。Stripeでプランの試用日数を定義することを選択した場合、過去にそのサブスクリプションを購読していた顧客の新しいサブスクリプションを含む、新しいサブスクリプションは全部、明示的にtrialDays(0)
メソッドを呼び出さない限り、常に試用期間が提供されることに注意してください。
支払い方法事前登録なし
ユーザーの支払い方法情報を事前に収集せずに試用期間を提供したい場合は、ユーザーレコードのtrial_ends_at
列を希望の試用終了日に設定してください。これは通常、ユーザー登録時に行います。
use App\Models\User;
$user = User::create([
// ...
'trial_ends_at' => now()->addDays(10),
]);
Note: Billableなモデルのクラス定義内の
trial_ends_at
属性にdatecastを必ず追加してください。
Cashierはこのタイプの試用期間を「一般的な試用期間(generic trial)」と呼んでいます。これは、既存のサブスクリプションに関連付けられていないからです。BillableなモデルインスタンスのonTrial
メソッドは、現在の日付がtrial_ends_at
の値を超えていない場合にtrue
を返します。
if ($user->onTrial()) {
// ユーザーは試用期間内
}
ユーザーの実際のサブスクリプションを作成する準備ができたら、通常どおりnewSubscription
メソッドを使用できます。
$user = User::find(1);
$user->newSubscription('default', 'price_monthly')->create($paymentMethod);
ユーザーの試用終了日を取得するには、trialEndsAt
メソッドを使用します。このメソッドは、ユーザーが試用中の場合はCarbon日付インスタンスを返し、そうでない場合はnull
を返します。デフォルト以外の特定のサブスクリプションの試用終了日を取得する場合は、オプションのサブスクリプション名パラメーターを渡すこともできます。
if ($user->onTrial()) {
$trialEndsAt = $user->trialEndsAt('main');
}
ユーザーが「一般的な」試用期間内であり、実際のサブスクリプションをまだ作成していないことを具体的に知りたい場合は、onGenericTrial
メソッドを使用することもできます。
if ($user->onGenericTrial()) {
// ユーザーは「一般的な」試用期間内
}
試用期間の延長
extendTrial
メソッドを使用すると、サブスクリプションの作成後にサブスクリプションの試用期間を延長できます。試用期間がすでに終了していて、顧客にサブスクリプションの料金が既に請求されている場合でも、延長試用期間を提供できます。試用期間内に費やされた時間は、その顧客の次の請求から差し引かれます。
use App\Models\User;
$subscription = User::find(1)->subscription('default');
// 今から7日後に試用期間終了
$subscription->extendTrial(
now()->addDays(7)
);
// 試用期間をさらに5日追加
$subscription->extendTrial(
$subscription->trial_ends_at->addDays(5)
);
StripeのWebフックの処理
Tip!! Stripe CLIを使用して、ローカル開発中にWebhookをテストすることができます。
Stripeは、Webフックを介してさまざまなイベントをアプリケーションに通知できます。デフォルトでは、CashierのWebフックコントローラーを指すルートは、Cashierサービスプロバイダーにより自動的に登録されます。このコントローラーは、すべての受信Webフックリクエストを処理します。
デフォルトでは、Cashier Webフックコントローラーは、(Stripe設定で定義する)課金失敗が多すぎるサブスクリプションのキャンセル、顧客の更新、顧客の削除、サブスクリプションの更新、および支払い方法の変更を自動的に処理します。ただし、すぐにわかりますが、このコントローラーを拡張して、任意のStripe Webフックイベントを処理できます。
アプリケーションがStripe Webフックを処理できるようにするには、StripeコントロールパネルでWebフックURLを設定してください。デフォルトでは、CashierのWebフックコントローラーは/stripe/webhook
URLパスに応答します。Stripeコントロールパネルで有効にする必要があるすべてのWebフックの完全なリストは次のとおりです。
customer.subscription.updated
customer.subscription.deleted
customer.updated
customer.deleted
invoice.payment_action_required
Note: Cashierが持っているWebフック署名検証ミドルウェアを使用して、受信Stripe Webフックリクエストを保護してください。
WebフックとCSRF保護
Stripe WebフックはLaravelのCSRF保護をバイパスする必要があるため、アプリケーションのApp\Http\Middleware\VerifyCsrfToken
ミドルウェアに例外としてURIをリストするか、web
ミドルウェアグループの外にルートをリストしてください。
protected $except = [
'stripe/*',
];
Webフックイベントハンドラの定義
Cashierは、失敗した請求やその他の一般的なStripe Webフックイベントのサブスクリプションを自動的にキャンセル処理します。ただし、思い通りに処理したいWebフックイベントがある場合は、Cashier Webフックコントローラを拡張し処理できます。
コントローラのメソッド名は、Cashierのコントローラの規則に対応している必要があります。具体的には、メソッドのプリフィックスとしてhandle
と、処理する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
ファイル内でCashier Webフックコントローラのルートを定義します。これにより、Cashierのサービスプロバイダが登録したデフォルトルートが上書きされます。
use App\Http\Controllers\WebhookController;
Route::post(
'/stripe/webhook',
[WebhookController::class, 'handleWebhook']
);
Tip!! Cashierは、Webフックを受信すると
Laravel\Cashier\Events\WebhookReceived
イベントを発行し、WebフックがCashierによって処理されるとLaravel\Cashier\Events\WebhookHandled
イベントを発行します。どちらのイベントにも、Stripe Webフックの全ペイロードが含まれています。
Webフック署名の確認
Webフックを保護するために、StripeのWebhook署名を使用できます。便利なように、Cashierには受信Stripe Webフックリクエストが有効であるかを検証するミドルウェアが自動的に含まれています。
Webフックの検証を有効にするには、STRIPE_WEBHOOK_SECRET
環境変数がアプリケーションの.env
ファイルに設定されていることを確認してください。Webフックのsecret
は、Stripeアカウントダッシュボードから取得できます。
一回限りの支払い
シンプルな支払い
Note:
charge
メソッドは、アプリケーションで使用する通貨の最小単位で請求する金額を受け入れます。たとえば、米ドルを使用する場合、金額はペニーで指定する必要があります。
顧客に対して1回限りの請求を行う場合は、Billableなモデルインスタンスでcharge
メソッドを使用します。charge
メソッドの2番目の引数として支払い方法識別子を指定する必要があります。
use Illuminate\Http\Request;
Route::post('/purchase', function (Request $request) {
$stripeCharge = $request->user()->charge(
100, $request->paymentMethodId
);
// ...
});
charge
メソッドは3番目の引数を取り、ベースにあるStripeの課金作成へのオプションを指定できます。課金作成時に利用できるオプションの詳細は、Stripeドキュメントを参照してください。
$user->charge(100, $paymentMethod, [
'custom_option' => $value,
]);
基礎となる顧客やユーザーなしでもcharge
メソッドを使用きます。これには、アプリケーションのBillableモデルの新しいインスタンスでcharge
メソッドを呼び出します。
use App\Models\User;
$stripeCharge = (new User)->charge(100, $paymentMethod);
課金失敗の場合、charge
メソッドは例外を投げます。課金が成功すると、メソッドはLaravel\Cashier\Payment
のインスタンスを返します。
try {
$payment = $user->charge(100, $paymentMethod);
} catch (Exception $e) {
//
}
インボイス付きの支払い
時に1回限りの請求を行い、PDFレシートを顧客に提供する必要がおきるでしょう。invoiceFor
メソッドを使用すると、まさにそれを実行できます。たとえば、「メンテナンス料金」を顧客に$5.00請求するとします。
$user->invoiceFor('One Time Fee', 500);
インボイスは、ユーザーのデフォルトの支払い方法に対し即時請求されます。invoiceFor
メソッドは3番目の引数として配列も受け入れます。この配列には、インボイスアイテムの請求オプションを指定します。メソッドの4番目の引数も配列で、インボイス自体の請求オプションを指定します。
$user->invoiceFor('Stickers', 500, [
'quantity' => 50,
], [
'default_tax_rates' => ['tax-rate-id'],
]);
Note:
invoiceFor
メソッドは、失敗した請求を再試行するStripeインボイスを作成します。課金に失敗した請求を再試行したくない場合は、最初の失敗した請求の後にStripe APIを使用し、インボイスをを閉じる必要があります。
支払いの払い戻し
Stripeの料金を払い戻す必要がある場合は、refund
メソッドを使用します。このメソッドは、最初の引数にStripe支払いインテントIDを取ります。
$payment = $user->charge(100, $paymentMethodId);
$user->refund($payment->id);
インボイス
インボイスの取得
invoices
メソッドを使用して、Billableなモデルのインボイスの配列を簡単に取得できます。invoices
メソッドはLaravel\Cashier\Invoice
インスタンスのコレクションを返します。
$invoices = $user->invoices();
結果に保留中のインボイスを含めたい場合は、invoicesInclusivePending
メソッドを使用します。
$invoices = $user->invoicesIncludingPending();
findInvoice
メソッドを使用して、IDで特定のインボイスを取得できます。
$invoice = $user->findInvoice($invoiceId);
インボイス情報の表示
ある顧客のインボイスを一覧表示する場合、インボイスのメソッドを使用し関連するインボイス情報を表示すると思います。たとえば、テーブル中の全インボイスをリストする場合、ユーザーがどれでも簡単にダウンロードできるようにしたいことでしょう。
<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応答を適切かつ自動的に生成します。
use Illuminate\Http\Request;
Route::get('/user/invoice/{invoice}', function (Request $request, $invoiceId) {
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});
downloadInvoice
メソッドは3番目の引数により、カスタムファイル名を使用できます。このファイル名には、自動的に「.pdf」という拡張子が付けられます。
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
], 'my-invoice');
支払い失敗の処理
サブスクリプションまたは1回限りの支払いが失敗する場合もあります。これが起きると、Cashierはこの発生を通知するLaravel\Cashier\Exceptions\IncompletePayment
例外を投げます。この例外をキャッチした後に続行する方法は、2つの選択肢があります。
1つ目は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へリダイレクトされます。リダイレクト時に、message
(文字列)およびsuccess
(整数)クエリ文字列変数をURLへ追加します。
もう一つの方法として、Stripeに支払い確認の処理を任せることもできます。この場合、支払い確認ページにリダイレクトする代わりに、StripeダッシュボードでStripeの自動請求メールを設定してください。ただし、IncompletePayment
例外がキャッチされた場合でも、支払い確認の手順を記載したメールがユーザーへ届くよう、通知する必要があります。
Billable
トレイトを使用するモデルのcharge
、invoiceFor
、invoice
メソッドでは、支払いの例外が投げられる場合があります。サブスクリプションを操作する場合では、SubscriptionBuilder
のcreate
メソッド、およびSubscription
モデルのincrementAndInvoice
メソッドとswapAndInvoice
メソッドは、不完全な支払い例外を投げる可能性があります。
既存のサブスクリプションの支払いが不完全であるかどうかの判断は、BillableモデルまたはサブスクリプションインスタンスでhasIncompletePayment
メソッドを使用して行えます。
if ($user->hasIncompletePayment('default')) {
//
}
if ($user->subscription('default')->hasIncompletePayment()) {
//
}
現在、IncompletePayment
を拡張する2種類の支払い例外があります。ユーザーエクスペリエンスをカスタマイズできるように、必要に応じてこれらを個別にキャッチできます。
Laravel\Cashier\Exceptions\PaymentActionRequired
:この例外は、Stripeが支払いを確認して処理するために追加の検証が必要であることを示します。Laravel\Cashier\Exceptions\PaymentFailure
:この例外は、利用可能な資金が不足しているなど、他のさまざまな理由で支払いが失敗したことを示します。
強力な顧客認証(SCA)
あなたのビジネスがヨーロッパに拠点を置いている場合は、EUの強力な顧客認証(SCA)法令を遵守する必要があります。これらの法令は、支払い詐欺を防ぐために2019年9月に欧州連合によって課されました。幸いにして、StripeとCashierは、SCA準拠のアプリケーションを構築する準備ができています。
Note: 使用開始前に、PSD2とSCAに関するStripeのガイドと新しいSCA APIに関するドキュメントを確認してください。
追加の確認が必要な支払い
SCA法令では支払いを確認し処理するため、追加の検証が頻繁に必要になります。これが起きると、Cashierは追加の検証が必要であることを通知するLaravel\Cashier\Exceptions\PaymentActionRequired
例外を投げます。こうした例外の処理方法の詳細は、失敗した支払いの処理のドキュメントを参照してください。
不完了と期限超過の状態
支払いに追加の確認が必要な場合、サブスクリプションはstripe_status
データベースカラムが示すように、incomplete
かpast_due
状態のままになります。Cashierは支払いの確認が完了し、アプリケーションがWebフックを介してStripeから完了の通知を受けるととすぐに、顧客のサブスクリプションを自動的にアクティブ化します。
incomplete
およびpast_due
状態の詳細については、これらの状態に関する追加のドキュメントを参照してください。
オフセッション支払い通知
SCAの法令では、サブスクリプションがアクティブな場合でも、顧客は支払いの詳細を時々確認する必要があるため、Cashierはオフセッションでの支払い確認が必要なときに顧客に通知を送信できます。たとえばこれは、サブスクリプションの更新時に発生する可能性があります。Cashierの支払い通知は、CASHIER_PAYMENT_NOTIFICATION
環境変数を通知クラスに設定することで有効にできます。デフォルトでは、この通知は無効になっています。もちろん、Cashierにはこの目的で使用できる通知クラスが含まれていますが、必要に応じて独自の通知クラスを自由に提供できます。
CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment
オフセッションでの支払い確認通知が確実に配信されるようにするため、アプリケーションでStripe Webフックが設定されていることと、Stripeダッシュボードでinvoice.payment_action_required
webhookが有効になっていることを確認してください。さらに、Billable
モデルでLaravelのIlluminate\Notifications\Notizable
トレイトを使用していることも確認する必要があります。
Note: 顧客が追加の確認を必要とする支払いを手動で行う場合でも、通知は送信されます。残念ながら、支払いが手動なのか「オフセッション」で行われたかをStripeが知る方法はありません。ただし、顧客が支払いを確認した後に支払いページへアクセスすると、「支払いが成功しました(Payment Successful)」というメッセージが表示されます。謝って顧客へ同じ支払いを2回確認させ、2回目の請求を行粉なってしまうことはありません。
Stripe SDK
Cashierのオブジェクトの多くは、StripeSDKオブジェクトのラッパーです。Stripeオブジェクトを直接操作したい場合は、asStripe
メソッドを使用してオブジェクトを簡単に取得できます。
$stripeSubscription = $subscription->asStripeSubscription();
$stripeSubscription->application_fee_percent = 5;
$stripeSubscription->save();
updateStripeSubscription
メソッドを使用して、Stripeサブスクリプションを直接更新することもできます。
$subscription->updateStripeSubscription(['application_fee_percent' => 5]);
テスト
Cashierを使用するアプリケーションをテストする場合、Stripe APIへの実際のHTTPリクエストをモックすることができます。ただし、これには、Cashier自身の動作を部分的に再実装する必要があります。したがって、テストが実際のStripe APIにヒットすることを許可することをおすすめします。これは遅いですが、アプリケーションが期待どおりに機能しているという確信が高まり、遅いテストは独自のPHPUnitテストグループ内に配置するのが良いでしょう。
テストするときは、Cashier自体には優れたテストスイートを既に持っていることを忘れないでください。したがって、基本的なCashierの動作すべてではなく、独自のアプリケーションのサブスクリプションと支払いフローのテストにのみ焦点を当てる必要があります。
テスト開始するには、Stripeシークレットのテストバージョンをphpunit.xml
ファイルに追加します。
<env name="STRIPE_SECRET" value="sk_test_<your-key>"/>
これで、テスト中にCashierとやり取りするたびに、実際のAPIリクエストがStripeテスト環境に送信されます。便宜上、Stripeテストアカウントに、テスト中に使用できるサブスクリプション/プランを事前に入力する必要があります。
Tip!! クレジットカードの拒否や失敗など、さまざまな請求シナリオをテストするため、Stripeが提供しているさまざまなカード番号とトークンのテストを使用できます。