Laravel Cashier (Stripe)
- イントロダクション
- Cashierのアップグレード
- インストール
- 設定
- クイックスタート
- 顧客
- 支払い方法
- サブスクリプション
- サブスクリプションのトライアル
- Stripe Webhookの処理
- 1回限りの支払い
- チェックアウト
- インボイス
- 支払い失敗の処理
- 強力な顧客認証(SCA)
- Stripe SDK
- テスト
イントロダクション
Laravel Cashier Stripeは、Stripeのサブスクリプション課金サービスへの表現力豊かで流暢なインターフェイスを提供します。書くのが面倒な定型的なサブスクリプション課金コードのほとんどを処理します。基本的なサブスクリプション管理に加えて、Cashierはクーポン、サブスクリプションの交換、サブスクリプションの「数量」、キャンセル猶予期間、さらにはインボイスPDFの生成まで処理できます。
Cashierのアップグレード
Cashierの新しいバージョンにアップグレードする際には、アップグレードガイドを注意深く確認することが重要です。
破壊的な変更を防ぐため、Cashierは固定のStripe APIバージョンを使用します。Cashier 15はStripe APIバージョン2023-10-16を利用します。Stripe APIバージョンは、新しいStripeの機能や改善を利用するために、マイナーリリースで更新されます。
インストール
まず、Composerパッケージマネージャを使用して、Stripe用のCashierパッケージをインストールします。
1composer require laravel/cashier
パッケージをインストールした後、vendor:publish Artisanコマンドを使用してCashierのマイグレーションを公開します。
1php artisan vendor:publish --tag="cashier-migrations"
次に、データベースをマイグレートします。
1php artisan migrate
Cashierのマイグレーションは、usersテーブルにいくつかのカラムを追加します。また、すべての顧客のサブスクリプションを保持するための新しいsubscriptionsテーブルと、複数の価格を持つサブスクリプションのためのsubscription_itemsテーブルを作成します。
必要であれば、vendor:publish Artisanコマンドを使用してCashierの設定ファイルを公開することもできます。
1php artisan vendor:publish --tag="cashier-config"
最後に、CashierがすべてのStripeイベントを適切に処理できるように、CashierのWebhook処理を設定することを忘れないでください。
Stripeは、Stripe識別子を保存するために使用されるカラムは、大文字と小文字を区別することを推奨しています。したがって、MySQLを使用する場合は、stripe_idカラムのカラム照合順序がutf8_binに設定されていることを確認してください。これに関する詳細な情報は、Stripeのドキュメントで確認できます。
設定
課金モデル
Cashierを使用する前に、課金モデルの定義にBillableトレイトを追加します。通常、これはApp\Models\Userモデルになります。このトレイトは、サブスクリプションの作成、クーポンの適用、支払い方法情報の更新など、一般的な課金タスクを実行できるさまざまなメソッドを提供します。
1use Laravel\Cashier\Billable;2 3class User extends Authenticatable4{5 use Billable;6}
Cashierは、課金モデルがLaravelに付属しているApp\Models\Userクラスであることを前提としています。これを変更したい場合は、useCustomerModelメソッドを介して別のモデルを指定できます。このメソッドは通常、AppServiceProviderクラスのbootメソッドで呼び出す必要があります。
1use App\Models\Cashier\User; 2use Laravel\Cashier\Cashier; 3 4/** 5 * Bootstrap any application services. 6 */ 7public function boot(): void 8{ 9 Cashier::useCustomerModel(User::class);10}
Laravelが提供するApp\Models\Userモデル以外のモデルを使用している場合は、提供されているCashierのマイグレーションを公開し、代替モデルのテーブル名に合わせて変更する必要があります。
APIキー
次に、アプリケーションの.envファイルでStripe APIキーを設定する必要があります。Stripe APIキーは、Stripeのコントロールパネルから取得できます。
1STRIPE_KEY=your-stripe-key2STRIPE_SECRET=your-stripe-secret3STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
アプリケーションの.envファイルにSTRIPE_WEBHOOK_SECRET環境変数が定義されていることを確認してください。この変数は、受信したWebhookが実際にStripeからのものであることを確認するために使用されます。
通貨の設定
Cashierのデフォルト通貨は米ドル(USD)です。アプリケーションの.envファイル内でCASHIER_CURRENCY環境変数を設定することで、デフォルト通貨を変更できます。
1CASHIER_CURRENCY=eur
Cashierの通貨設定に加えて、インボイスに表示する金額をフォーマットする際に使用するロケールを指定することもできます。内部的に、CashierはPHPのNumberFormatterクラスを使用して通貨ロケールを設定します。
1CASHIER_CURRENCY_LOCALE=nl_BE
en以外のロケールを使用するには、サーバーにext-intl PHP拡張機能がインストールされ、設定されていることを確認してください。
税金の設定
Stripe Taxのおかげで、Stripeによって生成されるすべてのインボイスの税金を自動的に計算することが可能です。アプリケーションのApp\Providers\AppServiceProviderクラスのbootメソッドでcalculateTaxesメソッドを呼び出すことで、自動税計算を有効にできます。
1use Laravel\Cashier\Cashier;2 3/**4 * Bootstrap any application services.5 */6public function boot(): void7{8 Cashier::calculateTaxes();9}
税計算が有効になると、新しいサブスクリプションや生成される1回限りのインボイスには、自動的に税計算が適用されます。
この機能が正しく動作するためには、顧客の請求情報(顧客名、住所、納税者番号など)をStripeに同期させる必要があります。これを行うには、Cashierが提供する顧客データ同期と納税者番号のメソッドを使用できます。
ロギング
Cashierでは、Stripeの致命的なエラーをログに記録する際に使用するログチャンネルを指定できます。アプリケーションの.envファイル内でCASHIER_LOGGER環境変数を定義することで、ログチャンネルを指定できます。
1CASHIER_LOGGER=stack
StripeへのAPI呼び出しによって生成された例外は、アプリケーションのデフォルトのログチャンネルを通じて記録されます。
カスタムモデルの使用
Cashierが内部的に使用するモデルを、独自のモデルを定義し、対応するCashierモデルを継承することで、自由に拡張できます。
1use Laravel\Cashier\Subscription as CashierSubscription;2 3class Subscription extends CashierSubscription4{5 // ...6}
モデルを定義した後、Laravel\Cashier\Cashierクラスを介してカスタムモデルを使用するようにCashierに指示できます。通常、アプリケーションのApp\Providers\AppServiceProviderクラスのbootメソッドで、カスタムモデルについてCashierに通知する必要があります。
1use App\Models\Cashier\Subscription; 2use App\Models\Cashier\SubscriptionItem; 3 4/** 5 * Bootstrap any application services. 6 */ 7public function boot(): void 8{ 9 Cashier::useSubscriptionModel(Subscription::class);10 Cashier::useSubscriptionItemModel(SubscriptionItem::class);11}
クイックスタート
製品の販売
Stripe Checkoutを利用する前に、Stripeダッシュボードで固定価格の製品を定義する必要があります。さらに、CashierのWebhook処理を設定する必要があります。
アプリケーションを介して製品やサブスクリプションの課金を提供することは、気が引けるかもしれません。しかし、CashierとStripe Checkoutのおかげで、モダンで堅牢な支払い統合を簡単に構築できます。
非定期的な、1回限りの製品に対して顧客に課金するために、Cashierを利用して顧客をStripe Checkoutに誘導します。そこで顧客は支払い詳細を提供し、購入を確定します。Checkoutを介して支払いが行われると、顧客はアプリケーション内の選択した成功URLにリダイレクトされます。
1use Illuminate\Http\Request; 2 3Route::get('/checkout', function (Request $request) { 4 $stripePriceId = 'price_deluxe_album'; 5 6 $quantity = 1; 7 8 return $request->user()->checkout([$stripePriceId => $quantity], [ 9 'success_url' => route('checkout-success'),10 'cancel_url' => route('checkout-cancel'),11 ]);12})->name('checkout');13 14Route::view('/checkout/success', 'checkout.success')->name('checkout-success');15Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');
上の例でわかるように、Cashierが提供するcheckoutメソッドを利用して、特定の「価格ID」に対して顧客をStripe Checkoutにリダイレクトします。Stripeを使用する場合、「価格」とは、特定の製品に対して定義された価格を指します。
必要に応じて、checkoutメソッドはStripeで自動的に顧客を作成し、そのStripe顧客レコードをアプリケーションのデータベース内の対応するユーザーに関連付けます。チェックアウトセッションを完了した後、顧客は専用の成功ページまたはキャンセルページにリダイレクトされ、そこで顧客に情報メッセージを表示できます。
Stripe Checkoutにメタデータを提供
製品を販売する際、独自のアプリケーションで定義したCartおよびOrderモデルを介して、完了した注文と購入された製品を追跡するのが一般的です。顧客をStripe Checkoutにリダイレクトして購入を完了させる際には、顧客がアプリケーションにリダイレクトされたときに完了した購入を対応する注文に関連付けられるように、既存の注文IDを提供する必要がある場合があります。
これを実現するには、checkoutメソッドにmetadataの配列を提供します。ユーザーがチェックアウトプロセスを開始すると、アプリケーション内で保留中のOrderが作成されると想像してみましょう。この例のCartとOrderモデルは説明用であり、Cashierによって提供されるものではないことに注意してください。これらの概念は、ご自身のアプリケーションのニーズに基づいて自由に実装できます。
1use App\Models\Cart; 2use App\Models\Order; 3use Illuminate\Http\Request; 4 5Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) { 6 $order = Order::create([ 7 'cart_id' => $cart->id, 8 'price_ids' => $cart->price_ids, 9 'status' => 'incomplete',10 ]);11 12 return $request->user()->checkout($order->price_ids, [13 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',14 'cancel_url' => route('checkout-cancel'),15 'metadata' => ['order_id' => $order->id],16 ]);17})->name('checkout');
上の例でわかるように、ユーザーがチェックアウトプロセスを開始すると、カート/注文に関連するすべてのStripe価格IDをcheckoutメソッドに提供します。もちろん、アプリケーションは、顧客が追加するにつれてこれらのアイテムを「ショッピングカート」または注文に関連付ける責任があります。また、metadata配列を介して注文IDをStripe Checkoutセッションに提供します。最後に、Checkoutの成功ルートにCHECKOUT_SESSION_IDテンプレート変数を追加しました。Stripeが顧客をアプリケーションにリダイレクトすると、このテンプレート変数は自動的にCheckoutセッションIDで埋められます。
次に、Checkoutの成功ルートを構築しましょう。これは、Stripe Checkoutでの購入が完了した後にユーザーがリダイレクトされるルートです。このルート内で、Stripe CheckoutセッションIDと関連するStripe Checkoutインスタンスを取得して、提供したメタデータにアクセスし、顧客の注文をそれに応じて更新できます。
1use App\Models\Order; 2use Illuminate\Http\Request; 3use Laravel\Cashier\Cashier; 4 5Route::get('/checkout/success', function (Request $request) { 6 $sessionId = $request->get('session_id'); 7 8 if ($sessionId === null) { 9 return;10 }11 12 $session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);13 14 if ($session->payment_status !== 'paid') {15 return;16 }17 18 $orderId = $session['metadata']['order_id'] ?? null;19 20 $order = Order::findOrFail($orderId);21 22 $order->update(['status' => 'completed']);23 24 return view('checkout-success', ['order' => $order]);25})->name('checkout-success');
Checkoutセッションオブジェクトに含まれるデータの詳細については、Stripeのドキュメントを参照してください。
サブスクリプションの販売
Stripe Checkoutを利用する前に、Stripeダッシュボードで固定価格の製品を定義する必要があります。さらに、CashierのWebhook処理を設定する必要があります。
アプリケーションを介して製品やサブスクリプションの課金を提供することは、気が引けるかもしれません。しかし、CashierとStripe Checkoutのおかげで、モダンで堅牢な支払い統合を簡単に構築できます。
CashierとStripe Checkoutを使用してサブスクリプションを販売する方法を学ぶために、基本的な月額(price_basic_monthly)および年額(price_basic_yearly)プランを持つサブスクリプションサービスの簡単なシナリオを考えてみましょう。これら2つの価格は、Stripeダッシュボードで「Basic」製品(pro_basic)の下にグループ化できます。さらに、当社のサブスクリプションサービスは、pro_expertとしてExpertプランを提供する場合があります。
まず、顧客がどのようにして私たちのサービスに登録できるかを見ていきましょう。もちろん、顧客はアプリケーションの価格設定ページでBasicプランの「登録」ボタンをクリックすると想像できます。このボタンまたはリンクは、ユーザーを選択したプランのStripe Checkoutセッションを作成するLaravelルートに誘導する必要があります。
1use Illuminate\Http\Request; 2 3Route::get('/subscription-checkout', function (Request $request) { 4 return $request->user() 5 ->newSubscription('default', 'price_basic_monthly') 6 ->trialDays(5) 7 ->allowPromotionCodes() 8 ->checkout([ 9 'success_url' => route('your-success-route'),10 'cancel_url' => route('your-cancel-route'),11 ]);12});
上の例でわかるように、顧客をStripe Checkoutセッションにリダイレクトし、Basicプランに登録できるようにします。チェックアウトが成功またはキャンセルされた後、顧客はcheckoutメソッドに提供したURLにリダイレクトされます。サブスクリプションが実際にいつ開始されたかを知るために(一部の支払い方法は処理に数秒かかるため)、CashierのWebhook処理も設定する必要があります。
顧客がサブスクリプションを開始できるようになったので、アプリケーションの特定の部分を制限して、登録ユーザーのみがアクセスできるようにする必要があります。もちろん、CashierのBillableトレイトが提供するsubscribedメソッドを介して、ユーザーの現在のサブスクリプションステータスをいつでも判断できます。
1@if ($user->subscribed())2 <p>You are subscribed.</p>3@endif
ユーザーが特定の製品や価格に登録しているかどうかを簡単に判断することもできます。
1@if ($user->subscribedToProduct('pro_basic'))2 <p>You are subscribed to our Basic product.</p>3@endif4 5@if ($user->subscribedToPrice('price_basic_monthly'))6 <p>You are subscribed to our monthly Basic plan.</p>7@endif
登録済みミドルウェアの構築
便宜上、受信リクエストが登録ユーザーからのものかどうかを判断するミドルウェアを作成するとよいでしょう。このミドルウェアを定義したら、それをルートに簡単に割り当てて、登録していないユーザーがルートにアクセスするのを防ぐことができます。
1<?php 2 3namespace App\Http\Middleware; 4 5use Closure; 6use Illuminate\Http\Request; 7use Symfony\Component\HttpFoundation\Response; 8 9class Subscribed10{11 /**12 * Handle an incoming request.13 */14 public function handle(Request $request, Closure $next): Response15 {16 if (! $request->user()?->subscribed()) {17 // Redirect user to billing page and ask them to subscribe...18 return redirect('/billing');19 }20 21 return $next($request);22 }23}
ミドルウェアを定義したら、それをルートに割り当てることができます。
1use App\Http\Middleware\Subscribed;2 3Route::get('/dashboard', function () {4 // ...5})->middleware([Subscribed::class]);
顧客が請求プランを管理できるようにする
もちろん、顧客はサブスクリプションプランを別の製品や「ティア」に変更したいと思うかもしれません。これを許可する最も簡単な方法は、顧客をStripeの顧客請求ポータルに誘導することです。これは、顧客がインボイスをダウンロードし、支払い方法を更新し、サブスクリプションプランを変更できるホストされたユーザーインターフェースを提供します。
まず、アプリケーション内に、ユーザーを請求ポータルセッションを開始するために利用するLaravelルートに誘導するリンクまたはボタンを定義します。
1<a href="{{ route('billing') }}">2 Billing3</a>
次に、Stripe顧客請求ポータルセッションを開始し、ユーザーをポータルにリダイレクトするルートを定義しましょう。redirectToBillingPortalメソッドは、ユーザーがポータルを終了するときに戻るべきURLを受け入れます。
1use Illuminate\Http\Request;2 3Route::get('/billing', function (Request $request) {4 return $request->user()->redirectToBillingPortal(route('dashboard'));5})->middleware(['auth'])->name('billing');
CashierのWebhook処理を設定している限り、CashierはStripeからの受信Webhookを検査することで、アプリケーションのCashier関連データベーステーブルを自動的に同期させ続けます。したがって、たとえば、ユーザーがStripeの顧客請求ポータルを介してサブスクリプションをキャンセルすると、Cashierは対応するWebhookを受信し、アプリケーションのデータベースでサブスクリプションを「キャンセル済み」としてマークします。
顧客
顧客の取得
Cashier::findBillableメソッドを使用して、Stripe IDで顧客を取得できます。このメソッドは、課金モデルのインスタンスを返します。
1use Laravel\Cashier\Cashier;2 3$user = Cashier::findBillable($stripeId);
顧客の作成
時々、サブスクリプションを開始せずにStripeの顧客を作成したい場合があります。これは、createAsStripeCustomerメソッドを使用して行うことができます。
1$stripeCustomer = $user->createAsStripeCustomer();
Stripeで顧客が作成されたら、後でサブスクリプションを開始できます。オプションの$options配列を指定して、Stripe APIでサポートされている追加の顧客作成パラメータを渡すことができます。
1$stripeCustomer = $user->createAsStripeCustomer($options);
課金モデルのStripe顧客オブジェクトを返したい場合は、asStripeCustomerメソッドを使用できます。
1$stripeCustomer = $user->asStripeCustomer();
特定の課金モデルのStripe顧客オブジェクトを取得したいが、その課金モデルがすでにStripeの顧客であるかどうかわからない場合は、createOrGetStripeCustomerメソッドを使用できます。このメソッドは、まだ存在しない場合はStripeに新しい顧客を作成します。
1$stripeCustomer = $user->createOrGetStripeCustomer();
顧客の更新
時々、Stripe顧客を直接追加情報で更新したい場合があります。これは、updateStripeCustomerメソッドを使用して行うことができます。このメソッドは、Stripe APIでサポートされている顧客更新オプションの配列を受け入れます。
1$stripeCustomer = $user->updateStripeCustomer($options);
残高
Stripeでは、顧客の「残高」にクレジットまたはデビットすることができます。後で、この残高は新しいインボイスにクレジットまたはデビットされます。顧客の合計残高を確認するには、課金モデルで利用可能なbalanceメソッドを使用できます。balanceメソッドは、顧客の通貨での残高のフォーマットされた文字列表現を返します。
1$balance = $user->balance();
顧客の残高にクレジットするには、creditBalanceメソッドに値を指定します。必要に応じて、説明を提供することもできます。
1$user->creditBalance(500, 'Premium customer top-up.');
debitBalanceメソッドに値を指定すると、顧客の残高がデビットされます。
1$user->debitBalance(300, 'Bad usage penalty.');
applyBalanceメソッドは、顧客の新しい顧客残高トランザクションを作成します。これらのトランザクションレコードは、balanceTransactionsメソッドを使用して取得でき、顧客が確認するためのクレジットとデビットのログを提供するのに役立ちます。
1// Retrieve all transactions... 2$transactions = $user->balanceTransactions(); 3 4foreach ($transactions as $transaction) { 5 // Transaction amount... 6 $amount = $transaction->amount(); // $2.31 7 8 // Retrieve the related invoice when available... 9 $invoice = $transaction->invoice();10}
納税者番号
Cashierは、顧客の納税者番号を管理する簡単な方法を提供します。たとえば、taxIdsメソッドを使用して、顧客に割り当てられているすべての納税者番号をコレクションとして取得できます。
1$taxIds = $user->taxIds();
顧客の特定の納税者番号をそのIDで取得することもできます。
1$taxId = $user->findTaxId('txi_belgium');
有効なタイプと値をcreateTaxIdメソッドに渡すことで、新しい納税者番号を作成できます。
1$taxId = $user->createTaxId('eu_vat', 'BE0123456789');
createTaxIdメソッドは、VAT IDを即座に顧客のアカウントに追加します。VAT IDの検証もStripeによって行われますが、これは非同期プロセスです。customer.tax_id.updated Webhookイベントを購読し、VAT IDのverificationパラメータを検査することで、検証の更新通知を受け取ることができます。Webhookの処理に関する詳細については、Webhookハンドラの定義に関するドキュメントを参照してください。
deleteTaxIdメソッドを使用して、納税者番号を削除できます。
1$user->deleteTaxId('txi_belgium');
顧客データとStripeとの同期
通常、アプリケーションのユーザーが名前、メールアドレス、またはStripeによっても保存されているその他の情報を更新した場合、Stripeに更新を通知する必要があります。そうすることで、Stripeの情報のコピーがアプリケーションのものと同期されます。
これを自動化するには、課金モデルでモデルのupdatedイベントに反応するイベントリスナーを定義します。そして、イベントリスナー内で、モデルのsyncStripeCustomerDetailsメソッドを呼び出します。
1use App\Models\User; 2use function Illuminate\Events\queueable; 3 4/** 5 * The "booted" method of the model. 6 */ 7protected static function booted(): void 8{ 9 static::updated(queueable(function (User $customer) {10 if ($customer->hasStripeId()) {11 $customer->syncStripeCustomerDetails();12 }13 }));14}
これで、顧客モデルが更新されるたびに、その情報がStripeと同期されます。便宜上、Cashierは顧客の初回作成時に自動的に顧客の情報をStripeと同期します。
Cashierが提供するさまざまなメソッドをオーバーライドすることで、顧客情報をStripeに同期するために使用されるカラムをカスタマイズできます。たとえば、stripeNameメソッドをオーバーライドして、Cashierが顧客情報をStripeに同期する際に顧客の「名前」と見なされるべき属性をカスタマイズできます。
1/**2 * Get the customer name that should be synced to Stripe.3 */4public function stripeName(): string|null5{6 return $this->company_name;7}
同様に、stripeEmail、stripePhone、stripeAddress、stripePreferredLocalesメソッドをオーバーライドできます。これらのメソッドは、Stripe顧客オブジェクトを更新する際に、対応する顧客パラメータに情報を同期します。顧客情報同期プロセスを完全に制御したい場合は、syncStripeCustomerDetailsメソッドをオーバーライドできます。
Billingポータル
Stripeは、顧客がサブスクリプション、支払い方法を管理し、請求履歴を表示できるように、請求ポータルを簡単に設定する方法を提供しています。コントローラーまたはルートから課金モデルでredirectToBillingPortalメソッドを呼び出すことで、ユーザーを請求ポータルにリダイレクトできます。
1use Illuminate\Http\Request;2 3Route::get('/billing-portal', function (Request $request) {4 return $request->user()->redirectToBillingPortal();5});
デフォルトでは、ユーザーがサブスクリプションの管理を終えると、Stripe請求ポータル内のリンクを介してアプリケーションのhomeルートに戻ることができます。redirectToBillingPortalメソッドにURLを引数として渡すことで、ユーザーが戻るべきカスタムURLを指定できます。
1use Illuminate\Http\Request;2 3Route::get('/billing-portal', function (Request $request) {4 return $request->user()->redirectToBillingPortal(route('billing'));5});
HTTPリダイレクト応答を生成せずに請求ポータルへのURLを生成したい場合は、billingPortalUrlメソッドを呼び出すことができます。
1$url = $request->user()->billingPortalUrl(route('billing'));
支払い方法
支払い方法の保存
Stripeでサブスクリプションを作成したり、「1回限り」の支払いを行ったりするには、支払い方法を保存し、そのIDをStripeから取得する必要があります。これを実現する方法は、支払い方法をサブスクリプション用に使用するか、1回限りの支払い用に使用するかによって異なるため、以下で両方を検討します。
サブスクリプションの支払い方法
サブスクリプションによる将来の使用のために顧客のクレジットカード情報を保存する場合、顧客の支払い方法の詳細を安全に収集するために、Stripeの「セットアップインテント」APIを使用する必要があります。「セットアップインテント」は、Stripeに対して顧客の支払い方法に請求する意図を示します。CashierのBillableトレイトには、新しいセットアップインテントを簡単に作成するためのcreateSetupIntentメソッドが含まれています。このメソッドは、顧客の支払い方法の詳細を収集するフォームをレンダリングするルートまたはコントローラーから呼び出す必要があります。
1return view('update-payment-method', [2 'intent' => $user->createSetupIntent()3]);
セットアップインテントを作成してビューに渡した後、そのシークレットを支払い方法を収集する要素に添付する必要があります。たとえば、この「支払い方法の更新」フォームを考えてみましょう。
1<input id="card-holder-name" type="text">2 3<!-- Stripe Elements Placeholder -->4<div id="card-element"></div>5 6<button id="card-button" data-secret="{{ $intent->client_secret }}">7 Update Payment Method8</button>
次に、Stripe.jsライブラリを使用して、フォームにStripe Elementを添付し、顧客の支払い詳細を安全に収集できます。
1<script src="https://js.stripe.com/v3/"></script> 2 3<script> 4 const stripe = Stripe('stripe-public-key'); 5 6 const elements = stripe.elements(); 7 const cardElement = elements.create('card'); 8 9 cardElement.mount('#card-element');10</script>
次に、カードを検証し、StripeのconfirmCardSetupメソッドを使用して、Stripeから安全な「支払い方法ID」を取得できます。
1const cardHolderName = document.getElementById('card-holder-name'); 2const cardButton = document.getElementById('card-button'); 3const clientSecret = cardButton.dataset.secret; 4 5cardButton.addEventListener('click', async (e) => { 6 const { setupIntent, error } = await stripe.confirmCardSetup( 7 clientSecret, { 8 payment_method: { 9 card: cardElement,10 billing_details: { name: cardHolderName.value }11 }12 }13 );14 15 if (error) {16 // Display "error.message" to the user...17 } else {18 // The card has been verified successfully...19 }20});
Stripeによってカードが検証された後、結果のsetupIntent.payment_method IDをLaravelアプリケーションに渡し、そこで顧客に添付できます。支払い方法は、新しい支払い方法として追加するか、デフォルトの支払い方法を更新するために使用できます。支払い方法IDをすぐに使用して、新しいサブスクリプションを作成することもできます。
セットアップインテントと顧客の支払い詳細の収集に関する詳細情報については、Stripeが提供するこの概要を確認してください。
1回限りの支払いのための支払い方法
もちろん、顧客の支払い方法に対して1回限りの支払いを行う場合、支払い方法IDを一度だけ使用する必要があります。Stripeの制限により、1回限りの支払いには顧客の保存されたデフォルトの支払い方法を使用することはできません。Stripe.jsライブラリを使用して、顧客に支払い方法の詳細を入力させる必要があります。たとえば、次のフォームを考えてみましょう。
1<input id="card-holder-name" type="text">2 3<!-- Stripe Elements Placeholder -->4<div id="card-element"></div>5 6<button id="card-button">7 Process Payment8</button>
このようなフォームを定義した後、Stripe.jsライブラリを使用して、フォームにStripe Elementを添付し、顧客の支払い詳細を安全に収集できます。
1<script src="https://js.stripe.com/v3/"></script> 2 3<script> 4 const stripe = Stripe('stripe-public-key'); 5 6 const elements = stripe.elements(); 7 const cardElement = elements.create('card'); 8 9 cardElement.mount('#card-element');10</script>
次に、カードを検証し、StripeのcreatePaymentMethodメソッドを使用して、Stripeから安全な「支払い方法ID」を取得できます。
1const cardHolderName = document.getElementById('card-holder-name'); 2const cardButton = document.getElementById('card-button'); 3 4cardButton.addEventListener('click', async (e) => { 5 const { paymentMethod, error } = await stripe.createPaymentMethod( 6 'card', cardElement, { 7 billing_details: { name: cardHolderName.value } 8 } 9 );10 11 if (error) {12 // Display "error.message" to the user...13 } else {14 // The card has been verified successfully...15 }16});
カードが正常に検証された場合は、paymentMethod.idをLaravelアプリケーションに渡し、1回限りの支払いを処理できます。
支払い方法の取得
課金モデルインスタンスのpaymentMethodsメソッドは、Laravel\Cashier\PaymentMethodインスタンスのコレクションを返します。
1$paymentMethods = $user->paymentMethods();
デフォルトでは、このメソッドはすべての種類の支払い方法を返します。特定の種類の支払い方法を取得するには、メソッドに引数としてtypeを渡すことができます。
1$paymentMethods = $user->paymentMethods('sepa_debit');
顧客のデフォルトの支払い方法を取得するには、defaultPaymentMethodメソッドを使用できます。
1$paymentMethod = $user->defaultPaymentMethod();
課金モデルに添付されている特定の支払い方法を取得するには、findPaymentMethodメソッドを使用します。
1$paymentMethod = $user->findPaymentMethod($paymentMethodId);
支払い方法の存在確認
課金モデルのアカウントにデフォルトの支払い方法が添付されているかどうかを判断するには、hasDefaultPaymentMethodメソッドを呼び出します。
1if ($user->hasDefaultPaymentMethod()) {2 // ...3}
課金モデルのアカウントに少なくとも1つの支払い方法が添付されているかどうかを判断するには、hasPaymentMethodメソッドを使用できます。
1if ($user->hasPaymentMethod()) {2 // ...3}
このメソッドは、課金モデルに支払い方法がまったくないかどうかを判断します。モデルに特定の種類の支払い方法が存在するかどうかを判断するには、メソッドに引数としてtypeを渡すことができます。
1if ($user->hasPaymentMethod('sepa_debit')) {2 // ...3}
デフォルト支払い方法の更新
updateDefaultPaymentMethodメソッドを使用して、顧客のデフォルトの支払い方法情報を更新できます。このメソッドはStripeの支払い方法IDを受け入れ、新しい支払い方法をデフォルトの請求支払い方法として割り当てます。
1$user->updateDefaultPaymentMethod($paymentMethod);
デフォルトの支払い方法情報をStripeの顧客のデフォルトの支払い方法情報と同期するには、updateDefaultPaymentMethodFromStripeメソッドを使用できます。
1$user->updateDefaultPaymentMethodFromStripe();
顧客のデフォルトの支払い方法は、インボイス発行と新しいサブスクリプションの作成にのみ使用できます。Stripeによって課せられた制限により、1回限りの支払いには使用できません。
支払い方法の追加
新しい支払い方法を追加するには、課金モデルでaddPaymentMethodメソッドを呼び出し、支払い方法IDを渡します。
1$user->addPaymentMethod($paymentMethod);
支払い方法IDの取得方法については、支払い方法の保存に関するドキュメントを確認してください。
支払い方法の削除
支払い方法を削除するには、削除したいLaravel\Cashier\PaymentMethodインスタンスでdeleteメソッドを呼び出します。
1$paymentMethod->delete();
deletePaymentMethodメソッドは、課金モデルから特定の支払い方法を削除します。
1$user->deletePaymentMethod('pm_visa');
deletePaymentMethodsメソッドは、課金モデルのすべての支払い方法情報を削除します。
1$user->deletePaymentMethods();
デフォルトでは、このメソッドはすべての種類の支払い方法を削除します。特定の種類の支払い方法を削除するには、メソッドに引数としてtypeを渡すことができます。
1$user->deletePaymentMethods('sepa_debit');
ユーザーが有効なサブスクリプションを持っている場合、アプリケーションはデフォルトの支払い方法を削除することを許可してはなりません。
サブスクリプション
サブスクリプションは、顧客の定期的な支払いを設定する方法を提供します。Cashierが管理するStripeサブスクリプションは、複数のサブスクリプション価格、サブスクリプション数量、トライアルなどをサポートします。
サブスクリプションの作成
サブスクリプションを作成するには、まず課金モデルのインスタンスを取得します。これは通常、App\Models\Userのインスタンスになります。モデルインスタンスを取得したら、newSubscriptionメソッドを使用してモデルのサブスクリプションを作成できます。
1use Illuminate\Http\Request;2 3Route::post('/user/subscribe', function (Request $request) {4 $request->user()->newSubscription(5 'default', 'price_monthly'6 )->create($request->paymentMethodId);7 8 // ...9});
newSubscriptionメソッドに渡す最初の引数は、サブスクリプションの内部タイプである必要があります。アプリケーションが単一のサブスクリプションのみを提供する場合、これをdefaultまたはprimaryと呼ぶことができます。このサブスクリプションタイプは、内部アプリケーションでの使用のみを目的としており、ユーザーに表示するためのものではありません。また、スペースを含んではならず、サブスクリプション作成後に変更してはなりません。2番目の引数は、ユーザーが購読する特定の価格です。この値は、Stripeの価格のIDに対応している必要があります。
createメソッドは、Stripeの支払い方法IDまたはStripeのPaymentMethodオブジェクトを受け入れ、サブスクリプションを開始し、データベースを課金モデルのStripe顧客IDやその他の関連する請求情報で更新します。
支払い方法IDを直接createサブスクリプションメソッドに渡すと、自動的にユーザーの保存済み支払い方法に追加されます。
インボイスメールによる定期支払いの収集
顧客の定期支払いを自動的に収集する代わりに、Stripeに、定期支払いの期日ごとにインボイスを顧客にメールで送信するように指示できます。そうすれば、顧客はインボイスを受け取ったときに手動で支払うことができます。インボイスを介して定期支払いを収集する場合、顧客は事前に支払い方法を提供する必要はありません。
1$user->newSubscription('default', 'price_monthly')->createAndSendInvoice();
顧客がサブスクリプションをキャンセルされるまでにインボイスを支払う期間は、days_until_dueオプションによって決まります。デフォルトでは30日ですが、必要に応じてこのオプションに特定の値を指定することもできます。
1$user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [2 'days_until_due' => 303]);
数量
サブスクリプションを作成する際に価格に特定の数量を設定したい場合は、サブスクリプションを作成する前にサブスクリプションビルダーでquantityメソッドを呼び出す必要があります。
1$user->newSubscription('default', 'price_monthly')2 ->quantity(5)3 ->create($paymentMethod);
追加の詳細
Stripeでサポートされている追加の顧客またはサブスクリプションオプションを指定したい場合は、createメソッドの2番目と3番目の引数として渡すことで行うことができます。
1$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [2 'email' => $email,3], [4 'metadata' => ['note' => 'Some extra information.'],5]);
クーポン
サブスクリプションの作成時にクーポンを適用したい場合は、withCouponメソッドを使用できます。
1$user->newSubscription('default', 'price_monthly')2 ->withCoupon('code')3 ->create($paymentMethod);
または、Stripeプロモーションコードを適用したい場合は、withPromotionCodeメソッドを使用できます。
1$user->newSubscription('default', 'price_monthly')2 ->withPromotionCode('promo_code_id')3 ->create($paymentMethod);
指定されたプロモーションコードIDは、顧客向けのプロモーションコードではなく、プロモーションコードに割り当てられたStripe API IDである必要があります。特定の顧客向けプロモーションコードに基づいてプロモーションコードIDを見つける必要がある場合は、findPromotionCodeメソッドを使用できます。
1// Find a promotion code ID by its customer facing code...2$promotionCode = $user->findPromotionCode('SUMMERSALE');3 4// Find an active promotion code ID by its customer facing code...5$promotionCode = $user->findActivePromotionCode('SUMMERSALE');
上記の例では、返された$promotionCodeオブジェクトはLaravel\Cashier\PromotionCodeのインスタンスです。このクラスは、基になるStripe\PromotionCodeオブジェクトを装飾します。couponメソッドを呼び出すことで、プロモーションコードに関連するクーポンを取得できます。
1$coupon = $user->findPromotionCode('SUMMERSALE')->coupon();
クーポンインスタンスを使用すると、割引額を決定したり、クーポンが固定割引かパーセンテージベースの割引かを確認したりできます。
1if ($coupon->isPercentage()) {2 return $coupon->percentOff().'%'; // 21.5%3} else {4 return $coupon->amountOff(); // $5.995}
顧客またはサブスクリプションに現在適用されている割引を取得することもできます。
1$discount = $billable->discount();2 3$discount = $subscription->discount();
返されたLaravel\Cashier\Discountインスタンスは、基になるStripe\Discountオブジェクトインスタンスを装飾します。この割引に関連するクーポンを取得するには、couponメソッドを呼び出します。
1$coupon = $subscription->discount()->coupon();
顧客またはサブスクリプションに新しいクーポンまたはプロモーションコードを適用したい場合は、applyCouponまたはapplyPromotionCodeメソッドを介して行うことができます。
1$billable->applyCoupon('coupon_id');2$billable->applyPromotionCode('promotion_code_id');3 4$subscription->applyCoupon('coupon_id');5$subscription->applyPromotionCode('promotion_code_id');
顧客向けのプロモーションコードではなく、プロモーションコードに割り当てられたStripe API IDを使用する必要があることを忘れないでください。顧客またはサブスクリプションには、一度に1つのクーポンまたはプロモーションコードしか適用できません。
この件に関する詳細については、クーポンとプロモーションコードに関するStripeのドキュメントを参照してください。
サブスクリプションの追加
すでにデフォルトの支払い方法を持っている顧客にサブスクリプションを追加したい場合は、サブスクリプションビルダーでaddメソッドを呼び出すことができます。
1use App\Models\User;2 3$user = User::find(1);4 5$user->newSubscription('default', 'price_monthly')->add();
Stripeダッシュボードからのサブスクリプション作成
Stripeダッシュボード自体からサブスクリプションを作成することもできます。その場合、Cashierは新しく追加されたサブスクリプションを同期し、defaultのタイプを割り当てます。ダッシュボードで作成されたサブスクリプションに割り当てられるサブスクリプションタイプをカスタマイズするには、Webhookイベントハンドラを定義します。
さらに、Stripeダッシュボードを介して作成できるサブスクリプションのタイプは1つだけです。アプリケーションが異なるタイプを使用する複数のサブスクリプションを提供する場合、Stripeダッシュボードを介して追加できるサブスクリプションのタイプは1つだけです。
最後に、アプリケーションが提供するサブスクリプションのタイプごとに、有効なサブスクリプションを1つだけ追加するように常に注意する必要があります。顧客が2つのdefaultサブスクリプションを持っている場合、両方がアプリケーションのデータベースと同期されていても、Cashierは最後に追加されたサブスクリプションのみを使用します。
サブスクリプションステータスの確認
顧客がアプリケーションに登録されると、さまざまな便利なメソッドを使用してサブスクリプションのステータスを簡単に確認できます。まず、subscribedメソッドは、サブスクリプションが現在トライアル期間中であっても、顧客が有効なサブスクリプションを持っている場合にtrueを返します。subscribedメソッドは、最初の引数としてサブスクリプションのタイプを受け入れます。
1if ($user->subscribed('default')) {2 // ...3}
subscribedメソッドは、ルートミドルウェアの優れた候補でもあり、ユーザーのサブスクリプションステータスに基づいてルートやコントローラーへのアクセスをフィルタリングできます。
1<?php 2 3namespace App\Http\Middleware; 4 5use Closure; 6use Illuminate\Http\Request; 7use Symfony\Component\HttpFoundation\Response; 8 9class EnsureUserIsSubscribed10{11 /**12 * Handle an incoming request.13 *14 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next15 */16 public function handle(Request $request, Closure $next): Response17 {18 if ($request->user() && ! $request->user()->subscribed('default')) {19 // This user is not a paying customer...20 return redirect('/billing');21 }22 23 return $next($request);24 }25}
ユーザーがまだトライアル期間中かどうかを判断したい場合は、onTrialメソッドを使用できます。このメソッドは、ユーザーがまだトライアル期間中であることを警告表示するかどうかを判断するのに役立ちます。
1if ($user->subscription('default')->onTrial()) {2 // ...3}
subscribedToProductメソッドは、指定されたStripe製品IDに基づいて、ユーザーが特定の製品に登録しているかどうかを判断するために使用できます。Stripeでは、製品は価格のコレクションです。この例では、ユーザーのdefaultサブスクリプションがアプリケーションの「プレミアム」製品にアクティブに登録されているかどうかを判断します。指定されたStripe製品IDは、Stripeダッシュボードの製品IDの1つに対応している必要があります。
1if ($user->subscribedToProduct('prod_premium', 'default')) {2 // ...3}
subscribedToProductメソッドに配列を渡すことで、ユーザーのdefaultサブスクリプションがアプリケーションの「ベーシック」または「プレミアム」製品にアクティブに登録されているかどうかを判断できます。
1if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {2 // ...3}
subscribedToPriceメソッドは、顧客のサブスクリプションが特定の価格IDに対応しているかどうかを判断するために使用できます。
1if ($user->subscribedToPrice('price_basic_monthly', 'default')) {2 // ...3}
recurringメソッドは、ユーザーが現在登録しており、トライアル期間が終了しているかどうかを判断するために使用できます。
1if ($user->subscription('default')->recurring()) {2 // ...3}
ユーザーが同じタイプの2つのサブスクリプションを持っている場合、subscriptionメソッドは常に最新のサブスクリプションを返します。たとえば、ユーザーがdefaultタイプの2つのサブスクリプションレコードを持っている場合があります。しかし、サブスクリプションの1つは古く、期限切れのサブスクリプションである可能性がありますが、もう1つは現在有効なサブスクリプションです。最新のサブスクリプションが常に返され、古いサブスクリプションは履歴確認のためにデータベースに保持されます。
キャンセルされたサブスクリプションのステータス
ユーザーがかつてはアクティブなサブスクライバーだったが、サブスクリプションをキャンセルしたかどうかを判断するには、canceledメソッドを使用できます。
1if ($user->subscription('default')->canceled()) {2 // ...3}
サブスクリプションをキャンセルしたが、サブスクリプションが完全に期限切れになるまで「猶予期間」にあるかどうかを判断することもできます。たとえば、ユーザーが3月5日にキャンセルしたサブスクリプションがもともと3月10日に期限切れになる予定だった場合、ユーザーは3月10日まで「猶予期間」にあります。この間、subscribedメソッドは引き続きtrueを返すことに注意してください。
1if ($user->subscription('default')->onGracePeriod()) {2 // ...3}
ユーザーがサブスクリプションをキャンセルし、「猶予期間」が終了したかどうかを判断するには、endedメソッドを使用できます。
1if ($user->subscription('default')->ended()) {2 // ...3}
未完了および支払い遅延ステータス
作成後にサブスクリプションに2回目の支払いアクションが必要な場合、サブスクリプションはincompleteとしてマークされます。サブスクリプションのステータスは、Cashierのsubscriptionsデータベーステーブルのstripe_statusカラムに保存されます。
同様に、価格を交換する際に2回目の支払いアクションが必要な場合、サブスクリプションはpast_dueとしてマークされます。サブスクリプションがこれらのいずれかの状態にある場合、顧客が支払いを確認するまでアクティブになりません。サブスクリプションに未完了の支払いがあるかどうかを判断するには、課金モデルまたはサブスクリプションインスタンスでhasIncompletePaymentメソッドを使用します。
1if ($user->hasIncompletePayment('default')) {2 // ...3}4 5if ($user->subscription('default')->hasIncompletePayment()) {6 // ...7}
サブスクリプションに未完了の支払いがある場合、latestPayment識別子を渡して、ユーザーをCashierの支払い確認ページに誘導する必要があります。この識別子を取得するには、サブスクリプションインスタンスで利用可能なlatestPaymentメソッドを使用できます。
1<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">2 Please confirm your payment.3</a>
サブスクリプションがpast_dueまたはincompleteの状態でもアクティブと見なしたい場合は、Cashierが提供するkeepPastDueSubscriptionsActiveおよびkeepIncompleteSubscriptionsActiveメソッドを使用できます。通常、これらのメソッドは、App\Providers\AppServiceProviderのregisterメソッドで呼び出す必要があります。
1use Laravel\Cashier\Cashier; 2 3/** 4 * Register any application services. 5 */ 6public function register(): void 7{ 8 Cashier::keepPastDueSubscriptionsActive(); 9 Cashier::keepIncompleteSubscriptionsActive();10}
サブスクリプションがincomplete状態の場合、支払いが確認されるまで変更できません。そのため、サブスクリプションがincomplete状態の場合、swapおよびupdateQuantityメソッドは例外をスローします。
サブスクリプションスコープ
ほとんどのサブスクリプション状態はクエリスコープとしても利用できるため、特定の状態のサブスクリプションをデータベースで簡単にクエリできます。
1// Get all active subscriptions...2$subscriptions = Subscription::query()->active()->get();3 4// Get all of the canceled subscriptions for a user...5$subscriptions = $user->subscriptions()->canceled()->get();
利用可能なスコープの完全なリストは以下の通りです。
1Subscription::query()->active(); 2Subscription::query()->canceled(); 3Subscription::query()->ended(); 4Subscription::query()->incomplete(); 5Subscription::query()->notCanceled(); 6Subscription::query()->notOnGracePeriod(); 7Subscription::query()->notOnTrial(); 8Subscription::query()->onGracePeriod(); 9Subscription::query()->onTrial();10Subscription::query()->pastDue();11Subscription::query()->recurring();
価格の変更
顧客がアプリケーションに登録した後、時々新しいサブスクリプション価格に変更したい場合があります。顧客を新しい価格に切り替えるには、Stripe価格のIDをswapメソッドに渡します。価格を切り替える際、以前にキャンセルされていた場合は、ユーザーがサブスクリプションを再開したいと想定されます。指定された価格IDは、Stripeダッシュボードで利用可能なStripe価格IDに対応している必要があります。
1use App\Models\User;2 3$user = App\Models\User::find(1);4 5$user->subscription('default')->swap('price_yearly');
顧客がトライアル期間中の場合、トライアル期間は維持されます。さらに、サブスクリプションに「数量」が存在する場合、その数量も維持されます。
価格を切り替えて、顧客が現在利用中のトライアル期間をキャンセルしたい場合は、skipTrialメソッドを呼び出すことができます。
1$user->subscription('default')2 ->skipTrial()3 ->swap('price_yearly');
価格を切り替えて、次回の請求サイクルを待たずにすぐに顧客に請求したい場合は、swapAndInvoiceメソッドを使用できます。
1$user = User::find(1);2 3$user->subscription('default')->swapAndInvoice('price_yearly');
按分
デフォルトでは、Stripeは価格を切り替える際に料金を按分します。noProrateメソッドを使用して、料金を按分せずにサブスクリプションの価格を更新できます。
1$user->subscription('default')->noProrate()->swap('price_yearly');
サブスクリプションの按分に関する詳細については、Stripeのドキュメントを参照してください。
swapAndInvoiceメソッドの前にnoProrateメソッドを実行しても、按分には影響しません。インボイスは常に発行されます。
サブスクリプションの数量
サブスクリプションは「数量」に影響されることがあります。たとえば、プロジェクト管理アプリケーションでは、プロジェクトごとに月額10ドルを請求する場合があります。incrementQuantityメソッドとdecrementQuantityメソッドを使用して、サブスクリプションの数量を簡単に増減できます。
1use App\Models\User; 2 3$user = User::find(1); 4 5$user->subscription('default')->incrementQuantity(); 6 7// Add five to the subscription's current quantity... 8$user->subscription('default')->incrementQuantity(5); 9 10$user->subscription('default')->decrementQuantity();11 12// Subtract five from the subscription's current quantity...13$user->subscription('default')->decrementQuantity(5);
あるいは、updateQuantityメソッドを使用して特定の数量を設定することもできます。
1$user->subscription('default')->updateQuantity(10);
noProrateメソッドを使用して、料金を按分せずにサブスクリプションの数量を更新できます。
1$user->subscription('default')->noProrate()->updateQuantity(10);
サブスクリプションの数量に関する詳細については、Stripeのドキュメントを参照してください。
複数製品を持つサブスクリプションの数量
サブスクリプションが複数製品を持つサブスクリプションの場合、増減メソッドの2番目の引数として数量を増減したい価格のIDを渡す必要があります。
1$user->subscription('default')->incrementQuantity(1, 'price_chat');
複数製品を持つサブスクリプション
複数製品を持つサブスクリプションでは、1つのサブスクリプションに複数の請求製品を割り当てることができます。たとえば、月額10ドルの基本サブスクリプション価格を持つ顧客サービス「ヘルプデスク」アプリケーションを構築していて、追加で月額15ドルのライブチャットアドオン製品を提供しているとします。複数製品を持つサブスクリプションの情報は、Cashierのsubscription_itemsデータベーステーブルに保存されます。
newSubscriptionメソッドの2番目の引数として価格の配列を渡すことで、特定のサブスクリプションに複数の製品を指定できます。
1use Illuminate\Http\Request; 2 3Route::post('/user/subscribe', function (Request $request) { 4 $request->user()->newSubscription('default', [ 5 'price_monthly', 6 'price_chat', 7 ])->create($request->paymentMethodId); 8 9 // ...10});
上記の例では、顧客はdefaultサブスクリプションに2つの価格が添付されます。両方の価格は、それぞれの請求間隔で請求されます。必要に応じて、quantityメソッドを使用して、各価格の特定の数量を指定できます。
1$user = User::find(1);2 3$user->newSubscription('default', ['price_monthly', 'price_chat'])4 ->quantity(5, 'price_chat')5 ->create($paymentMethod);
既存のサブスクリプションに別の価格を追加したい場合は、サブスクリプションのaddPriceメソッドを呼び出すことができます。
1$user = User::find(1);2 3$user->subscription('default')->addPrice('price_chat');
上記の例では、新しい価格が追加され、顧客は次回の請求サイクルで請求されます。すぐに顧客に請求したい場合は、addPriceAndInvoiceメソッドを使用できます。
1$user->subscription('default')->addPriceAndInvoice('price_chat');
特定の数量を持つ価格を追加したい場合は、addPriceまたはaddPriceAndInvoiceメソッドの2番目の引数として数量を渡すことができます。
1$user = User::find(1);2 3$user->subscription('default')->addPrice('price_chat', 5);
removePriceメソッドを使用して、サブスクリプションから価格を削除できます。
1$user->subscription('default')->removePrice('price_chat');
サブスクリプションの最後の価格を削除することはできません。代わりに、サブスクリプションを単にキャンセルする必要があります。
価格の交換
複数製品を持つサブスクリプションに添付されている価格を変更することもできます。たとえば、顧客がprice_basicサブスクリプションとprice_chatアドオン製品を持っていて、顧客をprice_basicからprice_pro価格にアップグレードしたいとします。
1use App\Models\User;2 3$user = User::find(1);4 5$user->subscription('default')->swap(['price_pro', 'price_chat']);
上記の例を実行すると、price_basicを持つ基になるサブスクリプションアイテムが削除され、price_chatを持つものが保持されます。さらに、price_proの新しいサブスクリプションアイテムが作成されます。
キーと値のペアの配列をswapメソッドに渡すことで、サブスクリプションアイテムのオプションを指定することもできます。たとえば、サブスクリプション価格の数量を指定する必要がある場合があります。
1$user = User::find(1);2 3$user->subscription('default')->swap([4 'price_pro' => ['quantity' => 5],5 'price_chat'6]);
サブスクリプションの単一の価格を交換したい場合は、サブスクリプションアイテム自体のswapメソッドを使用することで行うことができます。このアプローチは、サブスクリプションの他の価格の既存のメタデータをすべて保持したい場合に特に便利です。
1$user = User::find(1);2 3$user->subscription('default')4 ->findItemOrFail('price_basic')5 ->swap('price_pro');
按分
デフォルトでは、Stripeは複数製品を持つサブスクリプションに価格を追加または削除する際に料金を按分します。按分なしで価格調整を行いたい場合は、価格操作にnoProrateメソッドをチェーンする必要があります。
1$user->subscription('default')->noProrate()->removePrice('price_chat');
数量
個々のサブスクリプション価格の数量を更新したい場合は、メソッドに追加の引数として価格のIDを渡すことで、既存の数量メソッドを使用して行うことができます。
1$user = User::find(1);2 3$user->subscription('default')->incrementQuantity(5, 'price_chat');4 5$user->subscription('default')->decrementQuantity(3, 'price_chat');6 7$user->subscription('default')->updateQuantity(10, 'price_chat');
サブスクリプションに複数の価格がある場合、Subscriptionモデルのstripe_price属性とquantity属性はnullになります。個々の価格属性にアクセスするには、Subscriptionモデルで利用可能なitemsリレーションシップを使用する必要があります。
サブスクリプションアイテム
サブスクリプションに複数の価格がある場合、データベースのsubscription_itemsテーブルに複数のサブスクリプション「アイテム」が保存されます。これらには、サブスクリプションのitemsリレーションシップを介してアクセスできます。
1use App\Models\User;2 3$user = User::find(1);4 5$subscriptionItem = $user->subscription('default')->items->first();6 7// Retrieve the Stripe price and quantity for a specific item...8$stripePrice = $subscriptionItem->stripe_price;9$quantity = $subscriptionItem->quantity;
findItemOrFailメソッドを使用して、特定の価格を取得することもできます。
1$user = User::find(1);2 3$subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat');
複数のサブスクリプション
Stripeでは、顧客が同時に複数のサブスクリプションを持つことができます。たとえば、スイミングサブスクリプションとウェイトリフティングサブスクリプションを提供するジムを運営していて、各サブスクリプションの価格が異なる場合があります。もちろん、顧客はどちらか一方または両方のプランに登録できる必要があります。
アプリケーションがサブスクリプションを作成する際、newSubscriptionメソッドにサブスクリプションのタイプを指定できます。タイプは、ユーザーが開始するサブスクリプションのタイプを表す任意の文字列にすることができます。
1use Illuminate\Http\Request;2 3Route::post('/swimming/subscribe', function (Request $request) {4 $request->user()->newSubscription('swimming')5 ->price('price_swimming_monthly')6 ->create($request->paymentMethodId);7 8 // ...9});
この例では、顧客のために月額のスイミングサブスクリプションを開始しました。しかし、後で年額サブスクリプションに切り替えたいと思うかもしれません。顧客のサブスクリプションを調整する際、swimmingサブスクリプションの価格を単に交換することができます。
1$user->subscription('swimming')->swap('price_swimming_yearly');
もちろん、サブスクリプションを完全にキャンセルすることもできます。
1$user->subscription('swimming')->cancel();
従量制課金
従量制課金では、請求サイクル中の製品使用量に基づいて顧客に課金できます。たとえば、顧客が1か月間に送信するテキストメッセージやメールの数に基づいて課金できます。
従量課金を開始するには、まずStripeダッシュボードで従量課金モデルとメーターを持つ新しい製品を作成する必要があります。メーターを作成した後、関連するイベント名とメーターIDを保存します。これらは使用量を報告および取得するために必要になります。次に、meteredPriceメソッドを使用して、顧客のサブスクリプションに従量価格IDを追加します。
1use Illuminate\Http\Request;2 3Route::post('/user/subscribe', function (Request $request) {4 $request->user()->newSubscription('default')5 ->meteredPrice('price_metered')6 ->create($request->paymentMethodId);7 8 // ...9});
Stripe Checkoutを介して従量制サブスクリプションを開始することもできます。
1$checkout = Auth::user()2 ->newSubscription('default', [])3 ->meteredPrice('price_metered')4 ->checkout();5 6return view('your-checkout-view', [7 'checkout' => $checkout,8]);
使用量の報告
顧客がアプリケーションを使用するにつれて、正確に請求できるように、使用量をStripeに報告します。従量イベントの使用量を報告するには、BillableモデルでreportMeterEventメソッドを使用します。
1$user = User::find(1);2 3$user->reportMeterEvent('emails-sent');
デフォルトでは、請求期間に「使用量」1が追加されます。あるいは、請求期間の顧客の使用量に追加する特定の「使用量」を渡すこともできます。
1$user = User::find(1);2 3$user->reportMeterEvent('emails-sent', quantity: 15);
顧客のメーターのイベントサマリーを取得するには、BillableインスタンスのmeterEventSummariesメソッドを使用します。
1$user = User::find(1);2 3$meterUsage = $user->meterEventSummaries($meterId);4 5$meterUsage->first()->aggregated_value // 10
メーターイベントサマリーの詳細については、Stripeのメーターイベントサマリーオブジェクトのドキュメントを参照してください。
すべてのメーターを一覧表示するには、Billableインスタンスのmetersメソッドを使用できます。
1$user = User::find(1);2 3$user->meters();
サブスクリプションの税金
税率を手動で計算する代わりに、Stripe Taxを使用して税金を自動計算できます。
ユーザーがサブスクリプションで支払う税率を指定するには、課金モデルにtaxRatesメソッドを実装し、Stripeの税率IDを含む配列を返す必要があります。これらの税率は、Stripeダッシュボードで定義できます。
1/**2 * The tax rates that should apply to the customer's subscriptions.3 *4 * @return array<int, string>5 */6public function taxRates(): array7{8 return ['txr_id'];9}
taxRatesメソッドを使用すると、顧客ごとに税率を適用できるため、複数の国や税率にまたがるユーザーベースに役立ちます。
複数製品を持つサブスクリプションを提供している場合、課金モデルにpriceTaxRatesメソッドを実装することで、各価格に異なる税率を定義できます。
1/** 2 * The tax rates that should apply to the customer's subscriptions. 3 * 4 * @return array<string, array<int, string>> 5 */ 6public function priceTaxRates(): array 7{ 8 return [ 9 'price_monthly' => ['txr_id'],10 ];11}
taxRatesメソッドは、サブスクリプションの支払いにのみ適用されます。Cashierを使用して「1回限り」の支払いを行う場合は、その時点で手動で税率を指定する必要があります。
税率の同期
taxRatesメソッドによって返されるハードコードされた税率IDを変更しても、ユーザーの既存のサブスクリプションの税設定は変更されません。既存のサブスクリプションの税額を新しいtaxRatesの値で更新したい場合は、ユーザーのサブスクリプションインスタンスでsyncTaxRatesメソッドを呼び出す必要があります。
1$user->subscription('default')->syncTaxRates();
これにより、複数製品を持つサブスクリプションのアイテム税率も同期されます。アプリケーションが複数製品を持つサブスクリプションを提供している場合は、課金モデルが上記で説明したpriceTaxRatesメソッドを実装していることを確認する必要があります。
免税
Cashierは、顧客が免税対象かどうかを判断するためのisNotTaxExempt、isTaxExempt、reverseChargeAppliesメソッドも提供しています。これらのメソッドは、Stripe APIを呼び出して顧客の免税ステータスを判断します。
1use App\Models\User;2 3$user = User::find(1);4 5$user->isTaxExempt();6$user->isNotTaxExempt();7$user->reverseChargeApplies();
これらのメソッドは、どのLaravel\Cashier\Invoiceオブジェクトでも利用できます。ただし、Invoiceオブジェクトで呼び出された場合、メソッドはインボイスが作成された時点での免税ステータスを判断します。
サブスクリプションのアンカー日
デフォルトでは、請求サイクルのアンカーはサブスクリプションが作成された日、またはトライアル期間が使用されている場合はトライアルが終了する日です。請求アンカー日を変更したい場合は、anchorBillingCycleOnメソッドを使用できます。
1use Illuminate\Http\Request; 2 3Route::post('/user/subscribe', function (Request $request) { 4 $anchor = Carbon::parse('first day of next month'); 5 6 $request->user()->newSubscription('default', 'price_monthly') 7 ->anchorBillingCycleOn($anchor->startOfDay()) 8 ->create($request->paymentMethodId); 9 10 // ...11});
サブスクリプションの請求サイクルの管理に関する詳細については、Stripeの請求サイクルに関するドキュメントを参照してください。
サブスクリプションのキャンセル
サブスクリプションをキャンセルするには、ユーザーのサブスクリプションでcancelメソッドを呼び出します。
1$user->subscription('default')->cancel();
サブスクリプションがキャンセルされると、Cashierは自動的にsubscriptionsデータベーステーブルのends_atカラムを設定します。このカラムは、subscribedメソッドがfalseを返し始めるタイミングを知るために使用されます。
たとえば、顧客が3月1日にサブスクリプションをキャンセルしたが、サブスクリプションが3月5日まで終了する予定ではなかった場合、subscribedメソッドは3月5日までtrueを返し続けます。これは、ユーザーが通常、請求サイクルの終わりまでアプリケーションを使用し続けることが許可されているためです。
ユーザーがサブスクリプションをキャンセルしたが、まだ「猶予期間」にあるかどうかを判断するには、onGracePeriodメソッドを使用します。
1if ($user->subscription('default')->onGracePeriod()) {2 // ...3}
サブスクリプションをすぐにキャンセルしたい場合は、ユーザーのサブスクリプションでcancelNowメソッドを呼び出します。
1$user->subscription('default')->cancelNow();
サブスクリプションをすぐにキャンセルし、未請求の従量制使用量や新規/保留中の按分請求項目を請求したい場合は、ユーザーのサブスクリプションでcancelNowAndInvoiceメソッドを呼び出します。
1$user->subscription('default')->cancelNowAndInvoice();
特定の時点でサブスクリプションをキャンセルすることもできます。
1$user->subscription('default')->cancelAt(2 now()->addDays(10)3);
最後に、関連するユーザーモデルを削除する前に、必ずユーザーのサブスクリプションをキャンセルしてください。
1$user->subscription('default')->cancelNow();2 3$user->delete();
サブスクリプションの再開
顧客がサブスクリプションをキャンセルし、それを再開したい場合は、サブスクリプションでresumeメソッドを呼び出すことができます。サブスクリプションを再開するには、顧客がまだ「猶予期間」内である必要があります。
1$user->subscription('default')->resume();
顧客がサブスクリプションをキャンセルし、サブスクリプションが完全に期限切れになる前にそのサブスクリプションを再開した場合、顧客はすぐに請求されることはありません。代わりに、サブスクリプションは再開され、元の請求サイクルで請求されます。
サブスクリプションのトライアル
支払い方法を事前に登録
支払い方法情報を事前に収集しながら顧客にトライアル期間を提供したい場合は、サブスクリプションを作成する際にtrialDaysメソッドを使用する必要があります。
1use Illuminate\Http\Request;2 3Route::post('/user/subscribe', function (Request $request) {4 $request->user()->newSubscription('default', 'price_monthly')5 ->trialDays(10)6 ->create($request->paymentMethodId);7 8 // ...9});
このメソッドは、データベース内のサブスクリプションレコードにトライアル期間終了日を設定し、Stripeにこの日以降まで顧客への請求を開始しないように指示します。trialDaysメソッドを使用する場合、CashierはStripeで価格に設定されているデフォルトのトライアル期間を上書きします。
トライアル終了日前に顧客のサブスクリプションがキャンセルされない場合、トライアルが終了するとすぐに課金されるため、ユーザーにトライアル終了日を通知するようにしてください。
trialUntilメソッドを使用すると、トライアル期間がいつ終了するかを指定するDateTimeインスタンスを提供できます。
1use Carbon\Carbon;2 3$user->newSubscription('default', 'price_monthly')4 ->trialUntil(Carbon::now()->addDays(10))5 ->create($paymentMethod);
ユーザーインスタンスのonTrialメソッドまたはサブスクリプションインスタンスのonTrialメソッドのいずれかを使用して、ユーザーがトライアル期間中かどうかを判断できます。以下の2つの例は同等です。
1if ($user->onTrial('default')) {2 // ...3}4 5if ($user->subscription('default')->onTrial()) {6 // ...7}
endTrialメソッドを使用して、サブスクリプションのトライアルをすぐに終了できます。
1$user->subscription('default')->endTrial();
既存のトライアルが期限切れになったかどうかを判断するには、hasExpiredTrialメソッドを使用できます。
1if ($user->hasExpiredTrial('default')) {2 // ...3}4 5if ($user->subscription('default')->hasExpiredTrial()) {6 // ...7}
Stripe / Cashierでのトライアル日数の定義
Stripeダッシュボードで価格のトライアル日数を定義するか、Cashierを使用して常に明示的に渡すかを選択できます。Stripeで価格のトライアル日数を定義することを選択した場合、過去にサブスクリプションを持っていた顧客の新規サブスクリプションを含む新規サブスクリプションは、明示的にskipTrial()メソッドを呼び出さない限り、常にトライアル期間が付与されることに注意してください。
支払い方法を事前に登録しない
ユーザーの支払い方法情報を事前に収集せずにトライアル期間を提供したい場合は、ユーザーレコードのtrial_ends_atカラムに希望のトライアル終了日を設定できます。これは通常、ユーザー登録時に行われます。
1use App\Models\User;2 3$user = User::create([4 // ...5 'trial_ends_at' => now()->addDays(10),6]);
課金モデルのクラス定義内で、trial_ends_at属性に日付キャストを追加してください。
Cashierでは、この種のトライアルを「ジェネリックトライアル」と呼びます。これは、既存のどのサブスクリプションにも関連付けられていないためです。課金モデルインスタンスのonTrialメソッドは、現在の日付がtrial_ends_atの値を過ぎていない場合にtrueを返します。
1if ($user->onTrial()) {2 // User is within their trial period...3}
ユーザーの実際のサブスクリプションを作成する準備ができたら、通常どおりnewSubscriptionメソッドを使用できます。
1$user = User::find(1);2 3$user->newSubscription('default', 'price_monthly')->create($paymentMethod);
ユーザーのトライアル終了日を取得するには、trialEndsAtメソッドを使用できます。このメソッドは、ユーザーがトライアル中の場合はCarbonの日付インスタンスを返し、そうでない場合はnullを返します。デフォルト以外の特定のサブスクリプションのトライアル終了日を取得したい場合は、オプションのサブスクリプションタイプパラメータを渡すこともできます。
1if ($user->onTrial()) {2 $trialEndsAt = $user->trialEndsAt('main');3}
ユーザーが「ジェネリック」トライアル期間中であり、まだ実際のサブスクリプションを作成していないことを特に知りたい場合は、onGenericTrialメソッドを使用することもできます。
1if ($user->onGenericTrial()) {2 // User is within their "generic" trial period...3}
トライアルの延長
extendTrialメソッドを使用すると、サブスクリプションが作成された後で、サブスクリプションのトライアル期間を延長できます。トライアルがすでに期限切れで、顧客がすでにサブスクリプションの請求を受けている場合でも、延長されたトライアルを提供できます。トライアル期間中に費やされた時間は、顧客の次回の請求書から差し引かれます。
1use App\Models\User; 2 3$subscription = User::find(1)->subscription('default'); 4 5// End the trial 7 days from now... 6$subscription->extendTrial( 7 now()->addDays(7) 8); 9 10// Add an additional 5 days to the trial...11$subscription->extendTrial(12 $subscription->trial_ends_at->addDays(5)13);
Stripe Webhookの処理
Stripe CLIを使用して、ローカル開発中にWebhookをテストできます。
Stripeは、Webhookを介してアプリケーションにさまざまなイベントを通知できます。デフォルトでは、CashierのWebhookコントローラーを指すルートは、Cashierサービスプロバイダーによって自動的に登録されます。このコントローラーは、すべての受信Webhookリクエストを処理します。
デフォルトでは、CashierのWebhookコントローラーは、失敗した支払いが多すぎるサブスクリプションのキャンセル(Stripeの設定で定義)、顧客の更新、顧客の削除、サブスクリプションの更新、支払い方法の変更を自動的に処理します。しかし、すぐにご覧になるように、このコントローラーを拡張して、好きなStripe Webhookイベントを処理することができます。
アプリケーションがStripe Webhookを処理できるようにするには、StripeコントロールパネルでWebhook URLを設定してください。デフォルトでは、CashierのWebhookコントローラーは/stripe/webhook URLパスに応答します。Stripeコントロールパネルで有効にする必要があるすべてのWebhookの完全なリストは次のとおりです。
customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedcustomer.updatedcustomer.deletedpayment_method.automatically_updatedinvoice.payment_action_requiredinvoice.payment_succeeded
便宜上、Cashierにはcashier:webhook Artisanコマンドが含まれています。このコマンドは、Cashierが必要とするすべてのイベントをリッスンするWebhookをStripeに作成します。
1php artisan cashier:webhook
デフォルトでは、作成されたWebhookはAPP_URL環境変数で定義されたURLと、Cashierに含まれるcashier.webhookルートを指します。別のURLを使用したい場合は、コマンドを呼び出す際に--urlオプションを指定できます。
1php artisan cashier:webhook --url "https://example.com/stripe/webhook"
作成されるWebhookは、お使いのCashierのバージョンと互換性のあるStripe APIバージョンを使用します。別のStripeバージョンを使用したい場合は、--api-versionオプションを指定できます。
1php artisan cashier:webhook --api-version="2019-12-03"
作成後、Webhookはすぐに有効になります。Webhookを作成するが、準備ができるまで無効にしておきたい場合は、コマンドを呼び出す際に--disabledオプションを指定できます。
1php artisan cashier:webhook --disabled
Cashierに含まれるWebhook署名検証ミドルウェアで、受信するStripe Webhookリクエストを保護してください。
WebhookとCSRF保護
StripeのWebhookはLaravelのCSRF保護をバイパスする必要があるため、Laravelが受信するStripeのWebhookのCSRFトークンを検証しようとしないようにする必要があります。これを実現するには、アプリケーションのbootstrap/app.phpファイルでstripe/*をCSRF保護から除外する必要があります。
1->withMiddleware(function (Middleware $middleware) {2 $middleware->validateCsrfTokens(except: [3 'stripe/*',4 ]);5})
Webhookイベントハンドラの定義
Cashierは、失敗した支払いによるサブスクリプションのキャンセルやその他の一般的なStripe Webhookイベントを自動的に処理します。ただし、追加で処理したいWebhookイベントがある場合は、Cashierがディスパッチする以下のイベントをリッスンすることで行うことができます。
Laravel\Cashier\Events\WebhookReceivedLaravel\Cashier\Events\WebhookHandled
どちらのイベントにも、Stripe Webhookの完全なペイロードが含まれています。たとえば、invoice.payment_succeeded Webhookを処理したい場合は、イベントを処理するリスナーを登録できます。
1<?php 2 3namespace App\Listeners; 4 5use Laravel\Cashier\Events\WebhookReceived; 6 7class StripeEventListener 8{ 9 /**10 * Handle received Stripe webhooks.11 */12 public function handle(WebhookReceived $event): void13 {14 if ($event->payload['type'] === 'invoice.payment_succeeded') {15 // Handle the incoming event...16 }17 }18}
Webhook署名の検証
Webhookを保護するには、StripeのWebhook署名を使用できます。便宜上、Cashierには、受信したStripe Webhookリクエストが有効であることを検証するミドルウェアが自動的に含まれています。
Webhookの検証を有効にするには、アプリケーションの.envファイルにSTRIPE_WEBHOOK_SECRET環境変数が設定されていることを確認してください。Webhookのsecretは、Stripeアカウントのダッシュボードから取得できます。
1回限りの支払い
シンプルな支払い
顧客に対して1回限りの支払いを行いたい場合は、課金モデルインスタンスでchargeメソッドを使用できます。chargeメソッドの2番目の引数として、支払い方法IDを提供する必要があります。
1use Illuminate\Http\Request;2 3Route::post('/purchase', function (Request $request) {4 $stripeCharge = $request->user()->charge(5 100, $request->paymentMethodId6 );7 8 // ...9});
chargeメソッドは、3番目の引数として配列を受け入れ、基になるStripeの支払い作成に好きなオプションを渡すことができます。支払い作成時に利用できるオプションに関する詳細情報は、Stripeのドキュメントで確認できます。
1$user->charge(100, $paymentMethod, [2 'custom_option' => $value,3]);
基になる顧客やユーザーなしでchargeメソッドを使用することもできます。これを実現するには、アプリケーションの課金モデルの新しいインスタンスでchargeメソッドを呼び出します。
1use App\Models\User;2 3$stripeCharge = (new User)->charge(100, $paymentMethod);
chargeメソッドは、支払いが失敗した場合に例外をスローします。支払いが成功した場合、メソッドからLaravel\Cashier\Paymentのインスタンスが返されます。
1try {2 $payment = $user->charge(100, $paymentMethod);3} catch (Exception $e) {4 // ...5}
chargeメソッドは、アプリケーションで使用される通貨の最小単位で支払い額を受け入れます。たとえば、顧客が米ドルで支払う場合、金額はセント単位で指定する必要があります。
インボイス付きの支払い
1回限りの支払いを行い、顧客にPDFインボイスを提供する必要がある場合があります。invoicePriceメソッドでそれが可能です。たとえば、顧客に新しいシャツ5枚を請求してみましょう。
1$user->invoicePrice('price_tshirt', 5);
インボイスは、ユーザーのデフォルトの支払い方法に対してすぐに請求されます。invoicePriceメソッドは、3番目の引数として配列も受け入れます。この配列には、インボイスアイテムの請求オプションが含まれています。メソッドが受け入れる4番目の引数も配列であり、インボイス自体の請求オプションを含める必要があります。
1$user->invoicePrice('price_tshirt', 5, [2 'discounts' => [3 ['coupon' => 'SUMMER21SALE']4 ],5], [6 'default_tax_rates' => ['txr_id'],7]);
invoicePriceと同様に、tabPriceメソッドを使用して、複数のアイテム(インボイスごとに最大250アイテム)を顧客の「タブ」に追加し、その後顧客に請求することで、1回限りの支払いを作成できます。たとえば、顧客にシャツ5枚とマグカップ2個を請求できます。
1$user->tabPrice('price_tshirt', 5);2$user->tabPrice('price_mug', 2);3$user->invoice();
あるいは、invoiceForメソッドを使用して、顧客のデフォルトの支払い方法に対して「1回限り」の請求を行うこともできます。
1$user->invoiceFor('One Time Fee', 500);
invoiceForメソッドは利用可能ですが、事前定義された価格でinvoicePriceおよびtabPriceメソッドを使用することをお勧めします。そうすることで、Stripeダッシュボードで製品ごとの売上に関するより良い分析とデータにアクセスできます。
invoice、invoicePrice、invoiceForメソッドは、失敗した請求の試行を再試行するStripeインボイスを作成します。インボイスが失敗した請求を再試行しないようにしたい場合は、最初の失敗した請求の後にStripe APIを使用してそれらを閉じる必要があります。
Payment Intentの作成
課金モデルインスタンスでpayメソッドを呼び出すことで、新しいStripe Payment Intentを作成できます。このメソッドを呼び出すと、Laravel\Cashier\PaymentインスタンスにラップされたPayment Intentが作成されます。
1use Illuminate\Http\Request;2 3Route::post('/pay', function (Request $request) {4 $payment = $request->user()->pay(5 $request->get('amount')6 );7 8 return $payment->client_secret;9});
Payment Intentを作成した後、クライアントシークレットをアプリケーションのフロントエンドに返し、ユーザーがブラウザで支払いを完了できるようにします。Stripe Payment Intentを使用して完全な支払いフローを構築する方法の詳細については、Stripeのドキュメントを参照してください。
payメソッドを使用する場合、Stripeダッシュボードで有効になっているデフォルトの支払い方法が顧客に利用可能になります。あるいは、特定の支払い方法のみを使用できるようにしたい場合は、payWithメソッドを使用できます。
1use Illuminate\Http\Request;2 3Route::post('/pay', function (Request $request) {4 $payment = $request->user()->payWith(5 $request->get('amount'), ['card', 'bancontact']6 );7 8 return $payment->client_secret;9});
payおよびpayWithメソッドは、アプリケーションで使用される通貨の最小単位で支払い額を受け入れます。たとえば、顧客が米ドルで支払う場合、金額はセント単位で指定する必要があります。
支払いの返金
Stripeの支払いを返金する必要がある場合は、refundメソッドを使用できます。このメソッドは、最初の引数としてStripeのPayment Intent IDを受け入れます。
1$payment = $user->charge(100, $paymentMethodId);2 3$user->refund($payment->id);
インボイス
インボイスの取得
invoicesメソッドを使用して、課金モデルのインボイスの配列を簡単に取得できます。invoicesメソッドは、Laravel\Cashier\Invoiceインスタンスのコレクションを返します。
1$invoices = $user->invoices();
結果に保留中のインボイスを含めたい場合は、invoicesIncludingPendingメソッドを使用できます。
1$invoices = $user->invoicesIncludingPending();
findInvoiceメソッドを使用して、IDで特定のインボイスを取得できます。
1$invoice = $user->findInvoice($invoiceId);
インボイス情報の表示
顧客のインボイスを一覧表示する際、インボイスのメソッドを使用して関連するインボイス情報を表示できます。たとえば、すべてのインボイスを表に一覧表示し、ユーザーが簡単にダウンロードできるようにしたい場合があります。
1<table>2 @foreach ($invoices as $invoice)3 <tr>4 <td>{{ $invoice->date()->toFormattedDateString() }}</td>5 <td>{{ $invoice->total() }}</td>6 <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>7 </tr>8 @endforeach9</table>
次回のインボイス
顧客の次回のインボイスを取得するには、upcomingInvoiceメソッドを使用できます。
1$invoice = $user->upcomingInvoice();
同様に、顧客が複数のサブスクリプションを持っている場合、特定のサブスクリプションの次回のインボイスを取得することもできます。
1$invoice = $user->subscription('default')->upcomingInvoice();
サブスクリプションインボイスのプレビュー
previewInvoiceメソッドを使用すると、価格変更を行う前にインボイスをプレビューできます。これにより、特定の価格変更が行われたときに顧客のインボイスがどのようになるかを判断できます。
1$invoice = $user->subscription('default')->previewInvoice('price_yearly');
previewInvoiceメソッドに価格の配列を渡すことで、複数の新しい価格を持つインボイスをプレビューできます。
1$invoice = $user->subscription('default')->previewInvoice(['price_yearly', 'price_metered']);
インボイスPDFの生成
インボイスPDFを生成する前に、Composerを使用してDompdfライブラリをインストールする必要があります。これはCashierのデフォルトのインボイスレンダラーです。
1composer require dompdf/dompdf
ルートまたはコントローラー内から、downloadInvoiceメソッドを使用して、特定のインボイスのPDFダウンロードを生成できます。このメソッドは、インボイスのダウンロードに必要な適切なHTTPレスポンスを自動的に生成します。
1use Illuminate\Http\Request;2 3Route::get('/user/invoice/{invoice}', function (Request $request, string $invoiceId) {4 return $request->user()->downloadInvoice($invoiceId);5});
デフォルトでは、インボイス上のすべてのデータは、Stripeに保存されている顧客およびインボイスデータから派生します。ファイル名は、app.name設定値に基づいています。ただし、downloadInvoiceメソッドの2番目の引数として配列を提供することで、このデータの一部をカスタマイズできます。この配列を使用すると、会社や製品の詳細などの情報をカスタマイズできます。
1return $request->user()->downloadInvoice($invoiceId, [ 2 'vendor' => 'Your Company', 3 'product' => 'Your Product', 4 'street' => 'Main Str. 1', 5 'location' => '2000 Antwerp, Belgium', 6 'phone' => '+32 499 00 00 00', 8 'url' => 'https://example.com', 9 'vendorVat' => 'BE123456789',10]);
downloadInvoiceメソッドは、3番目の引数を介してカスタムファイル名も許可します。このファイル名には、自動的に.pdfが接尾辞として付けられます。
1return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice'); 2 3<a name="custom-invoice-render"></a> 4#### Custom Invoice Renderer 5 6Cashier also makes it possible to use a custom invoice renderer. By default, Cashier uses the `DompdfInvoiceRenderer` implementation, which utilizes the [dompdf](https://github.com/dompdf/dompdf) PHP library to generate Cashier's invoices. However, you may use any renderer you wish by implementing the `Laravel\Cashier\Contracts\InvoiceRenderer` interface. For example, you may wish to render an invoice PDF using an API call to a third-party PDF rendering service: 7 8use Illuminate\Support\Facades\Http; 9use Laravel\Cashier\Contracts\InvoiceRenderer;10use Laravel\Cashier\Invoice;11 12class ApiInvoiceRenderer implements InvoiceRenderer13{14 /**15 * Render the given invoice and return the raw PDF bytes.16 */17 public function render(Invoice $invoice, array $data = [], array $options = []): string18 {19 $html = $invoice->view($data)->render();20 21 return Http::get('https://example.com/html-to-pdf', ['html' => $html])->get()->body();22 }23}
インボイスレンダラーコントラクトを実装したら、アプリケーションのconfig/cashier.php設定ファイルのcashier.invoices.renderer設定値を更新する必要があります。この設定値は、カスタムレンダラー実装のクラス名に設定する必要があります。
チェックアウト
Cashier Stripeは、Stripe Checkoutもサポートしています。Stripe Checkoutは、支払いを受け付けるためのカスタムページの実装の手間を省き、事前に構築されたホスト型の支払いページを提供します。
以下のドキュメントには、CashierでStripe Checkoutを使い始める方法についての情報が含まれています。Stripe Checkoutについてさらに詳しく知るには、Stripe自身のCheckoutに関するドキュメントを確認することも検討してください。
製品のチェックアウト
課金モデルでcheckoutメソッドを使用すると、Stripeダッシュボード内で作成された既存の商品のチェックアウトを実行できます。checkoutメソッドは、新しいStripe Checkoutセッションを開始します。デフォルトでは、Stripe価格IDを渡す必要があります。
1use Illuminate\Http\Request;2 3Route::get('/product-checkout', function (Request $request) {4 return $request->user()->checkout('price_tshirt');5});
必要であれば、商品の数量を指定することもできます。
1use Illuminate\Http\Request;2 3Route::get('/product-checkout', function (Request $request) {4 return $request->user()->checkout(['price_tshirt' => 15]);5});
顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます。デフォルトでは、ユーザーが購入を正常に完了したかキャンセルすると、homeルートの場所にリダイレクトされますが、success_urlとcancel_urlオプションを使用してカスタムコールバックURLを指定することもできます。
1use Illuminate\Http\Request;2 3Route::get('/product-checkout', function (Request $request) {4 return $request->user()->checkout(['price_tshirt' => 1], [5 'success_url' => route('your-success-route'),6 'cancel_url' => route('your-cancel-route'),7 ]);8});
success_urlチェックアウトオプションを定義する際に、URLを呼び出すときにチェックアウトセッションIDをクエリ文字列パラメータとして追加するようにStripeに指示できます。そのためには、リテラル文字列{CHECKOUT_SESSION_ID}をsuccess_urlのクエリ文字列に追加します。Stripeはこのプレースホルダを実際のチェックアウトセッションIDに置き換えます。
1use Illuminate\Http\Request; 2use Stripe\Checkout\Session; 3use Stripe\Customer; 4 5Route::get('/product-checkout', function (Request $request) { 6 return $request->user()->checkout(['price_tshirt' => 1], [ 7 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}', 8 'cancel_url' => route('checkout-cancel'), 9 ]);10});11 12Route::get('/checkout-success', function (Request $request) {13 $checkoutSession = $request->user()->stripe()->checkout->sessions->retrieve($request->get('session_id'));14 15 return view('checkout.success', ['checkoutSession' => $checkoutSession]);16})->name('checkout-success');
プロモーションコード
デフォルトでは、Stripe Checkoutはユーザーが利用できるプロモーションコードを許可していません。幸いなことに、Checkoutページでこれを有効にする簡単な方法があります。そのためには、allowPromotionCodesメソッドを呼び出すことができます。
1use Illuminate\Http\Request;2 3Route::get('/product-checkout', function (Request $request) {4 return $request->user()5 ->allowPromotionCodes()6 ->checkout('price_tshirt');7});
1回限りの支払いのチェックアウト
Stripeダッシュボードで作成されていないアドホックな商品の簡単な請求を実行することもできます。そのためには、課金モデルでcheckoutChargeメソッドを使用し、請求可能な金額、商品名、およびオプションの数量を渡します。顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます。
1use Illuminate\Http\Request;2 3Route::get('/charge-checkout', function (Request $request) {4 return $request->user()->checkoutCharge(1200, 'T-Shirt', 5);5});
checkoutChargeメソッドを使用すると、Stripeは常にStripeダッシュボードに新しい商品と価格を作成します。したがって、事前にStripeダッシュボードで商品を作成し、代わりにcheckoutメソッドを使用することをお勧めします。
サブスクリプションのチェックアウト
Stripe Checkoutをサブスクリプションに使用するには、Stripeダッシュボードでcustomer.subscription.created webhookを有効にする必要があります。このwebhookは、データベースにサブスクリプションレコードを作成し、関連するすべてのサブスクリプションアイテムを保存します。
Stripe Checkoutを使用してサブスクリプションを開始することもできます。Cashierのサブスクリプションビルダーメソッドでサブスクリプションを定義した後、checkoutメソッドを呼び出すことができます。顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます。
1use Illuminate\Http\Request;2 3Route::get('/subscription-checkout', function (Request $request) {4 return $request->user()5 ->newSubscription('default', 'price_monthly')6 ->checkout();7});
商品のチェックアウトと同様に、成功時とキャンセル時のURLをカスタマイズできます。
1use Illuminate\Http\Request; 2 3Route::get('/subscription-checkout', function (Request $request) { 4 return $request->user() 5 ->newSubscription('default', 'price_monthly') 6 ->checkout([ 7 'success_url' => route('your-success-route'), 8 'cancel_url' => route('your-cancel-route'), 9 ]);10});
もちろん、サブスクリプションのチェックアウトに対してもプロモーションコードを有効にすることができます。
1use Illuminate\Http\Request;2 3Route::get('/subscription-checkout', function (Request $request) {4 return $request->user()5 ->newSubscription('default', 'price_monthly')6 ->allowPromotionCodes()7 ->checkout();8});
残念ながら、Stripe Checkoutはサブスクリプション開始時にすべてのサブスクリプション請求オプションをサポートしているわけではありません。サブスクリプションビルダーでanchorBillingCycleOnメソッドを使用したり、按分処理(proration behavior)や支払い方法(payment behavior)を設定しても、Stripe Checkoutセッション中には何の効果もありません。利用可能なパラメータを確認するには、Stripe Checkout Session APIのドキュメントを参照してください。
Stripe Checkoutと試用期間
もちろん、Stripe Checkoutを使用して完了するサブスクリプションを構築する際に、試用期間を定義することができます。
1$checkout = Auth::user()->newSubscription('default', 'price_monthly')2 ->trialDays(3)3 ->checkout();
ただし、試用期間は少なくとも48時間でなければなりません。これはStripe Checkoutがサポートする最小の試用期間です。
サブスクリプションとWebhook
StripeとCashierはwebhookを介してサブスクリプションのステータスを更新することを忘れないでください。そのため、顧客が支払い情報を入力した後にアプリケーションに戻ったときに、サブスクリプションがまだアクティブになっていない可能性があります。このシナリオを処理するために、支払いまたはサブスクリプションが保留中であることをユーザーに通知するメッセージを表示することをお勧めします。
納税者番号の収集
Checkoutは顧客の納税者番号(Tax ID)の収集もサポートしています。チェックアウトセッションでこれを有効にするには、セッション作成時にcollectTaxIdsメソッドを呼び出します。
1$checkout = $user->collectTaxIds()->checkout('price_tshirt');
このメソッドが呼び出されると、顧客には新しいチェックボックスが表示され、会社として購入しているかどうかを示すことができます。もしそうであれば、納税者番号を提供する機会が与えられます。
アプリケーションのサービスプロバイダで既に自動税金徴収を設定している場合、この機能は自動的に有効になり、collectTaxIdsメソッドを呼び出す必要はありません。
ゲストのチェックアウト
Checkout::guestメソッドを使用すると、「アカウント」を持たないアプリケーションのゲストのためにチェックアウトセッションを開始できます。
1use Illuminate\Http\Request;2use Laravel\Cashier\Checkout;3 4Route::get('/product-checkout', function (Request $request) {5 return Checkout::guest()->create('price_tshirt', [6 'success_url' => route('your-success-route'),7 'cancel_url' => route('your-cancel-route'),8 ]);9});
既存のユーザーのためにチェックアウトセッションを作成する場合と同様に、Laravel\Cashier\CheckoutBuilderインスタンスで利用可能な追加のメソッドを利用して、ゲストチェックアウトセッションをカスタマイズできます。
1use Illuminate\Http\Request; 2use Laravel\Cashier\Checkout; 3 4Route::get('/product-checkout', function (Request $request) { 5 return Checkout::guest() 6 ->withPromotionCode('promo-code') 7 ->create('price_tshirt', [ 8 'success_url' => route('your-success-route'), 9 'cancel_url' => route('your-cancel-route'),10 ]);11});
ゲストチェックアウトが完了した後、Stripeはcheckout.session.completed webhookイベントをディスパッチすることができます。そのため、Stripe webhookを設定して、このイベントが実際にアプリケーションに送信されるようにしてください。Stripeダッシュボードでwebhookが有効になったら、Cashierでwebhookを処理できます。webhookペイロードに含まれるオブジェクトは、顧客の注文を処理するために検査できるcheckoutオブジェクトです。
支払い失敗の処理
サブスクリプションや単発の請求に対する支払いが失敗することがあります。この場合、CashierはLaravel\Cashier\Exceptions\IncompletePayment例外をスローし、これが発生したことを通知します。この例外をキャッチした後、どのように進めるかについて2つの選択肢があります。
まず、Cashierに含まれている専用の支払い確認ページに顧客をリダイレクトすることができます。このページには、Cashierのサービスプロバイダを介して登録された名前付きルートが既に関連付けられています。したがって、IncompletePayment例外をキャッチし、ユーザーを支払い確認ページにリダイレクトすることができます。
1use Laravel\Cashier\Exceptions\IncompletePayment; 2 3try { 4 $subscription = $user->newSubscription('default', 'price_monthly') 5 ->create($paymentMethod); 6} catch (IncompletePayment $exception) { 7 return redirect()->route( 8 'cashier.payment', 9 [$exception->payment->id, 'redirect' => route('home')]10 );11}
支払い確認ページでは、顧客はクレジットカード情報を再入力し、「3Dセキュア」認証など、Stripeが必要とする追加のアクションを実行するように促されます。支払いを確認した後、ユーザーは上記で指定したredirectパラメータによって提供されるURLにリダイレクトされます。リダイレクト時に、message(文字列)とsuccess(整数)のクエリ文字列変数がURLに追加されます。支払いページは現在、以下の支払い方法タイプをサポートしています。
- クレジットカード
- Alipay
- Bancontact
- BECS Direct Debit
- EPS
- Giropay
- iDEAL
- SEPA Direct Debit
あるいは、Stripeに支払い確認を処理させることもできます。この場合、支払い確認ページにリダイレクトする代わりに、StripeダッシュボードでStripeの自動請求メールを設定できます。ただし、IncompletePayment例外がキャッチされた場合は、ユーザーにさらなる支払い確認手順が記載されたメールが届くことを通知する必要があります。
支払い例外は、Billableトレイトを使用するモデルのcharge、invoiceFor、およびinvoiceメソッドでスローされる可能性があります。サブスクリプションを操作する場合、SubscriptionBuilderのcreateメソッド、およびSubscriptionとSubscriptionItemモデルのincrementAndInvoiceとswapAndInvoiceメソッドが不完全な支払いの例外をスローする可能性があります。
既存のサブスクリプションに不完全な支払いがあるかどうかを判断するには、課金モデルまたはサブスクリプションインスタンスでhasIncompletePaymentメソッドを使用します。
1if ($user->hasIncompletePayment('default')) {2 // ...3}4 5if ($user->subscription('default')->hasIncompletePayment()) {6 // ...7}
不完全な支払いの具体的なステータスは、例外インスタンスのpaymentプロパティを調べることで導き出すことができます。
1use Laravel\Cashier\Exceptions\IncompletePayment; 2 3try { 4 $user->charge(1000, 'pm_card_threeDSecure2Required'); 5} catch (IncompletePayment $exception) { 6 // Get the payment intent status... 7 $exception->payment->status; 8 9 // Check specific conditions...10 if ($exception->payment->requiresPaymentMethod()) {11 // ...12 } elseif ($exception->payment->requiresConfirmation()) {13 // ...14 }15}
支払いの確認
一部の支払い方法では、支払いを確認するために追加のデータが必要です。たとえば、SEPAの支払い方法では、支払いプロセス中に追加の「マンデート」データが必要です。このデータをCashierに提供するには、withPaymentConfirmationOptionsメソッドを使用します。
1$subscription->withPaymentConfirmationOptions([2 'mandate_data' => '...',3])->swap('price_xxx');
支払いを確認する際に受け入れられるすべてのオプションを確認するには、Stripe APIのドキュメントを参照してください。
強力な顧客認証(SCA)
あなたのビジネスまたは顧客のいずれかがヨーロッパに拠点を置いている場合、EUの強力な顧客認証(SCA)規制に従う必要があります。これらの規制は、支払い詐欺を防ぐために2019年9月に欧州連合によって課されました。幸いなことに、StripeとCashierはSCA準拠のアプリケーションを構築する準備ができています。
始める前に、StripeのPSD2とSCAに関するガイドと、新しいSCA APIに関するドキュメントを確認してください。
追加の確認が必要な支払い
SCA規制では、支払いを確認し処理するために追加の検証が必要になることがよくあります。これが発生すると、CashierはLaravel\Cashier\Exceptions\IncompletePayment例外をスローし、追加の検証が必要であることを通知します。これらの例外の処理方法に関する詳細は、失敗した支払いの処理に関するドキュメントで確認できます。
StripeまたはCashierによって表示される支払い確認画面は、特定の銀行またはカード発行会社の支払いフローに合わせて調整される場合があり、追加のカード確認、一時的な少額請求、別のデバイスでの認証、またはその他の形式の検証が含まれることがあります。
不完全(Incomplete)および延滞(Past Due)状態
支払いに追加の確認が必要な場合、サブスクリプションはデータベースのカラムstripe_statusに示されるように、incompleteまたはpast_dueの状態のままになります。支払いの確認が完了し、アプリケーションがStripeからwebhookを介して完了通知を受け取るとすぐに、Cashierは顧客のサブスクリプションを自動的にアクティブにします。
incompleteとpast_dueの状態に関する詳細については、これらの状態に関する追加のドキュメントを参照してください。
オフセッションでの支払い通知
SCA規制では、サブスクリプションがアクティブな間でも顧客が支払い詳細を時々確認する必要があるため、Cashierはオフセッションでの支払い確認が必要な場合に顧客に通知を送信できます。例えば、サブスクリプションが更新されるときにこれが発生する可能性があります。Cashierの支払い通知は、CASHIER_PAYMENT_NOTIFICATION環境変数を通知クラスに設定することで有効にできます。デフォルトでは、この通知は無効になっています。もちろん、Cashierにはこの目的のために使用できる通知クラスが含まれていますが、必要であれば独自の通知クラスを自由に提供できます。
1CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment
オフセッションでの支払い確認通知が確実に配信されるようにするには、アプリケーションでStripe webhookが設定されていること、およびStripeダッシュボードでinvoice.payment_action_required webhookが有効になっていることを確認してください。さらに、BillableモデルもLaravelのIlluminate\Notifications\Notifiableトレイトを使用する必要があります。
通知は、顧客が追加の確認が必要な支払いを手動で行っている場合でも送信されます。残念ながら、Stripeはその支払いが手動で行われたか、「オフセッション」であったかを知る方法がありません。しかし、顧客が支払いを確認した後に支払いページにアクセスした場合、単に「支払い成功」というメッセージが表示されます。顧客が誤って同じ支払いを2回確認し、意図しない2回目の請求が発生することはありません。
Stripe SDK
Cashierのオブジェクトの多くは、Stripe SDKオブジェクトのラッパーです。Stripeオブジェクトと直接対話したい場合は、asStripeメソッドを使用して便利に取得できます。
1$stripeSubscription = $subscription->asStripeSubscription();2 3$stripeSubscription->application_fee_percent = 5;4 5$stripeSubscription->save();
updateStripeSubscriptionメソッドを使用して、Stripeサブスクリプションを直接更新することもできます。
1$subscription->updateStripeSubscription(['application_fee_percent' => 5]);
Stripe\StripeClientクライアントを直接使用したい場合は、Cashierクラスでstripeメソッドを呼び出すことができます。たとえば、このメソッドを使用してStripeClientインスタンスにアクセスし、Stripeアカウントから価格のリストを取得できます。
1use Laravel\Cashier\Cashier;2 3$prices = Cashier::stripe()->prices->all();
テスト
Cashierを使用するアプリケーションをテストする場合、Stripe APIへの実際のHTTPリクエストをモックすることができます。しかし、これにはCashier自身の動作を部分的に再実装する必要があります。したがって、テストが実際のStripe APIにヒットするようにすることをお勧めします。これは遅くなりますが、アプリケーションが期待通りに動作しているという信頼性が高まり、遅いテストは独自のPest / PHPUnitテストグループ内に配置することができます。
テストの際には、Cashier自体が既に優れたテストスイートを持っていることを忘れないでください。したがって、Cashierのすべての基盤となる動作ではなく、あなた自身のアプリケーションのサブスクリプションと支払いのフローのテストにのみ焦点を当てるべきです。
始めるには、Stripeシークレットのテストバージョンをphpunit.xmlファイルに追加します。
1<env name="STRIPE_SECRET" value="sk_test_<your-key>"/>
これで、テスト中にCashierと対話するたびに、Stripeのテスト環境に実際のAPIリクエストが送信されます。便宜上、テスト中に使用できるサブスクリプション/価格をStripeのテストアカウントに事前に登録しておく必要があります。
クレジットカードの拒否や失敗など、さまざまな請求シナリオをテストするために、Stripeが提供する広範なテスト用カード番号とトークンを使用できます。