はじめに

Laravel Cashier Stripe は、Stripe’s サブスクリプション請求サービスに対する表現力豊かで流暢なインターフェースを提供します。これは、あなたが書くことを恐れているほとんどすべてのボイラープレートサブスクリプション請求コードを処理します。基本的なサブスクリプション管理に加えて、Cashier はクーポン、サブスクリプションの交換、サブスクリプションの「数量」、キャンセルの猶予期間、さらには請求書 PDF の生成も処理できます。

Cashier のアップグレード

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

破壊的変更を防ぐために、Cashier は固定の Stripe API バージョンを使用します。Cashier 15 は Stripe API バージョン 2023-10-16 を利用しています。Stripe API バージョンは、新しい Stripe 機能や改善を利用するためにマイナーリリースで更新されます。

インストール

まず、Composer パッケージマネージャーを使用して Stripe 用の Cashier パッケージをインストールします:

  1. composer require laravel/cashier

パッケージをインストールした後、vendor:publish Artisan コマンドを使用して Cashier のマイグレーションを公開します:

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

次に、データベースをマイグレーションします:

  1. php artisan migrate

Cashier のマイグレーションは、users テーブルにいくつかの列を追加します。また、すべての顧客のサブスクリプションを保持するための新しい subscriptions テーブルと、複数の価格のサブスクリプション用の subscription_items テーブルも作成します。

希望する場合は、vendor:publish Artisan コマンドを使用して Cashier の設定ファイルを公開することもできます:

  1. php artisan vendor:publish --tag="cashier-config"

最後に、Cashier がすべての Stripe イベントを適切に処理することを確認するために、Cashier の Webhook 処理を設定することを忘れないでください。

Stripe は、Stripe 識別子を保存するために使用される任意の列は大文字と小文字を区別するべきだと推奨しています。したがって、MySQL を使用する場合は、stripe_id 列の照合順序が utf8_bin に設定されていることを確認する必要があります。この件に関する詳細は、Stripe ドキュメントで確認できます。

設定

請求可能モデル

Cashier を使用する前に、請求可能モデル定義に Billable トレイトを追加します。通常、これは App\Models\User モデルになります。このトレイトは、サブスクリプションの作成、クーポンの適用、支払い方法情報の更新など、一般的な請求タスクを実行するためのさまざまなメソッドを提供します:

  1. use Laravel\Cashier\Billable;
  2. class User extends Authenticatable
  3. {
  4. use Billable;
  5. }

Cashier は、請求可能モデルが Laravel に付属する App\Models\User クラスであると仮定します。これを変更したい場合は、useCustomerModel メソッドを介して別のモデルを指定できます。このメソッドは通常、boot クラスの AppServiceProvider メソッド内で呼び出されるべきです:

  1. use App\Models\Cashier\User;
  2. use Laravel\Cashier\Cashier;
  3. /**
  4. * Bootstrap any application services.
  5. */
  6. public function boot(): void
  7. {
  8. Cashier::useCustomerModel(User::class);
  9. }

Laravel の提供する App\Models\User モデル以外のモデルを使用している場合は、Cashier マイグレーションを公開して、代替モデルのテーブル名に合わせて変更する必要があります。

API キー

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

  1. STRIPE_KEY=your-stripe-key
  2. STRIPE_SECRET=your-stripe-secret
  3. STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret

STRIPE_WEBHOOK_SECRET 環境変数がアプリケーションの .env ファイルに定義されていることを確認してください。この変数は、受信した Webhook が実際に Stripe からのものであることを確認するために使用されます。

通貨設定

デフォルトの Cashier 通貨は米ドル (USD) です。アプリケーションの CASHIER_CURRENCY 環境変数を設定することで、デフォルトの通貨を変更できます:

  1. CASHIER_CURRENCY=eur

Cashier の通貨を設定することに加えて、請求書の表示用に金額をフォーマットする際に使用されるロケールを指定することもできます。内部的に、Cashier は PHP の NumberFormatter クラス を利用して通貨ロケールを設定します:

  1. CASHIER_CURRENCY_LOCALE=nl_BE

en 以外のロケールを使用するには、ext-intl PHP 拡張がサーバーにインストールされ、設定されていることを確認してください。

税金設定

Stripe Tax のおかげで、Stripe によって生成されたすべての請求書の税金を自動的に計算することが可能です。自動税計算を有効にするには、アプリケーションの boot メソッド内の calculateTaxes メソッドを呼び出します:

  1. use Laravel\Cashier\Cashier;
  2. /**
  3. * Bootstrap any application services.
  4. */
  5. public function boot(): void
  6. {
  7. Cashier::calculateTaxes();
  8. }

税計算が有効になると、新しいサブスクリプションや生成される一回限りの請求書には自動税計算が適用されます。

この機能が正しく機能するためには、顧客の名前、住所、税 ID などの請求情報が Stripe に同期されている必要があります。これを達成するために、Cashier が提供する 顧客データの同期 および Tax ID メソッドを使用できます。

ロギング

Cashier は、致命的な Stripe エラーをログに記録するために使用されるログチャネルを指定することを許可します。アプリケーションの .env ファイル内で CASHIER_LOGGER 環境変数を定義することで、ログチャネルを指定できます:

  1. CASHIER_LOGGER=stack

Stripe への API 呼び出しによって生成された例外は、アプリケーションのデフォルトのログチャネルを介してログに記録されます。

カスタムモデルの使用

Cashier によって内部で使用されるモデルを拡張するために、自分自身のモデルを定義し、対応する Cashier モデルを拡張することができます:

  1. use Laravel\Cashier\Subscription as CashierSubscription;
  2. class Subscription extends CashierSubscription
  3. {
  4. // ...
  5. }

モデルを定義した後、Laravel\Cashier\Cashier クラスを介して Cashier にカスタムモデルを使用するよう指示できます。通常、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッド内で Cashier にカスタムモデルを通知する必要があります:

  1. use App\Models\Cashier\Subscription;
  2. use App\Models\Cashier\SubscriptionItem;
  3. /**
  4. * Bootstrap any application services.
  5. */
  6. public function boot(): void
  7. {
  8. Cashier::useSubscriptionModel(Subscription::class);
  9. Cashier::useSubscriptionItemModel(SubscriptionItem::class);
  10. }

クイックスタート

製品の販売

Stripe Checkout を利用する前に、Stripe ダッシュボードで固定価格の製品を定義する必要があります。また、Cashier の Webhook 処理を設定する必要があります。

アプリケーションを通じて製品およびサブスクリプション請求を提供することは、圧倒されるかもしれません。しかし、Cashier と Stripe Checkout のおかげで、現代的で堅牢な支払い統合を簡単に構築できます。

非定期的な一回限りの製品に対して顧客に請求するために、Cashier を利用して顧客を Stripe Checkout に誘導し、そこで支払い情報を提供し、購入を確認します。Checkout を介して支払いが完了すると、顧客はアプリケーション内の選択した成功 URL にリダイレクトされます:

  1. use Illuminate\Http\Request;
  2. Route::get('/checkout', function (Request $request) {
  3. $stripePriceId = 'price_deluxe_album';
  4. $quantity = 1;
  5. return $request->user()->checkout([$stripePriceId => $quantity], [
  6. 'success_url' => route('checkout-success'),
  7. 'cancel_url' => route('checkout-cancel'),
  8. ]);
  9. })->name('checkout');
  10. Route::view('/checkout/success', 'checkout.success')->name('checkout-success');
  11. Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');

上記の例のように、Cashier が提供する checkout メソッドを使用して、顧客を特定の「価格識別子」のために Stripe Checkout にリダイレクトします。Stripe を使用する際、「価格」は 特定の製品のために定義された価格 を指します。

必要に応じて、checkout メソッドは自動的に Stripe に顧客を作成し、その Stripe 顧客レコードをアプリケーションの対応するユーザーに接続します。チェックアウトセッションが完了すると、顧客は情報メッセージを表示できる専用の成功またはキャンセルページにリダイレクトされます。

Stripe Checkout にメタデータを提供する

製品を販売する際、完了した注文や購入した製品を Cart および Order モデルを介して追跡することが一般的です。顧客を Stripe Checkout にリダイレクトして購入を完了させる際、顧客がアプリケーションにリダイレクトされる際に、完了した購入を対応する注文に関連付けるために既存の注文識別子を提供する必要があるかもしれません。

これを達成するために、checkout メソッドに metadata の配列を提供できます。ユーザーがチェックアウトプロセスを開始すると、保留中の Order がアプリケーション内で作成されると想像してみましょう。この例の Cart および Order モデルは説明用であり、Cashier によって提供されていません。これらの概念は、アプリケーションのニーズに基づいて自由に実装できます:

  1. use App\Models\Cart;
  2. use App\Models\Order;
  3. use Illuminate\Http\Request;
  4. Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
  5. $order = Order::create([
  6. 'cart_id' => $cart->id,
  7. 'price_ids' => $cart->price_ids,
  8. 'status' => 'incomplete',
  9. ]);
  10. return $request->user()->checkout($order->price_ids, [
  11. 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
  12. 'cancel_url' => route('checkout-cancel'),
  13. 'metadata' => ['order_id' => $order->id],
  14. ]);
  15. })->name('checkout');

上記の例のように、ユーザーがチェックアウトプロセスを開始すると、カート/注文に関連するすべての Stripe 価格識別子を checkout メソッドに提供します。もちろん、アプリケーションは、顧客がアイテムを追加する際にこれらのアイテムを「ショッピングカート」または注文に関連付ける責任があります。また、metadata 配列を介して Stripe Checkout セッションに注文の ID を提供します。最後に、Checkout 成功ルートに CHECKOUT_SESSION_ID テンプレート変数を追加しました。Stripe が顧客をアプリケーションにリダイレクトすると、このテンプレート変数は自動的に Checkout セッション ID で埋められます。

次に、Checkout 成功ルートを構築しましょう。これは、ユーザーが Stripe Checkout を介して購入を完了した後にリダイレクトされるルートです。このルート内で、Stripe Checkout セッション ID と関連する Stripe Checkout インスタンスを取得し、提供されたメタデータにアクセスして顧客の注文を適切に更新できます:

  1. use App\Models\Order;
  2. use Illuminate\Http\Request;
  3. use Laravel\Cashier\Cashier;
  4. Route::get('/checkout/success', function (Request $request) {
  5. $sessionId = $request->get('session_id');
  6. if ($sessionId === null) {
  7. return;
  8. }
  9. $session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);
  10. if ($session->payment_status !== 'paid') {
  11. return;
  12. }
  13. $orderId = $session['metadata']['order_id'] ?? null;
  14. $order = Order::findOrFail($orderId);
  15. $order->update(['status' => 'completed']);
  16. return view('checkout-success', ['order' => $order]);
  17. })->name('checkout-success');

Checkout セッションオブジェクトに含まれるデータに関する詳細は、Stripe のドキュメントを参照してください Checkout セッションオブジェクトに含まれるデータ

サブスクリプションの販売

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 ルートにユーザーを誘導する必要があります:

  1. use Illuminate\Http\Request;
  2. Route::get('/subscription-checkout', function (Request $request) {
  3. return $request->user()
  4. ->newSubscription('default', 'price_basic_monthly')
  5. ->trialDays(5)
  6. ->allowPromotionCodes()
  7. ->checkout([
  8. 'success_url' => route('your-success-route'),
  9. 'cancel_url' => route('your-cancel-route'),
  10. ]);
  11. });

上記の例のように、顧客を 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. @endif
  4. @if ($user->subscribedToPrice('price_basic_monthly'))
  5. <p>You are subscribed to our monthly Basic plan.</p>
  6. @endif

サブスクライブミドルウェアの構築

便利さのために、サブスクライブしたユーザーからのリクエストかどうかを判断する ミドルウェア を作成することをお勧めします。このミドルウェアが定義されると、サブスクライブしていないユーザーがルートにアクセスできないように、簡単にルートに割り当てることができます:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. use Illuminate\Http\Request;
  5. use Symfony\Component\HttpFoundation\Response;
  6. class Subscribed
  7. {
  8. /**
  9. * Handle an incoming request.
  10. */
  11. public function handle(Request $request, Closure $next): Response
  12. {
  13. if (! $request->user()?->subscribed()) {
  14. // Redirect user to billing page and ask them to subscribe...
  15. return redirect('/billing');
  16. }
  17. return $next($request);
  18. }
  19. }

ミドルウェアが定義されたら、ルートに割り当てることができます:

  1. use App\Http\Middleware\Subscribed;
  2. Route::get('/dashboard', function () {
  3. // ...
  4. })->middleware([Subscribed::class]);

顧客が請求プランを管理できるようにする

もちろん、顧客はサブスクリプションプランを別の製品や「ティア」に変更したいかもしれません。これを許可する最も簡単な方法は、顧客を Stripe の 顧客請求ポータル に誘導することです。これにより、顧客は請求書をダウンロードしたり、支払い方法を更新したり、サブスクリプションプランを変更したりできます。

まず、アプリケーション内にユーザーを Laravel ルートに誘導するリンクまたはボタンを定義します。このルートを使用して請求ポータルセッションを開始します:

  1. <a href="{{ route('billing') }}">
  2. Billing
  3. </a>

次に、Stripe 顧客請求ポータルセッションを開始し、ユーザーをポータルにリダイレクトするルートを定義します。redirectToBillingPortal メソッドは、ユーザーがポータルを終了したときに戻るべき URL を受け取ります:

  1. use Illuminate\Http\Request;
  2. Route::get('/billing', function (Request $request) {
  3. return $request->user()->redirectToBillingPortal(route('dashboard'));
  4. })->middleware(['auth'])->name('billing');

Cashier の Webhook 処理を設定している限り、Cashier は Stripe からの受信 Webhook を検査することで、アプリケーションの Cashier 関連のデータベーステーブルを自動的に同期します。たとえば、ユーザーが Stripe の顧客請求ポータルを介してサブスクリプションをキャンセルすると、Cashier は対応する Webhook を受信し、アプリケーションのデータベースでサブスクリプションを「キャンセル済み」としてマークします。

顧客

顧客の取得

顧客の Stripe ID を使用して、Cashier::findBillable メソッドで顧客を取得できます。このメソッドは、請求可能モデルのインスタンスを返します:

  1. use Laravel\Cashier\Cashier;
  2. $user = Cashier::findBillable($stripeId);

顧客の作成

時折、サブスクリプションを開始せずに Stripe 顧客を作成したい場合があります。createAsStripeCustomer メソッドを使用してこれを実現できます:

  1. $stripeCustomer = $user->createAsStripeCustomer();

顧客が Stripe に作成された後、後でサブスクリプションを開始できます。任意の追加の 顧客作成パラメータ を渡すために、オプションの $options 配列を提供できます:

  1. $stripeCustomer = $user->createAsStripeCustomer($options);

請求可能モデルの Stripe 顧客オブジェクトを返したい場合は、asStripeCustomer メソッドを使用できます:

  1. $stripeCustomer = $user->asStripeCustomer();

createOrGetStripeCustomer メソッドは、指定された請求可能モデルの Stripe 顧客オブジェクトを取得するために使用できますが、請求可能モデルがすでに Stripe 内の顧客であるかどうかが不明な場合に使用します。このメソッドは、顧客がまだ存在しない場合に Stripe に新しい顧客を作成します:

  1. $stripeCustomer = $user->createOrGetStripeCustomer();

顧客の更新

時折、追加情報で Stripe 顧客を直接更新したい場合があります。updateStripeCustomer メソッドを使用してこれを実現できます。このメソッドは、顧客更新オプション の配列を受け入れます:

  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. foreach ($transactions as $transaction) {
  4. // Transaction amount...
  5. $amount = $transaction->amount(); // $2.31
  6. // Retrieve the related invoice when available...
  7. $invoice = $transaction->invoice();
  8. }

税 ID

Cashier は、顧客の税 ID を管理する簡単な方法を提供します。たとえば、taxIds メソッドを使用して、顧客に割り当てられたすべての 税 ID をコレクションとして取得できます:

  1. $taxIds = $user->taxIds();

特定の顧客の税 ID をその識別子で取得することもできます:

  1. $taxId = $user->findTaxId('txi_belgium');

有効な タイプ と値を提供することで、新しい税 ID を作成できます:

  1. $taxId = $user->createTaxId('eu_vat', 'BE0123456789');

createTaxId メソッドは、VAT ID を顧客のアカウントに即座に追加します。VAT ID の検証も Stripe によって行われます; ただし、これは非同期プロセスです。検証の更新を通知されるには、customer.tax_id.updated Webhook イベントにサブスクライブし、VAT IDs verification パラメータを確認します。Webhook の処理に関する詳細は、Webhook ハンドラの定義に関するドキュメントを参照してください。

deleteTaxId メソッドを使用して税 ID を削除できます:

  1. $user->deleteTaxId('txi_belgium');

Stripe との顧客データの同期

通常、アプリケーションのユーザーが名前、メールアドレス、または Stripe にも保存されているその他の情報を更新する場合、Stripe に更新を通知する必要があります。これにより、Stripe の情報のコピーがアプリケーションの情報と同期されます。

これを自動化するために、請求可能モデルの updated イベントに反応するイベントリスナーを定義できます。次に、イベントリスナー内でモデルの syncStripeCustomerDetails メソッドを呼び出します:

  1. use App\Models\User;
  2. use function Illuminate\Events\queueable;
  3. /**
  4. * The "booted" method of the model.
  5. */
  6. protected static function booted(): void
  7. {
  8. static::updated(queueable(function (User $customer) {
  9. if ($customer->hasStripeId()) {
  10. $customer->syncStripeCustomerDetails();
  11. }
  12. }));
  13. }

これで、顧客モデルが更新されるたびに、その情報が Stripe と同期されます。便利なことに、Cashier は顧客の初回作成時に自動的に顧客の情報を Stripe と同期します。

顧客情報を Stripe と同期するために使用される列をカスタマイズするには、Cashier が提供するさまざまなメソッドをオーバーライドできます。たとえば、stripeName メソッドをオーバーライドして、Cashier が顧客情報を Stripe に同期する際に考慮すべき「名前」と見なされる属性をカスタマイズできます:

  1. /**
  2. * Get the customer name that should be synced to Stripe.
  3. */
  4. public function stripeName(): string|null
  5. {
  6. return $this->company_name;
  7. }

同様に、stripeEmailstripePhonestripeAddress、および stripePreferredLocales メソッドをオーバーライドできます。これらのメソッドは、Stripe 顧客オブジェクトを更新する際に対応する顧客パラメータに情報を同期します。顧客情報の同期プロセスを完全に制御したい場合は、syncStripeCustomerDetails メソッドをオーバーライドできます。

請求ポータル

Stripe は、顧客がサブスクリプション、支払い方法を管理し、請求履歴を表示できるようにする 請求ポータルの設定を簡単に行う方法 を提供します。請求可能モデルの redirectToBillingPortal メソッドをコントローラーまたはルートから呼び出すことで、ユーザーを請求ポータルにリダイレクトできます:

  1. use Illuminate\Http\Request;
  2. Route::get('/billing-portal', function (Request $request) {
  3. return $request->user()->redirectToBillingPortal();
  4. });

デフォルトでは、ユーザーがサブスクリプションの管理を終えたとき、Stripe 請求ポータル内のリンクを介してアプリケーションの home ルートに戻ることができます。ユーザーが戻るべきカスタム URL を提供するには、redirectToBillingPortal メソッドに URL を引数として渡します:

  1. use Illuminate\Http\Request;
  2. Route::get('/billing-portal', function (Request $request) {
  3. return $request->user()->redirectToBillingPortal(route('billing'));
  4. });

HTTP リダイレクトレスポンスを生成せずに請求ポータルの URL を生成したい場合は、billingPortalUrl メソッドを呼び出すことができます:

  1. $url = $request->user()->billingPortalUrl(route('billing'));

支払い方法

支払い方法の保存

サブスクリプションを作成したり、Stripe で「一回限り」の請求を行ったりするには、支払い方法を保存し、その識別子を Stripe から取得する必要があります。これを達成するためのアプローチは、サブスクリプションまたは一回限りの請求に支払い方法を使用するかどうかによって異なるため、以下で両方を検討します。

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

顧客のクレジットカード情報を将来のサブスクリプションで使用するために保存する場合、Stripe の「セットアップインテント」APIを使用して、顧客の支払い方法の詳細を安全に収集する必要があります。「セットアップインテント」は、顧客の支払い方法を請求する意図を Stripe に示します。Cashier の Billable トレイトには、新しいセットアップインテントを簡単に作成するための createSetupIntent メソッドが含まれています。このメソッドは、顧客の支払い方法の詳細を収集するフォームを表示するルートまたはコントローラーから呼び出す必要があります:

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

セットアップインテントを作成し、ビューに渡した後、そのシークレットを支払い方法を収集する要素に添付する必要があります。たとえば、次の「支払い方法の更新」フォームを考えてみましょう:

  1. <input id="card-holder-name" type="text">
  2. <!-- Stripe Elements Placeholder -->
  3. <div id="card-element"></div>
  4. <button id="card-button" data-secret="{{ $intent->client_secret }}">
  5. Update Payment Method
  6. </button>

次に、Stripe.js ライブラリを使用して、フォームに Stripe Element を添付し、顧客の支払い詳細を安全に収集します:

  1. <script src="https://js.stripe.com/v3/"></script>
  2. <script>
  3. const stripe = Stripe('stripe-public-key');
  4. const elements = stripe.elements();
  5. const cardElement = elements.create('card');
  6. cardElement.mount('#card-element');
  7. </script>

次に、カードを確認し、Stripe の confirmCardSetup メソッド を使用して安全な「支払い方法識別子」を Stripe から取得できます:

  1. const cardHolderName = document.getElementById('card-holder-name');
  2. const cardButton = document.getElementById('card-button');
  3. const clientSecret = cardButton.dataset.secret;
  4. cardButton.addEventListener('click', async (e) => {
  5. const { setupIntent, error } = await stripe.confirmCardSetup(
  6. clientSecret, {
  7. payment_method: {
  8. card: cardElement,
  9. billing_details: { name: cardHolderName.value }
  10. }
  11. }
  12. );
  13. if (error) {
  14. // Display "error.message" to the user...
  15. } else {
  16. // The card has been verified successfully...
  17. }
  18. });

カードが Stripe によって確認された後、結果の setupIntent.payment_method 識別子を Laravel アプリケーションに渡し、顧客に添付できます。支払い方法は、新しい支払い方法として追加するか、デフォルトの支払い方法を更新するために使用できます。また、支払い方法識別子を使用して 新しいサブスクリプションを作成することもできます。

セットアップインテントと顧客の支払い詳細を収集する方法についての詳細は、Stripe が提供するこの概要を確認してください

一回限りの請求用の支払い方法

もちろん、顧客の支払い方法に対して一回限りの請求を行う場合、支払い方法識別子を一度だけ使用する必要があります。Stripe の制限により、顧客の保存されたデフォルトの支払い方法を一回限りの請求に使用することはできません。顧客に Stripe.js ライブラリを使用して支払い方法の詳細を入力させる必要があります。たとえば、次のフォームを考えてみましょう:

  1. <input id="card-holder-name" type="text">
  2. <!-- Stripe Elements Placeholder -->
  3. <div id="card-element"></div>
  4. <button id="card-button">
  5. Process Payment
  6. </button>

このようなフォームを定義した後、Stripe.js ライブラリを使用して、フォームに Stripe Element を添付し、顧客の支払い詳細を安全に収集します:

  1. <script src="https://js.stripe.com/v3/"></script>
  2. <script>
  3. const stripe = Stripe('stripe-public-key');
  4. const elements = stripe.elements();
  5. const cardElement = elements.create('card');
  6. cardElement.mount('#card-element');
  7. </script>

次に、カードを確認し、Stripe の createPaymentMethod メソッド を使用して安全な「支払い方法識別子」を Stripe から取得できます:

  1. const cardHolderName = document.getElementById('card-holder-name');
  2. const cardButton = document.getElementById('card-button');
  3. cardButton.addEventListener('click', async (e) => {
  4. const { paymentMethod, error } = await stripe.createPaymentMethod(
  5. 'card', cardElement, {
  6. billing_details: { name: cardHolderName.value }
  7. }
  8. );
  9. if (error) {
  10. // Display "error.message" to the user...
  11. } else {
  12. // The card has been verified successfully...
  13. }
  14. });

カードが正常に確認された場合、paymentMethod.id を Laravel アプリケーションに渡し、一回限りの請求を処理します。

支払い方法の取得

請求可能モデルインスタンスの 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 メソッドを呼び出します:

  1. if ($user->hasDefaultPaymentMethod()) {
  2. // ...
  3. }

hasPaymentMethod メソッドを使用して、請求可能モデルに少なくとも1つの支払い方法が添付されているかどうかを判断できます:

  1. if ($user->hasPaymentMethod()) {
  2. // ...
  3. }

このメソッドは、請求可能モデルに支払い方法が存在するかどうかを判断します。特定のタイプの支払い方法がモデルに存在するかどうかを判断するには、type を引数としてメソッドに渡します:

  1. if ($user->hasPaymentMethod('sepa_debit')) {
  2. // ...
  3. }

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

updateDefaultPaymentMethod メソッドを使用して、顧客のデフォルトの支払い方法情報を更新できます。このメソッドは、Stripe の支払い方法識別子を受け入れ、新しい支払い方法をデフォルトの請求支払い方法として割り当てます:

  1. $user->updateDefaultPaymentMethod($paymentMethod);

顧客のデフォルトの支払い方法情報を Stripe の顧客のデフォルトの支払い方法情報と同期するには、updateDefaultPaymentMethodFromStripe メソッドを使用できます:

  1. $user->updateDefaultPaymentMethodFromStripe();

顧客のデフォルトの支払い方法は、請求書の作成や新しいサブスクリプションの作成にのみ使用できます。Stripe によって課せられた制限により、一回限りの請求には使用できません。

支払い方法の追加

新しい支払い方法を追加するには、請求可能モデルで addPaymentMethod メソッドを呼び出し、支払い方法識別子を渡します:

  1. $user->addPaymentMethod($paymentMethod);

支払い方法識別子を取得する方法については、支払い方法の保存に関するドキュメントを確認してください。

支払い方法の削除

支払い方法を削除するには、削除したい 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 メソッドを使用してモデルのサブスクリプションを作成できます:

  1. use Illuminate\Http\Request;
  2. Route::post('/user/subscribe', function (Request $request) {
  3. $request->user()->newSubscription(
  4. 'default', 'price_monthly'
  5. )->create($request->paymentMethodId);
  6. // ...
  7. });

newSubscription メソッドに渡される最初の引数は、サブスクリプションの内部タイプである必要があります。アプリケーションが単一のサブスクリプションのみを提供する場合、これを default または primary と呼ぶことができます。このサブスクリプションタイプは、内部アプリケーション使用のためのものであり、ユーザーに表示されることを意図していません。また、スペースを含めるべきではなく、サブスクリプションを作成した後に変更されるべきではありません。2 番目の引数は、ユーザーがサブスクライブする特定の価格です。この値は、Stripe の価格識別子に対応する必要があります。

create メソッドは、Stripe 支払い方法識別子または Stripe PaymentMethod オブジェクトを受け入れ、サブスクリプションを開始し、請求可能モデルの Stripe 顧客 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' => 30
  3. ]);

数量

サブスクリプションを作成する際に価格に特定の[https://stripe.com/docs/billing/subscriptions/quantities]数量を設定したい場合は、サブスクリプションビルダーで`````quantity`````メソッドを呼び出す必要があります:

  1. $user->newSubscription('default', 'price_monthly')
  2. ->quantity(5)
  3. ->create($paymentMethod);

追加の詳細

Stripeがサポートする追加の[https://stripe.com/docs/api/customers/create]顧客または[https://stripe.com/docs/api/subscriptions/create]サブスクリプションオプションを指定したい場合は、`````create`````メソッドに第二および第三の引数として渡すことができます:

  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);

また、[https://stripe.com/docs/billing/subscriptions/discounts/codes]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. // Find an active promotion code ID by its customer facing code...
  4. $promotionCode = $user->findActivePromotionCode('SUMMERSALE');

上記の例では、返された$promotionCodeオブジェクトはLaravel\Cashier\PromotionCodeのインスタンスです。このクラスは、基になるStripe\PromotionCodeオブジェクトを装飾します。couponメソッドを呼び出すことで、プロモーションコードに関連するクーポンを取得できます:

  1. $coupon = $user->findPromotionCode('SUMMERSALE')->coupon();

クーポンインスタンスを使用すると、割引額を決定し、クーポンが固定割引かパーセンテージベースの割引かを判断できます:

  1. if ($coupon->isPercentage()) {
  2. return $coupon->percentOff().'%'; // 21.5%
  3. } else {
  4. return $coupon->amountOff(); // $5.99
  5. }

現在顧客またはサブスクリプションに適用されている割引を取得することもできます:

  1. $discount = $billable->discount();
  2. $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. $subscription->applyCoupon('coupon_id');
  4. $subscription->applyPromotionCode('promotion_code_id');

覚えておいてください、プロモーションコードに割り当てられたStripe API IDを使用し、顧客向けのプロモーションコードではないことを確認してください。顧客またはサブスクリプションには、同時に1つのクーポンまたはプロモーションコードのみを適用できます。

この件に関する詳細については、[https://stripe.com/docs/billing/subscriptions/coupons]クーポンおよび[https://stripe.com/docs/billing/subscriptions/coupons/codes]プロモーションコードに関するStripeのドキュメントを参照してください。

サブスクリプションの追加

すでにデフォルトの支払い方法を持つ顧客にサブスクリプションを追加したい場合は、サブスクリプションビルダーでaddメソッドを呼び出すことができます:

  1. use App\Models\User;
  2. $user = User::find(1);
  3. $user->newSubscription('default', 'price_monthly')->add();

Stripeダッシュボードからのサブスクリプションの作成

Stripeダッシュボード自体からサブスクリプションを作成することもできます。その際、Cashierは新しく追加されたサブスクリプションを同期し、defaultのタイプを割り当てます。ダッシュボードで作成されたサブスクリプションに割り当てられるサブスクリプションタイプをカスタマイズするには、Webhookイベントハンドラーを定義する必要があります。

さらに、Stripeダッシュボードを介して1種類のサブスクリプションのみを作成できます。アプリケーションが異なるタイプを使用する複数のサブスクリプションを提供している場合、Stripeダッシュボードを介して追加できるのは1種類のサブスクリプションのみです。

最後に、アプリケーションが提供するサブスクリプションのタイプごとに常に1つのアクティブなサブスクリプションのみを追加することを確認してください。顧客が2つのdefaultサブスクリプションを持っている場合、Cashierは最新の追加されたサブスクリプションのみを使用し、両方がアプリケーションのデータベースと同期されます。

サブスクリプションのステータスを確認する

顧客がアプリケーションにサブスクリプションを持っている場合、さまざまな便利なメソッドを使用してそのサブスクリプションのステータスを簡単に確認できます。まず、subscribedメソッドは、顧客がアクティブなサブスクリプションを持っている場合、trueを返します。たとえそのサブスクリプションが現在トライアル期間中であってもです。subscribedメソッドは、サブスクリプションのタイプを最初の引数として受け取ります:

  1. if ($user->subscribed('default')) {
  2. // ...
  3. }

subscribed メソッドは、ルートミドルウェア の優れた候補でもあり、ユーザーのサブスクリプションステータスに基づいてルートやコントローラーへのアクセスをフィルタリングできます:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. use Illuminate\Http\Request;
  5. use Symfony\Component\HttpFoundation\Response;
  6. class EnsureUserIsSubscribed
  7. {
  8. /**
  9. * Handle an incoming request.
  10. *
  11. * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
  12. */
  13. public function handle(Request $request, Closure $next): Response
  14. {
  15. if ($request->user() && ! $request->user()->subscribed('default')) {
  16. // This user is not a paying customer...
  17. return redirect('/billing');
  18. }
  19. return $next($request);
  20. }
  21. }

ユーザーがまだトライアル期間内にいるかどうかを判断するには、onTrial メソッドを使用できます。このメソッドは、ユーザーにトライアル期間中であることを警告するメッセージを表示する必要があるかどうかを判断するのに役立ちます:

  1. if ($user->subscription('default')->onTrial()) {
  2. // ...
  3. }
  1. ``````php
  2. if ($user->subscribedToProduct('prod_premium', 'default')) {
  3. // ...
  4. }
  5. `
  1. ``````php
  2. if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {
  3. // ...
  4. }
  5. `
  1. ``````php
  2. if ($user->subscribedToPrice('price_basic_monthly', 'default')) {
  3. // ...
  4. }
  5. `
  1. ``````php
  2. if ($user->subscription('default')->recurring()) {
  3. // ...
  4. }
  5. `

ユーザーが同じタイプの2つのサブスクリプションを持っている場合、subscriptionメソッドは常に最新のサブスクリプションを返します。たとえば、ユーザーがdefaultのタイプの2つのサブスクリプションレコードを持っている場合、1つのサブスクリプションは古い期限切れのサブスクリプションであり、もう1つは現在のアクティブなサブスクリプションである可能性があります。最新のサブスクリプションが常に返され、古いサブスクリプションは履歴レビューのためにデータベースに保持されます。

キャンセルされたサブスクリプションのステータス

ユーザーがかつてアクティブなサブスクライバーであったが、サブスクリプションをキャンセルしたかどうかを判断するには、canceled メソッドを使用できます:

  1. if ($user->subscription('default')->canceled()) {
  2. // ...
  3. }

ユーザーがサブスクリプションをキャンセルしたが、サブスクリプションが完全に期限切れになるまで「猶予期間」にあるかどうかを判断することもできます。たとえば、ユーザーが3月5日にサブスクリプションをキャンセルし、そのサブスクリプションが3月10日に期限切れになる予定であった場合、ユーザーは3月10日まで「猶予期間」にあります。この期間中、subscribedメソッドはtrueを返し続けます:

  1. if ($user->subscription('default')->onGracePeriod()) {
  2. // ...
  3. }

ユーザーがサブスクリプションをキャンセルし、「猶予期間」を過ぎているかどうかを判断するには、endedメソッドを使用できます:

  1. if ($user->subscription('default')->ended()) {
  2. // ...
  3. }

不完全および過期のステータス

サブスクリプションが作成後に二次的な支払いアクションを必要とする場合、サブスクリプションはincompleteとしてマークされます。サブスクリプションのステータスは、Cashierのstripe_statusデータベーステーブルのsubscriptions列に保存されます。

同様に、価格を交換する際に二次的な支払いアクションが必要な場合、サブスクリプションはpast_dueとしてマークされます。サブスクリプションがこれらの状態のいずれかにある場合、顧客が支払いを確認するまでアクティブにはなりません。サブスクリプションに不完全な支払いがあるかどうかを判断するには、請求可能モデルまたはサブスクリプションインスタンスでhasIncompletePaymentメソッドを使用できます:

  1. if ($user->hasIncompletePayment('default')) {
  2. // ...
  3. }
  4. if ($user->subscription('default')->hasIncompletePayment()) {
  5. // ...
  6. }

サブスクリプションに不完全な支払いがある場合、顧客をCashierの支払い確認ページに誘導し、latestPayment識別子を渡す必要があります。サブスクリプションインスタンスで利用可能なlatestPaymentメソッドを使用して、この識別子を取得できます:

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

サブスクリプションがpast_dueまたはincompleteの状態にあるときもアクティブと見なされるようにしたい場合は、Cashierが提供するkeepPastDueSubscriptionsActiveおよびkeepIncompleteSubscriptionsActiveメソッドを使用できます。通常、これらのメソッドはregisterメソッドのApp\Providers\AppServiceProviderで呼び出す必要があります:

  1. use Laravel\Cashier\Cashier;
  2. /**
  3. * Register any application services.
  4. */
  5. public function register(): void
  6. {
  7. Cashier::keepPastDueSubscriptionsActive();
  8. Cashier::keepIncompleteSubscriptionsActive();
  9. }

サブスクリプションがincompleteの状態にある場合、支払いが確認されるまで変更できません。したがって、swapおよびupdateQuantityメソッドは、サブスクリプションがincompleteの状態にあるときに例外をスローします。

サブスクリプションスコープ

ほとんどのサブスクリプション状態は、特定の状態にあるサブスクリプションをデータベースで簡単にクエリできるように、クエリスコープとしても利用可能です:

  1. // Get all active subscriptions...
  2. $subscriptions = Subscription::query()->active()->get();
  3. // Get all of the canceled subscriptions for a user...
  4. $subscriptions = $user->subscriptions()->canceled()->get();

利用可能なスコープの完全なリストは以下にあります:

  1. Subscription::query()->active();
  2. Subscription::query()->canceled();
  3. Subscription::query()->ended();
  4. Subscription::query()->incomplete();
  5. Subscription::query()->notCanceled();
  6. Subscription::query()->notOnGracePeriod();
  7. Subscription::query()->notOnTrial();
  8. Subscription::query()->onGracePeriod();
  9. Subscription::query()->onTrial();
  10. Subscription::query()->pastDue();
  11. Subscription::query()->recurring();

価格の変更

顧客がアプリケーションにサブスクリプションしている場合、時折新しいサブスクリプション価格に変更したい場合があります。顧客を新しい価格にスワップするには、Stripe価格の識別子をswapメソッドに渡します。価格を交換する際、ユーザーが以前にキャンセルされた場合、サブスクリプションを再アクティブ化したいと仮定します。指定された価格識別子は、Stripeダッシュボードで利用可能なStripe価格識別子に対応する必要があります:

  1. use App\Models\User;
  2. $user = App\Models\User::find(1);
  3. $user->subscription('default')->swap('price_yearly');

顧客がトライアル中の場合、トライアル期間は維持されます。さらに、サブスクリプションに「数量」が存在する場合、その数量も維持されます。

価格を交換し、顧客が現在トライアル中の期間をキャンセルしたい場合は、skipTrialメソッドを呼び出すことができます:

  1. $user->subscription('default')
  2. ->skipTrial()
  3. ->swap('price_yearly');

価格を交換し、顧客に次の請求サイクルを待たずに請求したい場合は、swapAndInvoiceメソッドを使用できます:

  1. $user = User::find(1);
  2. $user->subscription('default')->swapAndInvoice('price_yearly');

按分

デフォルトでは、Stripeは価格を交換する際に料金を按分します。noProrateメソッドを使用して、料金を按分せずにサブスクリプションの価格を更新できます:

  1. $user->subscription('default')->noProrate()->swap('price_yearly');

サブスクリプションの按分に関する詳細については、Stripeのドキュメントを参照してください。

  1. <a name="subscription-quantity"></a>
  2. ### サブスクリプション数量
  3. 時には、サブスクリプションは「数量」に影響されます。たとえば、プロジェクト管理アプリケーションは、プロジェクトごとに月額10ドルを請求するかもしれません。`````incrementQuantity`````および`````decrementQuantity`````メソッドを使用して、サブスクリプション数量を簡単に増減できます:
  4. ``````php
  5. use App\Models\User;
  6. $user = User::find(1);
  7. $user->subscription('default')->incrementQuantity();
  8. // Add five to the subscription's current quantity...
  9. $user->subscription('default')->incrementQuantity(5);
  10. $user->subscription('default')->decrementQuantity();
  11. // Subtract five from the subscription's current quantity...
  12. $user->subscription('default')->decrementQuantity(5);
  13. `

または、updateQuantityメソッドを使用して特定の数量を設定できます:

  1. $user->subscription('default')->updateQuantity(10);
  1. ``````php
  2. $user->subscription('default')->noProrate()->updateQuantity(10);
  3. `

サブスクリプション数量に関する詳細については、Stripeのドキュメントを参照してください。

Laravel Cashier (Stripe)

複数製品を持つサブスクリプション

[https://stripe.com/docs/billing/subscriptions/multiple-products複数製品を持つサブスクリプション]は、単一のサブスクリプションに複数の請求製品を割り当てることを可能にします。たとえば、月額10ドルの基本サブスクリプションを持ち、追加で月額15ドルのライブチャットアドオン製品を提供するカスタマーサービス「ヘルプデスク」アプリケーションを構築していると想像してください。複数製品を持つサブスクリプションの情報は、Cashierの`````subscription_items`````データベーステーブルに保存されます。

特定のサブスクリプションに対して複数の製品を指定するには、newSubscriptionメソッドの第二引数として価格の配列を渡します:

  1. use Illuminate\Http\Request;
  2. Route::post('/user/subscribe', function (Request $request) {
  3. $request->user()->newSubscription('default', [
  4. 'price_monthly',
  5. 'price_chat',
  6. ])->create($request->paymentMethodId);
  7. // ...
  8. });

上記の例では、顧客はdefaultサブスクリプションに2つの価格が付いています。両方の価格は、それぞれの請求間隔で請求されます。必要に応じて、quantityメソッドを使用して各価格の特定の数量を示すことができます:

  1. $user = User::find(1);
  2. $user->newSubscription('default', ['price_monthly', 'price_chat'])
  3. ->quantity(5, 'price_chat')
  4. ->create($paymentMethod);

既存のサブスクリプションに別の価格を追加したい場合は、サブスクリプションのaddPriceメソッドを呼び出すことができます:

  1. $user = User::find(1);
  2. $user->subscription('default')->addPrice('price_chat');

上記の例では、新しい価格が追加され、顧客は次の請求サイクルで請求されます。顧客に即座に請求したい場合は、addPriceAndInvoiceメソッドを使用できます:

  1. $user->subscription('default')->addPriceAndInvoice('price_chat');

特定の数量を持つ価格を追加したい場合は、addPriceまたはaddPriceAndInvoiceメソッドの第二引数として数量を渡すことができます:

  1. $user = User::find(1);
  2. $user->subscription('default')->addPrice('price_chat', 5);
  1. ``````php
  2. $user->subscription('default')->removePrice('price_chat');
  3. `

サブスクリプションの最後の価格を削除することはできません。代わりに、サブスクリプションをキャンセルする必要があります。

価格の交換

複数製品を持つサブスクリプションに添付された価格を変更することもできます。たとえば、顧客がprice_basicサブスクリプションを持ち、price_chatアドオン製品があり、price_basicからprice_pro価格にアップグレードしたいと想像してください:

  1. use App\Models\User;
  2. $user = User::find(1);
  3. $user->subscription('default')->swap(['price_pro', 'price_chat']);

上記の例を実行すると、price_basicを持つ基になるサブスクリプションアイテムが削除され、price_chatを持つものが保持されます。さらに、price_proの新しいサブスクリプションアイテムが作成されます。

  1. ``````php
  2. $user = User::find(1);
  3. $user->subscription('default')->swap([
  4. 'price_pro' => ['quantity' => 5],
  5. 'price_chat'
  6. ]);
  7. `

サブスクリプションの価格を1つ交換したい場合は、サブスクリプションアイテム自体のswapメソッドを使用して行うことができます。このアプローチは、サブスクリプションの他の価格に関するすべての既存のメタデータを保持したい場合に特に便利です:

  1. $user = User::find(1);
  2. $user->subscription('default')
  3. ->findItemOrFail('price_basic')
  4. ->swap('price_pro');

按分

デフォルトでは、Stripeは複数製品を持つサブスクリプションから価格を追加または削除する際に料金を按分します。按分なしで価格調整を行いたい場合は、価格操作にnoProrateメソッドをチェーンする必要があります:

  1. $user->subscription('default')->noProrate()->removePrice('price_chat');

Laravel Cashier (Stripe)

サブスクリプションアイテム

サブスクリプションに複数の価格がある場合、データベースのsubscription_itemsテーブルに複数のサブスクリプション「アイテム」が保存されます。サブスクリプションのitemsリレーションシップを介してこれらにアクセスできます:

  1. use App\Models\User;
  2. $user = User::find(1);
  3. $subscriptionItem = $user->subscription('default')->items->first();
  4. // Retrieve the Stripe price and quantity for a specific item...
  5. $stripePrice = $subscriptionItem->stripe_price;
  6. $quantity = $subscriptionItem->quantity;
  1. ``````php
  2. $user = User::find(1);
  3. $subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat');
  4. `

複数のサブスクリプション

Stripeは顧客が同時に複数のサブスクリプションを持つことを許可します。たとえば、プールサブスクリプションとウエイトリフティングサブスクリプションを提供するジムを運営している場合、各サブスクリプションには異なる価格が設定される可能性があります。もちろん、顧客はどちらか一方または両方のプランにサブスクリプションできる必要があります。

アプリケーションがサブスクリプションを作成する際、newSubscriptionメソッドにサブスクリプションのタイプを提供できます。タイプは、ユーザーが開始しているサブスクリプションのタイプを表す任意の文字列である可能性があります:

  1. use Illuminate\Http\Request;
  2. Route::post('/swimming/subscribe', function (Request $request) {
  3. $request->user()->newSubscription('swimming')
  4. ->price('price_swimming_monthly')
  5. ->create($request->paymentMethodId);
  6. // ...
  7. });

この例では、顧客のために月額のプールサブスクリプションを開始しました。しかし、後で年額サブスクリプションにスワップしたい場合があります。顧客のサブスクリプションを調整する際、swimmingサブスクリプションの価格を単純にスワップできます:

  1. $user->subscription('swimming')->swap('price_swimming_yearly');

もちろん、サブスクリプションを完全にキャンセルすることもできます:

  1. $user->subscription('swimming')->cancel();

メーター請求

[https://stripe.com/docs/billing/subscriptions/metered-billingメーター請求]は、請求サイクル中の製品使用に基づいて顧客に請求することを可能にします。たとえば、顧客が月に送信するテキストメッセージやメールの数に基づいて請求することができます。

メーター請求を使用するには、まずStripeダッシュボードでメーター価格の新しい製品を作成する必要があります。その後、meteredPriceを使用してメーター価格IDを顧客のサブスクリプションに追加します:

  1. use Illuminate\Http\Request;
  2. Route::post('/user/subscribe', function (Request $request) {
  3. $request->user()->newSubscription('default')
  4. ->meteredPrice('price_metered')
  5. ->create($request->paymentMethodId);
  6. // ...
  7. });

https://stripe.com/docs/billing/subscriptions/metered-billingStripe Checkoutを介してメーターサブスクリプションを開始することもできます:

  1. $checkout = Auth::user()
  2. ->newSubscription('default', [])
  3. ->meteredPrice('price_metered')
  4. ->checkout();
  5. return view('your-checkout-view', [
  6. 'checkout' => $checkout,
  7. ]);

使用状況の報告

顧客がアプリケーションを使用するにつれて、正確に請求されるようにStripeに使用状況を報告します。メーターサブスクリプションの使用状況を増加させるには、reportUsageメソッドを使用できます:

  1. $user = User::find(1);
  2. $user->subscription('default')->reportUsage();

デフォルトでは、「使用量」が1が請求期間に追加されます。代わりに、請求期間の顧客の使用量に追加する特定の「使用量」を渡すことができます:

  1. $user = User::find(1);
  2. $user->subscription('default')->reportUsage(15);

アプリケーションが単一のサブスクリプションで複数の価格を提供している場合、reportUsageForメソッドを使用して、使用状況を報告したいメーター価格を指定する必要があります:

  1. $user = User::find(1);
  2. $user->subscription('default')->reportUsageFor('price_metered', 15);

時には、以前に報告した使用状況を更新する必要があるかもしれません。これを実現するには、reportUsageに第二のパラメータとしてタイムスタンプまたはDateTimeInterfaceインスタンスを渡すことができます。そうすることで、Stripeはその時点で報告された使用状況を更新します。指定された日付と時刻が現在の請求期間内である限り、以前の使用状況レコードを更新し続けることができます:

  1. $user = User::find(1);
  2. $user->subscription('default')->reportUsage(5, $timestamp);

使用状況レコードの取得

顧客の過去の使用状況を取得するには、サブスクリプションインスタンスのusageRecordsメソッドを使用できます:

  1. $user = User::find(1);
  2. $usageRecords = $user->subscription('default')->usageRecords();

アプリケーションが単一のサブスクリプションで複数の価格を提供している場合、usageRecordsForメソッドを使用して、使用状況レコードを取得したいメーター価格を指定できます:

  1. $user = User::find(1);
  2. $usageRecords = $user->subscription('default')->usageRecordsFor('price_metered');
  1. ``````php
  2. @foreach ($usageRecords as $usageRecord)
  3. - Period Starting: {{ $usageRecord['period']['start'] }}
  4. - Period Ending: {{ $usageRecord['period']['end'] }}
  5. - Total Usage: {{ $usageRecord['total_usage'] }}
  6. @endforeach
  7. `

返されるすべての使用データの完全なリファレンスと、Stripeのカーソルベースのページネーションの使用方法については、[https://stripe.com/docs/api/usage_records/subscription_item_summary_list公式Stripe APIドキュメント]を参照してください。

サブスクリプション税

税率を手動で計算する代わりに、[https://dashboard.stripe.com/test/tax-ratesStripe Taxを使用して自動的に税金を計算する]ことができます。

サブスクリプションに対してユーザーが支払う税率を指定するには、請求可能モデルでtaxRatesメソッドを実装し、Stripe税率IDを含む配列を返す必要があります。これらの税率は、[https://dashboard.stripe.com/test/tax-ratesStripeダッシュボード]で定義できます:

  1. /**
  2. * The tax rates that should apply to the customer's subscriptions.
  3. *
  4. * @return array<int, string>
  5. */
  6. public function taxRates(): array
  7. {
  8. return ['txr_id'];
  9. }
  1. 複数製品を持つサブスクリプションを提供している場合は、請求可能モデルで`````priceTaxRates`````メソッドを実装することで、各価格に異なる税率を定義できます:
  2. ``````php
  3. /**
  4. * The tax rates that should apply to the customer's subscriptions.
  5. *
  6. * @return array<string, array<int, string>>
  7. */
  8. public function priceTaxRates(): array
  9. {
  10. return [
  11. 'price_monthly' => ['txr_id'],
  12. ];
  13. }
  14. `
  1. <a name="syncing-tax-rates"></a>
  2. #### 税率の同期
  3. `````taxRates`````メソッドによって返されるハードコーディングされた税率IDを変更すると、ユーザーの既存のサブスクリプションの税設定はそのままになります。新しい`````taxRates`````値で既存のサブスクリプションの税値を更新したい場合は、ユーザーのサブスクリプションインスタンスで`````syncTaxRates`````メソッドを呼び出す必要があります:
  4. ``````php
  5. $user->subscription('default')->syncTaxRates();
  6. `

これにより、複数製品を持つサブスクリプションのアイテム税率も同期されます。アプリケーションが複数製品を持つサブスクリプションを提供している場合は、請求可能モデルが上記で説明したpriceTaxRatesメソッドを実装していることを確認してください。

税の免除

Cashierは、顧客が税免除であるかどうかを判断するためにisNotTaxExemptisTaxExempt、およびreverseChargeAppliesメソッドも提供します。これらのメソッドは、顧客の税免除ステータスを判断するためにStripe APIを呼び出します:

  1. use App\Models\User;
  2. $user = User::find(1);
  3. $user->isTaxExempt();
  4. $user->isNotTaxExempt();
  5. $user->reverseChargeApplies();

これらのメソッドは、任意のLaravel\Cashier\Invoiceオブジェクトでも利用可能です。ただし、Invoiceオブジェクトで呼び出された場合、メソッドは請求書が作成された時点での免除ステータスを判断します。

サブスクリプションのアンカーデート

デフォルトでは、請求サイクルのアンカーはサブスクリプションが作成された日付、またはトライアル期間が使用されている場合はトライアルが終了する日付です。請求アンカーデートを変更したい場合は、anchorBillingCycleOnメソッドを使用できます:

  1. use Illuminate\Http\Request;
  2. Route::post('/user/subscribe', function (Request $request) {
  3. $anchor = Carbon::parse('first day of next month');
  4. $request->user()->newSubscription('default', 'price_monthly')
  5. ->anchorBillingCycleOn($anchor->startOfDay())
  6. ->create($request->paymentMethodId);
  7. // ...
  8. });

サブスクリプションの請求サイクルを管理する方法についての詳細は、[https://stripe.com/docs/billing/subscriptions/billing-cycleStripe請求サイクルのドキュメント]を参照してください。

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

サブスクリプションをキャンセルするには、ユーザーのサブスクリプションでcancelメソッドを呼び出します:

  1. $user->subscription('default')->cancel();

サブスクリプションがキャンセルされると、Cashierは自動的にends_at列をsubscriptionsデータベーステーブルに設定します。この列は、subscribedメソッドがfalseを返し始める時期を知るために使用されます。

たとえば、顧客が3月1日にサブスクリプションをキャンセルしたが、サブスクリプションは3月5日まで終了する予定であった場合、subscribedメソッドは3月5日までtrueを返し続けます。これは、ユーザーが通常、請求サイクルの終了までアプリケーションを使用し続けることが許可されているためです。

ユーザーがサブスクリプションをキャンセルしたが、「猶予期間」にあるかどうかを判断するには、onGracePeriodメソッドを使用できます:

  1. if ($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. $user->delete();

サブスクリプションの再開

顧客がサブスクリプションをキャンセルし、それを再開したい場合は、サブスクリプションでresumeメソッドを呼び出すことができます。顧客はサブスクリプションを再開するために「猶予期間」にある必要があります:

  1. $user->subscription('default')->resume();

顧客がサブスクリプションをキャンセルし、そのサブスクリプションが完全に期限切れになる前に再開した場合、顧客は即座に請求されません。代わりに、サブスクリプションは再アクティブ化され、元の請求サイクルで請求されます。

サブスクリプショントライアル

支払い方法を事前に提供する場合

顧客にトライアル期間を提供しながら、事前に支払い方法情報を収集したい場合は、サブスクリプションを作成する際にtrialDaysメソッドを使用する必要があります:

  1. use Illuminate\Http\Request;
  2. Route::post('/user/subscribe', function (Request $request) {
  3. $request->user()->newSubscription('default', 'price_monthly')
  4. ->trialDays(10)
  5. ->create($request->paymentMethodId);
  6. // ...
  7. });

このメソッドは、データベース内のサブスクリプションレコードにトライアル期間の終了日を設定し、Stripeにこの日以降に顧客の請求を開始しないよう指示します。trialDaysメソッドを使用する場合、CashierはStripeで設定された価格のデフォルトのトライアル期間を上書きします。

顧客のサブスクリプションがトライアル終了日までにキャンセルされない場合、トライアルが終了するとすぐに請求されるため、ユーザーにトライアル終了日を通知することを確認してください。

  1. ``````php
  2. use Carbon\Carbon;
  3. $user->newSubscription('default', 'price_monthly')
  4. ->trialUntil(Carbon::now()->addDays(10))
  5. ->create($paymentMethod);
  6. `

ユーザーがトライアル期間内にいるかどうかを判断するには、ユーザーインスタンスのonTrialメソッドまたはサブスクリプションインスタンスのonTrialメソッドを使用できます。以下の2つの例は同等です:

  1. if ($user->onTrial('default')) {
  2. // ...
  3. }
  4. if ($user->subscription('default')->onTrial()) {
  5. // ...
  6. }
  1. ``````php
  2. $user->subscription('default')->endTrial();
  3. `

既存のトライアルが期限切れかどうかを判断するには、hasExpiredTrialメソッドを使用できます:

  1. if ($user->hasExpiredTrial('default')) {
  2. // ...
  3. }
  4. if ($user->subscription('default')->hasExpiredTrial()) {
  5. // ...
  6. }

Stripe / Cashierでのトライアル日数の定義

価格のトライアル日数をStripeダッシュボードで定義するか、Cashierを使用して常に明示的に渡すことを選択できます。価格のトライアル日数をStripeで定義することを選択した場合、過去にサブスクリプションを持っていた顧客を含む新しいサブスクリプションは、skipTrial()メソッドを明示的に呼び出さない限り、常にトライアル期間を受け取ります。

事前に支払い方法を提供しない場合

ユーザーの支払い方法情報を事前に収集せずにトライアル期間を提供したい場合は、ユーザーレコードのtrial_ends_at列を希望するトライアル終了日に設定する必要があります。これは通常、ユーザー登録中に行われます:

  1. use App\Models\User;
  2. $user = User::create([
  3. // ...
  4. 'trial_ends_at' => now()->addDays(10),
  5. ]);
  1. Cashierは、このタイプのトライアルを「一般的なトライアル」と呼びます。これは、既存のサブスクリプションに関連付けられていないためです。請求可能モデルインスタンスの`````onTrial`````メソッドは、現在の日付が`````trial_ends_at`````の値を過ぎていない場合、`````true`````を返します:
  2. ``````php
  3. if ($user->onTrial()) {
  4. // User is within their trial period...
  5. }
  6. `

ユーザーのために実際のサブスクリプションを作成する準備ができたら、newSubscriptionメソッドを通常通り使用できます:

  1. $user = User::find(1);
  2. $user->newSubscription('default', 'price_monthly')->create($paymentMethod);

ユーザーのトライアル終了日を取得するには、trialEndsAtメソッドを使用できます。このメソッドは、ユーザーがトライアル中であればCarbon日付インスタンスを返し、nullでなければ返しません。特定のサブスクリプションのトライアル終了日を取得したい場合は、オプションのサブスクリプションタイプパラメータを渡すこともできます:

  1. if ($user->onTrial()) {
  2. $trialEndsAt = $user->trialEndsAt('main');
  3. }

ユーザーが「一般的な」トライアル期間内にあり、まだ実際のサブスクリプションを作成していないことを特に知りたい場合は、onGenericTrialメソッドを使用できます:

  1. if ($user->onGenericTrial()) {
  2. // User is within their "generic" trial period...
  3. }

トライアルの延長

  1. ``````php
  2. use App\Models\User;
  3. $subscription = User::find(1)->subscription('default');
  4. // End the trial 7 days from now...
  5. $subscription->extendTrial(
  6. now()->addDays(7)
  7. );
  8. // Add an additional 5 days to the trial...
  9. $subscription->extendTrial(
  10. $subscription->trial_ends_at->addDays(5)
  11. );
  12. `

Stripe Webhookの処理

ローカル開発中にWebhookをテストするために、Stripe CLIを使用できます。

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.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • customer.updated
  • customer.deleted
  • payment_method.automatically_updated
  • invoice.payment_action_required
  • invoice.payment_succeeded

便利なことに、Cashierにはcashier:webhook Artisanコマンドが含まれています。このコマンドは、Cashierが必要とするすべてのイベントをリッスンするWebhookをStripeに作成します:

  1. php artisan cashier:webhook

デフォルトでは、作成されたWebhookはAPP_URL環境変数で定義されたURLと、Cashierに含まれるcashier.webhookルートを指します。異なるURLを使用したい場合は、コマンドを呼び出すときに--urlオプションを提供できます:

  1. php artisan cashier:webhook --url "https://example.com/stripe/webhook"

作成されたWebhookは、あなたのCashierのバージョンと互換性のあるStripe APIバージョンを使用します。異なるStripeバージョンを使用したい場合は、--api-versionオプションを提供できます:

  1. php artisan cashier:webhook --api-version="2019-12-03"

作成後、Webhookはすぐにアクティブになります。Webhookを作成したいが、準備ができるまで無効にしておきたい場合は、コマンドを呼び出すときに--disabledオプションを提供できます:

  1. php artisan cashier:webhook --disabled

受信するStripe WebhookリクエストをCashierに含まれる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\WebhookReceived
  • Laravel\Cashier\Events\WebhookHandled

両方のイベントには、Stripe Webhookの完全なペイロードが含まれています。たとえば、invoice.payment_succeeded Webhookを処理したい場合は、イベントを処理するリスナーを登録できます:

  1. <?php
  2. namespace App\Listeners;
  3. use Laravel\Cashier\Events\WebhookReceived;
  4. class StripeEventListener
  5. {
  6. /**
  7. * Handle received Stripe webhooks.
  8. */
  9. public function handle(WebhookReceived $event): void
  10. {
  11. if ($event->payload['type'] === 'invoice.payment_succeeded') {
  12. // Handle the incoming event...
  13. }
  14. }
  15. }

Webhook署名の検証

Webhookを保護するために、StripeのWebhook署名を使用できます。便利なことに、Cashierは受信したStripe Webhookリクエストが有効であることを検証するミドルウェアを自動的に含めています。

Webhook検証を有効にするには、アプリケーションの.envファイルでSTRIPE_WEBHOOK_SECRET環境変数が設定されていることを確認してください。Webhooksecretは、Stripeアカウントのダッシュボードから取得できます。

単一の請求

シンプルな請求

顧客に対して一度限りの請求を行いたい場合は、請求可能なモデルインスタンスのchargeメソッドを使用できます。chargeメソッドの第2引数として支払い方法識別子を提供する必要があります:

  1. use Illuminate\Http\Request;
  2. Route::post('/purchase', function (Request $request) {
  3. $stripeCharge = $request->user()->charge(
  4. 100, $request->paymentMethodId
  5. );
  6. // ...
  7. });
  1. ``````php
  2. $user->charge(100, $paymentMethod, [
  3. 'custom_option' => $value,
  4. ]);
  5. `
  1. ``````php
  2. use App\Models\User;
  3. $stripeCharge = (new User)->charge(100, $paymentMethod);
  4. `
  1. ``````php
  2. try {
  3. $payment = $user->charge(100, $paymentMethod);
  4. } catch (Exception $e) {
  5. // ...
  6. }
  7. `
  1. <a name="charge-with-invoice"></a>
  2. ### 請求書による請求
  3. 時には、一度限りの請求を行い、顧客にPDF請求書を提供する必要があります。`````invoicePrice`````メソッドを使用すると、これを実現できます。たとえば、5枚の新しいシャツの請求書を顧客に発行しましょう:
  4. ``````php
  5. $user->invoicePrice('price_tshirt', 5);
  6. `

請求書は、ユーザーのデフォルトの支払い方法に対して即座に請求されます。invoicePriceメソッドも第三引数として配列を受け入れます。この配列には、請求項目の請求オプションが含まれています。メソッドが受け入れる第4引数も配列で、請求書自体の請求オプションを含む必要があります:

  1. $user->invoicePrice('price_tshirt', 5, [
  2. 'discounts' => [
  3. ['coupon' => 'SUMMER21SALE']
  4. ],
  5. ], [
  6. 'default_tax_rates' => ['txr_id'],
  7. ]);
  1. ``````php
  2. $user->tabPrice('price_tshirt', 5);
  3. $user->tabPrice('price_mug', 2);
  4. $user->invoice();
  5. `

または、invoiceForメソッドを使用して、顧客のデフォルトの支払い方法に対して「一度限り」の請求を行うこともできます:

  1. $user->invoiceFor('One Time Fee', 500);
  1. `````invoice``````````invoicePrice`````、および`````invoiceFor`````メソッドは、失敗した請求の試行を再試行するStripe請求書を作成します。失敗した請求を再試行したくない場合は、最初の失敗した請求の後にStripe APIを使用してそれらを閉じる必要があります。
  2. <a name="creating-payment-intents"></a>
  3. ### 支払い意図の作成
  4. 請求可能なモデルインスタンスの`````pay`````メソッドを呼び出すことで、新しいStripe支払い意図を作成できます。このメソッドを呼び出すと、`````Laravel\Cashier\Payment`````インスタンスでラップされた支払い意図が作成されます:
  5. ``````php
  6. use Illuminate\Http\Request;
  7. Route::post('/pay', function (Request $request) {
  8. $payment = $request->user()->pay(
  9. $request->get('amount')
  10. );
  11. return $payment->client_secret;
  12. });
  13. `

支払い意図を作成した後、クライアントシークレットをアプリケーションのフロントエンドに返して、ユーザーがブラウザで支払いを完了できるようにします。Stripe支払い意図を使用して完全な支払いフローを構築する方法については、Stripeのドキュメントを参照してください。

  1. ``````php
  2. use Illuminate\Http\Request;
  3. Route::post('/pay', function (Request $request) {
  4. $payment = $request->user()->payWith(
  5. $request->get('amount'), ['card', 'bancontact']
  6. );
  7. return $payment->client_secret;
  8. });
  9. `
  1. <a name="refunding-charges"></a>
  2. ### 請求の返金
  3. Stripeの請求を返金する必要がある場合は、`````refund`````メソッドを使用できます。このメソッドは、最初の引数としてStripeの[支払い意図ID](#payment-methods-for-single-charges)を受け入れます:
  4. ``````php
  5. $payment = $user->charge(100, $paymentMethodId);
  6. $user->refund($payment->id);
  7. `

請求書

請求書の取得

請求可能なモデルの請求書の配列を簡単に取得するには、invoicesメソッドを使用します。invoicesメソッドは、Laravel\Cashier\Invoiceインスタンスのコレクションを返します:

  1. $invoices = $user->invoices();

保留中の請求書を結果に含めたい場合は、invoicesIncludingPendingメソッドを使用できます:

  1. $invoices = $user->invoicesIncludingPending();

特定の請求書をIDで取得するには、findInvoiceメソッドを使用できます:

  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. @endforeach
  9. </table>

今後の請求書

顧客の今後の請求書を取得するには、upcomingInvoiceメソッドを使用します:

  1. $invoice = $user->upcomingInvoice();

同様に、顧客が複数のサブスクリプションを持っている場合、特定のサブスクリプションの今後の請求書を取得することもできます:

  1. $invoice = $user->subscription('default')->upcomingInvoice();

サブスクリプション請求書のプレビュー

  1. ``````php
  2. $invoice = $user->subscription('default')->previewInvoice('price_yearly');
  3. `
  1. ``````php
  2. $invoice = $user->subscription('default')->previewInvoice(['price_yearly', 'price_metered']);
  3. `

請求書PDFの生成

請求書PDFを生成する前に、Composerを使用してDompdfライブラリをインストールする必要があります。これはCashierのデフォルトの請求書レンダラーです:

  1. composer require dompdf/dompdf

ルートまたはコントローラー内から、downloadInvoiceメソッドを使用して特定の請求書のPDFダウンロードを生成できます。このメソッドは、請求書をダウンロードするために必要な適切なHTTPレスポンスを自動的に生成します:

  1. use Illuminate\Http\Request;
  2. Route::get('/user/invoice/{invoice}', function (Request $request, string $invoiceId) {
  3. return $request->user()->downloadInvoice($invoiceId);
  4. });

デフォルトでは、請求書のすべてのデータはStripeに保存されている顧客および請求書データから派生します。ファイル名はapp.name設定値に基づいています。ただし、downloadInvoiceメソッドの第2引数として配列を提供することで、一部のデータをカスタマイズできます。この配列を使用して、会社や製品の詳細などの情報をカスタマイズできます:

  1. return $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',
  7. 'email' => '',
  8. 'url' => 'https://example.com',
  9. 'vendorVat' => 'BE123456789',
  10. ]);
  1. ``````php
  2. return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice');
  3. `

カスタム請求書レンダラー

Cashierは、カスタム請求書レンダラーを使用することも可能にします。デフォルトでは、CashierはDompdfInvoiceRenderer実装を使用しており、これはdompdf PHPライブラリを使用してCashierの請求書を生成します。ただし、Laravel\Cashier\Contracts\InvoiceRendererインターフェースを実装することで、任意のレンダラーを使用できます。たとえば、サードパーティのPDFレンダリングサービスへのAPI呼び出しを使用して請求書PDFをレンダリングすることができます:

  1. use Illuminate\Support\Facades\Http;
  2. use Laravel\Cashier\Contracts\InvoiceRenderer;
  3. use Laravel\Cashier\Invoice;
  4. class ApiInvoiceRenderer implements InvoiceRenderer
  5. {
  6. /**
  7. * Render the given invoice and return the raw PDF bytes.
  8. */
  9. public function render(Invoice $invoice, array $data = [], array $options = []): string
  10. {
  11. $html = $invoice->view($data)->render();
  12. return Http::get('https://example.com/html-to-pdf', ['html' => $html])->get()->body();
  13. }
  14. }

請求書レンダラー契約を実装したら、アプリケーションのconfig/cashier.php設定ファイルでcashier.invoices.renderer設定値を更新する必要があります。この設定値は、カスタムレンダラー実装のクラス名に設定する必要があります。

チェックアウト

Cashier Stripeは、Stripe Checkoutのサポートも提供します。Stripe Checkoutは、支払いを受け入れるためのカスタムページを実装する手間を省き、事前に構築されたホストされた支払いページを提供します。

以下のドキュメントには、Cashierを使用してStripe Checkoutを開始する方法に関する情報が含まれています。Stripe Checkoutについて詳しく知りたい場合は、StripeのCheckoutに関するドキュメントも確認してください。

製品チェックアウト

Stripeダッシュボードで作成された既存の製品のチェックアウトを、請求可能なモデルのcheckoutメソッドを使用して実行できます。checkoutメソッドは、新しいStripe Checkoutセッションを開始します。デフォルトでは、Stripe Price IDを渡す必要があります:

  1. use Illuminate\Http\Request;
  2. Route::get('/product-checkout', function (Request $request) {
  3. return $request->user()->checkout('price_tshirt');
  4. });

必要に応じて、製品の数量を指定することもできます:

  1. use Illuminate\Http\Request;
  2. Route::get('/product-checkout', function (Request $request) {
  3. return $request->user()->checkout(['price_tshirt' => 15]);
  4. });

顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます。デフォルトでは、ユーザーが購入を成功裏に完了またはキャンセルすると、homeルートの場所にリダイレクトされますが、success_urlおよびcancel_urlオプションを使用してカスタムコールバックURLを指定できます:

  1. use Illuminate\Http\Request;
  2. Route::get('/product-checkout', function (Request $request) {
  3. return $request->user()->checkout(['price_tshirt' => 1], [
  4. 'success_url' => route('your-success-route'),
  5. 'cancel_url' => route('your-cancel-route'),
  6. ]);
  7. });
  1. ``````php
  2. use Illuminate\Http\Request;
  3. use Stripe\Checkout\Session;
  4. use Stripe\Customer;
  5. Route::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. Route::get('/checkout-success', function (Request $request) {
  12. $checkoutSession = $request->user()->stripe()->checkout->sessions->retrieve($request->get('session_id'));
  13. return view('checkout.success', ['checkoutSession' => $checkoutSession]);
  14. })->name('checkout-success');
  15. `

プロモーションコード

デフォルトでは、Stripe Checkoutはユーザーが引き換え可能なプロモーションコードを許可していません。幸いなことに、これをCheckoutページで有効にする簡単な方法があります。これを実現するには、allowPromotionCodesメソッドを呼び出します:

  1. use Illuminate\Http\Request;
  2. Route::get('/product-checkout', function (Request $request) {
  3. return $request->user()
  4. ->allowPromotionCodes()
  5. ->checkout('price_tshirt');
  6. });

単一の請求チェックアウト

Stripeダッシュボードで作成されていないアドホック製品に対しても、単純な請求を行うことができます。これを実現するには、請求可能なモデルのcheckoutChargeメソッドを使用し、請求可能な金額、製品名、およびオプションの数量を渡します。顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます:

  1. use Illuminate\Http\Request;
  2. Route::get('/charge-checkout', function (Request $request) {
  3. return $request->user()->checkoutCharge(1200, 'T-Shirt', 5);
  4. });
  1. <a name="subscription-checkouts"></a>
  2. ### サブスクリプションチェックアウト
  3. サブスクリプションにStripe Checkoutを使用するには、Stripeダッシュボードで`````customer.subscription.created````` Webhookを有効にする必要があります。このWebhookは、データベースにサブスクリプションレコードを作成し、すべての関連するサブスクリプションアイテムを保存します。
  4. また、Stripe Checkoutを使用してサブスクリプションを開始することもできます。Cashierのサブスクリプションビルダーのメソッドでサブスクリプションを定義した後、`````checkout `````メソッドを呼び出すことができます。顧客がこのルートにアクセスすると、StripeのCheckoutページにリダイレクトされます:
  5. ``````php
  6. use Illuminate\Http\Request;
  7. Route::get('/subscription-checkout', function (Request $request) {
  8. return $request->user()
  9. ->newSubscription('default', 'price_monthly')
  10. ->checkout();
  11. });
  12. `

製品チェックアウトと同様に、成功およびキャンセルのURLをカスタマイズできます:

  1. use Illuminate\Http\Request;
  2. Route::get('/subscription-checkout', function (Request $request) {
  3. return $request->user()
  4. ->newSubscription('default', 'price_monthly')
  5. ->checkout([
  6. 'success_url' => route('your-success-route'),
  7. 'cancel_url' => route('your-cancel-route'),
  8. ]);
  9. });

もちろん、サブスクリプションチェックアウトのためにプロモーションコードを有効にすることもできます:

  1. use Illuminate\Http\Request;
  2. Route::get('/subscription-checkout', function (Request $request) {
  3. return $request->user()
  4. ->newSubscription('default', 'price_monthly')
  5. ->allowPromotionCodes()
  6. ->checkout();
  7. });

残念ながら、Stripe Checkoutはサブスクリプションを開始する際にすべての請求オプションをサポートしていません。サブスクリプションビルダーのanchorBillingCycleOnメソッドを使用して、比例配分の動作を設定したり、支払いの動作を設定したりしても、Stripe Checkoutセッション中には効果がありません。どのパラメータが利用可能かを確認するには、Stripe Checkoutセッション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を介してサブスクリプションのステータスを更新するため、顧客が支払い情報を入力した後にアプリケーションに戻ったときにサブスクリプションがまだアクティブでない可能性があります。このシナリオを処理するために、ユーザーに支払いまたはサブスクリプションが保留中であることを通知するメッセージを表示することをお勧めします。

税IDの収集

Checkoutは、顧客の税IDを収集することもサポートしています。チェックアウトセッションを作成する際に、collectTaxIdsメソッドを呼び出してこれを有効にします:

  1. $checkout = $user->collectTaxIds()->checkout('price_tshirt');

このメソッドが呼び出されると、顧客が会社として購入しているかどうかを示すチェックボックスが新たに利用可能になります。そうであれば、税ID番号を提供する機会が与えられます。

すでにアプリケーションのサービスプロバイダーで自動税収集を設定している場合、この機能は自動的に有効になり、collectTaxIdsメソッドを呼び出す必要はありません。

ゲストチェックアウト

  1. ``````php
  2. use Illuminate\Http\Request;
  3. use Laravel\Cashier\Checkout;
  4. Route::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. });
  10. `

既存のユーザーのためにチェックアウトセッションを作成する場合と同様に、Laravel\Cashier\CheckoutBuilderインスタンスで利用可能な追加のメソッドを使用してゲストチェックアウトセッションをカスタマイズできます:

  1. use Illuminate\Http\Request;
  2. use Laravel\Cashier\Checkout;
  3. Route::get('/product-checkout', function (Request $request) {
  4. return Checkout::guest()
  5. ->withPromotionCode('promo-code')
  6. ->create('price_tshirt', [
  7. 'success_url' => route('your-success-route'),
  8. 'cancel_url' => route('your-cancel-route'),
  9. ]);
  10. });

ゲストチェックアウトが完了すると、Stripeはcheckout.session.completed Webhookイベントを送信できます。したがって、実際にこのイベントをアプリケーションに送信するためにStripe Webhookを設定することを確認してください。WebhookがStripeダッシュボード内で有効になったら、CashierでWebhookを処理できます。Webhookペイロードに含まれるオブジェクトは、顧客の注文を履行するために調査できるcheckoutオブジェクトです。

失敗した支払いの処理

時には、サブスクリプションや単一の請求の支払いが失敗することがあります。この場合、CashierはLaravel\Cashier\Exceptions\IncompletePayment例外をスローし、これが発生したことを通知します。この例外をキャッチした後、進める方法は2つあります。

まず、Cashierに含まれる専用の支払い確認ページに顧客をリダイレクトすることができます。このページには、Cashierのサービスプロバイダーを介して登録された関連する名前付きルートがあります。したがって、IncompletePayment例外をキャッチして、ユーザーを支払い確認ページにリダイレクトできます:

  1. use Laravel\Cashier\Exceptions\IncompletePayment;
  2. try {
  3. $subscription = $user->newSubscription('default', 'price_monthly')
  4. ->create($paymentMethod);
  5. } catch (IncompletePayment $exception) {
  6. return redirect()->route(
  7. 'cashier.payment',
  8. [$exception->payment->id, 'redirect' => route('home')]
  9. );
  10. }

支払い確認ページでは、顧客は再度クレジットカード情報を入力し、Stripeによって要求される追加のアクション(「3Dセキュア」確認など)を実行するように促されます。支払いを確認した後、ユーザーは上記のredirectパラメータで指定されたURLにリダイレクトされます。リダイレクト時に、message(文字列)およびsuccess(整数)クエリ文字列変数がURLに追加されます。支払いページは、次の支払い方法タイプをサポートしています:

  • クレジットカード
  • Alipay
  • Bancontact
  • BECSダイレクトデビット
  • EPS
  • Giropay
  • iDEAL
  • SEPAダイレクトデビット

または、Stripeに支払い確認を処理させることもできます。この場合、支払い確認ページにリダイレクトするのではなく、StripeダッシュボードでStripeの自動請求メールを設定できます。ただし、IncompletePayment例外がキャッチされた場合は、ユーザーにさらなる支払い確認手順が記載されたメールが届くことを通知する必要があります。

支払い例外は、chargeinvoiceForinvoiceメソッドに対してスローされる可能性があります。Billableトレイトを使用しているモデルでサブスクリプションとやり取りする場合、createメソッドはSubscriptionBuilderincrementAndInvoiceおよびswapAndInvoiceメソッドはSubscriptionおよびSubscriptionItemモデルで不完全な支払い例外をスローする可能性があります。

既存のサブスクリプションに不完全な支払いがあるかどうかを判断するには、請求可能なモデルまたはサブスクリプションインスタンスでhasIncompletePaymentメソッドを使用できます:

  1. if ($user->hasIncompletePayment('default')) {
  2. // ...
  3. }
  4. if ($user->subscription('default')->hasIncompletePayment()) {
  5. // ...
  6. }

例外インスタンスのpaymentプロパティを調査することで、不完全な支払いの具体的なステータスを導き出すことができます:

  1. use Laravel\Cashier\Exceptions\IncompletePayment;
  2. try {
  3. $user->charge(1000, 'pm_card_threeDSecure2Required');
  4. } catch (IncompletePayment $exception) {
  5. // Get the payment intent status...
  6. $exception->payment->status;
  7. // Check specific conditions...
  8. if ($exception->payment->requiresPaymentMethod()) {
  9. // ...
  10. } elseif ($exception->payment->requiresConfirmation()) {
  11. // ...
  12. }
  13. }

支払いの確認

一部の支払い方法では、支払いを確認するために追加のデータが必要です。たとえば、SEPA支払い方法では、支払いプロセス中に追加の「委任」データが必要です。このデータをwithPaymentConfirmationOptionsメソッドを使用してCashierに提供できます:

  1. $subscription->withPaymentConfirmationOptions([
  2. 'mandate_data' => '...',
  3. ])->swap('price_xxx');

支払いを確認する際に受け入れられるすべてのオプションを確認するには、Stripe APIドキュメントを参照してください。

強力な顧客認証

ビジネスまたは顧客のいずれかがヨーロッパに拠点を置いている場合、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データベース列によって示されます。Cashierは、支払い確認が完了し、StripeがWebhookを介して完了を通知すると、顧客のサブスクリプションを自動的にアクティブにします。

  1. <a name="off-session-payment-notifications"></a>
  2. ### オフセッション支払い通知
  3. SCA規制により、顧客はサブスクリプションがアクティブである間でも、時折支払い詳細を確認する必要があります。Cashierは、オフセッションの支払い確認が必要な場合に顧客に通知を送信できます。たとえば、サブスクリプションが更新されるときにこれが発生する可能性があります。Cashierの支払い通知は、`````CASHIER_PAYMENT_NOTIFICATION`````環境変数を通知クラスに設定することで有効にできます。デフォルトでは、この通知は無効になっています。もちろん、Cashierにはこの目的のために使用できる通知クラスが含まれていますが、必要に応じて独自の通知クラスを提供することもできます:
  4. ``````ini
  5. CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment
  6. `

オフセッションの支払い確認通知が配信されることを確認するには、アプリケーションのためにStripe Webhookが設定されていることを確認し、invoice.payment_action_required WebhookがStripeダッシュボードで有効になっていることを確認してください。さらに、BillableモデルもLaravelのIlluminate\Notifications\Notifiableトレイトを使用する必要があります。

顧客が追加の確認が必要な支払いを手動で行っている場合でも、通知は送信されます。残念ながら、Stripeは支払いが手動で行われたか「オフセッション」であるかを知る方法はありません。しかし、顧客は支払いを確認した後に支払いページにアクセスすると、「支払い成功」のメッセージが表示されます。顧客は、同じ支払いを誤って二重に確認して二重請求を受けることは許可されません。

Stripe SDK

Cashierの多くのオブジェクトは、Stripe SDKオブジェクトのラッパーです。Stripeオブジェクトと直接やり取りしたい場合は、asStripeメソッドを使用して便利に取得できます:

  1. $stripeSubscription = $subscription->asStripeSubscription();
  2. $stripeSubscription->application_fee_percent = 5;
  3. $stripeSubscription->save();
  1. ``````php
  2. $subscription->updateStripeSubscription(['application_fee_percent' => 5]);
  3. `
  1. ``````php
  2. use Laravel\Cashier\Cashier;
  3. $prices = Cashier::stripe()->prices->all();
  4. `

テスト

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とやり取りするたびに、実際のAPIリクエストがStripeテスト環境に送信されます。便利なことに、テスト中に使用できるサブスクリプション/価格でStripeテストアカウントを事前に埋めておくべきです。

クレジットカードの拒否や失敗など、さまざまな請求シナリオをテストするために、Stripeが提供するテストカード番号とトークンの広範な範囲を使用できます。