コンテンツにスキップ

Laravel Cashier (Stripe)

はじめに

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

Cashierのアップグレード

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

exclamation

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

インストール

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

composer require laravel/cashier

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

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

次に、データベースをマイグレートします。

php artisan migrate

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

必要に応じて、 `vendor:publish` Artisanコマンドを使用してCashierの設定ファイルを公開することもできます。

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

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

exclamation

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

設定

課金可能モデル

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

use Laravel\Cashier\Billable;
 
class User extends Authenticatable
{
use Billable;
}

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

use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useCustomerModel(User::class);
}
exclamation

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

APIキー

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

STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
exclamation

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

通貨設定

デフォルトのCashier通貨は米ドル(USD)です。デフォルトの通貨を変更するには、アプリケーションの `.env` ファイル内で `CASHIER_CURRENCY` 環境変数を設定します。

CASHIER_CURRENCY=eur

Cashierの通貨を設定することに加えて、請求書に表示するために金額を書式設定するときに使用されるロケールを指定することもできます。内部的には、CashierはPHPの `NumberFormatter` クラス を使用して通貨ロケールを設定します。

CASHIER_CURRENCY_LOCALE=nl_BE
exclamation

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

税金設定

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

use Laravel\Cashier\Cashier;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::calculateTaxes();
}

税計算が有効になると、新しく生成されたサブスクリプションと単発請求書はすべて自動的に税計算されます。

この機能が適切に機能するには、顧客の名前、住所、税 ID などの顧客の請求詳細をStripeと同期する必要があります。これを実現するには、Cashierが提供する顧客データ同期および税 IDメソッドを使用できます。

ロギング

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

CASHIER_LOGGER=stack

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

カスタムモデルの使用

独自のモデルを定義し、対応するCashierモデルを拡張することにより、Cashierが内部的に使用するモデルを自由に拡張できます。

use Laravel\Cashier\Subscription as CashierSubscription;
 
class Subscription extends CashierSubscription
{
// ...
}

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

use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}

クイックスタート

商品の販売

lightbulb

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

アプリケーションを介して製品とサブスクリプションの課金をオファーすることは、困難な場合があります。 しかし、Cashier と Stripe Checkout のおかげで、最新の堅牢な支払い統合を簡単に構築できます。

非定期的な単一料金の製品について顧客に課金するには、Cashier を利用して顧客を Stripe Checkout に誘導します。そこで顧客は支払い詳細を提供し、購入を確認します。 Checkout を介して支払いが行われると、顧客はアプリケーション内で選択した成功 URL にリダイレクトされます。

use Illuminate\Http\Request;
 
Route::get('/checkout', function (Request $request) {
$stripePriceId = 'price_deluxe_album';
 
$quantity = 1;
 
return $request->user()->checkout([$stripePriceId => $quantity], [
'success_url' => route('checkout-success'),
'cancel_url' => route('checkout-cancel'),
]);
})->name('checkout');
 
Route::view('/checkout/success', 'checkout.success')->name('checkout-success');
Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');

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

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

Stripe Checkout へのメタデータの提供

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

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

use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
 
Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
$order = Order::create([
'cart_id' => $cart->id,
'price_ids' => $cart->price_ids,
'status' => 'incomplete',
]);
 
return $request->user()->checkout($order->price_ids, [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
'metadata' => ['order_id' => $order->id],
]);
})->name('checkout');

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

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

use App\Models\Order;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
 
Route::get('/checkout/success', function (Request $request) {
$sessionId = $request->get('session_id');
 
if ($sessionId === null) {
return;
}
 
$session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);
 
if ($session->payment_status !== 'paid') {
return;
}
 
$orderId = $session['metadata']['order_id'] ?? null;
 
$order = Order::findOrFail($orderId);
 
$order->update(['status' => 'completed']);
 
return view('checkout-success', ['order' => $order]);
})->name('checkout-success');

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

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

lightbulb

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

アプリケーションを介して製品とサブスクリプションの課金をオファーすることは、困難な場合があります。 しかし、Cashier と Stripe Checkout のおかげで、最新の堅牢な支払い統合を簡単に構築できます。

Cashier と Stripe Checkout を使用してサブスクリプションを販売する方法を学ぶために、基本的な月額(price_basic_monthly)と年間(price_basic_yearly)プランを持つサブスクリプションサービスの簡単なシナリオを考えてみましょう。 これらの 2 つの価格は、Stripe ダッシュボードの「基本」製品(pro_basic)にグループ化できます。 さらに、サブスクリプションサービスは、pro_expert としてエキスパートプランを提供する場合があります。

まず、顧客がサービスにどのようにサブスクライブできるかを見てみましょう。 もちろん、顧客がアプリケーションの価格設定ページで基本プランの「サブスクライブ」ボタンをクリックする可能性があると想像できます。 このボタンまたはリンクは、選択したプランの Stripe Checkout セッションを作成する Laravel ルートにユーザーを誘導する必要があります。

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_basic_monthly')
->trialDays(5)
->allowPromotionCodes()
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

上記の例でわかるように、顧客を基本プランにサブスクライブできる Stripe Checkout セッションにリダイレクトします。 チェックアウトの成功またはキャンセル後、顧客は checkout メソッドに提供した URL にリダイレクトされます。 サブスクリプションが実際に開始された時期を知るには(一部の支払い方法では処理に数秒かかるため)、Cashier の webhook 処理を設定する必要もあります。

顧客がサブスクリプションを開始できるようになったので、アプリケーションの特定の部分を制限して、サブスクライブしたユーザーのみがアクセスできるようにする必要があります。 もちろん、Cashier の Billable トレイトによって提供される subscribed メソッドを使用して、ユーザーの現在のサブスクリプションステータスをいつでも確認できます。

@if ($user->subscribed())
<p>You are subscribed.</p>
@endif

ユーザーが特定の製品または価格にサブスクライブしているかどうかを簡単に判断することもできます。

@if ($user->subscribedToProduct('pro_basic'))
<p>You are subscribed to our Basic product.</p>
@endif
 
@if ($user->subscribedToPrice('price_basic_monthly'))
<p>You are subscribed to our monthly Basic plan.</p>
@endif

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

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

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class Subscribed
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirect user to billing page and ask them to subscribe...
return redirect('/billing');
}
 
return $next($request);
}
}

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

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

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

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

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

<a href="{{ route('billing') }}">
Billing
</a>

次に、Stripe Customer Billing Portal セッションを開始し、ユーザーをポータルにリダイレクトするルートを定義しましょう。 redirectToBillingPortal メソッドは、ユーザーがポータルを終了したときに返される URL を受け入れます。

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

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

顧客

顧客の取得

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

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

顧客の作成

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

$stripeCustomer = $user->createAsStripeCustomer();

Stripe で顧客が作成されたら、後日サブスクリプションを開始できます。 オプションの $options 配列を提供して、Stripe API でサポートされている追加の顧客作成パラメータ を渡すことができます

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

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

$stripeCustomer = $user->asStripeCustomer();

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

$stripeCustomer = $user->createOrGetStripeCustomer();

顧客の更新

場合によっては、追加情報を使用して Stripe 顧客を直接更新したい場合があります。 updateStripeCustomer メソッドを使用してこれを実現できます。 このメソッドは、Stripe API でサポートされている顧客更新オプション の配列を受け入れます

$stripeCustomer = $user->updateStripeCustomer($options);

残高

Stripe を使用すると、顧客の「残高」を入金または引き落とすことができます。 後で、この残高は新しい請求書で入金または引き落とされます。 顧客の合計残高を確認するには、請求可能モデルで使用可能な balance メソッドを使用できます。 balance メソッドは、顧客の通貨で残高の書式設定された文字列表現を返します

$balance = $user->balance();

顧客の残高を入金するには、creditBalance メソッドに値を指定します。 必要に応じて、説明を提供することもできます

$user->creditBalance(500, 'Premium customer top-up.');

debitBalance メソッドに値を指定すると、顧客の残高が引き落とされます

$user->debitBalance(300, 'Bad usage penalty.');

applyBalance メソッドは、顧客の新しい顧客残高トランザクションを作成します。 これらのトランザクションレコードは、balanceTransactions メソッドを使用して取得できます。これは、顧客がレビューするためのクレジットとデビットのログを提供するのに役立ちます

// Retrieve all transactions...
$transactions = $user->balanceTransactions();
 
foreach ($transactions as $transaction) {
// Transaction amount...
$amount = $transaction->amount(); // $2.31
 
// Retrieve the related invoice when available...
$invoice = $transaction->invoice();
}

税 ID

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

$taxIds = $user->taxIds();

識別子を使用して、顧客の特定の税 ID を取得することもできます

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

有効な タイプ と値を createTaxId メソッドに提供することで、新しい税 ID を作成できます

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

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

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

$user->deleteTaxId('txi_belgium');

Stripeと顧客データの同期

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

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

use App\Models\User;
use function Illuminate\Events\queueable;
 
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::updated(queueable(function (User $customer) {
if ($customer->hasStripeId()) {
$customer->syncStripeCustomerDetails();
}
}));
}

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

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

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

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

請求ポータル

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

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

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

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

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

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

支払方法

支払方法の保存

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

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

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

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

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

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

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

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

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

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
 
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
 
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});

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

lightbulb

Setup Intentsと顧客の支払い詳細の収集について詳しくは、Stripeが提供するこの概要をご覧ください。

単一請求の支払い方法

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

<input id="card-holder-name" type="text">
 
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
 
<button id="card-button">
Process Payment
</button>

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

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

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

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
 
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
 
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});

カードが正常に確認されたら、paymentMethod.idをLaravelアプリケーションに渡して、単一の請求を処理できます。

支払方法の取得

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

$paymentMethods = $user->paymentMethods();

デフォルトでは、このメソッドはすべてのタイプの支払い方法を返します。特定のタイプの支払い方法を取得するには、`type`を引数としてメソッドに渡します。

$paymentMethods = $user->paymentMethods('sepa_debit');

顧客のデフォルトの支払い方法を取得するには、`defaultPaymentMethod`メソッドを使用できます。

$paymentMethod = $user->defaultPaymentMethod();

`findPaymentMethod`メソッドを使用して、課金対象モデルに添付されている特定の支払い方法を取得できます。

$paymentMethod = $user->findPaymentMethod($paymentMethodId);

支払方法の有無

課金対象モデルにデフォルトの支払い方法がアカウントに添付されているかどうかを判断するには、`hasDefaultPaymentMethod`メソッドを呼び出します。

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

課金対象モデルに少なくとも1つの支払い方法がアカウントに添付されているかどうかを判断するには、`hasPaymentMethod`メソッドを使用できます。

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

このメソッドは、課金対象モデルに支払い方法が überhaupt 存在するかどうかを判断します。モデルに特定のタイプの支払い方法が存在するかどうかを判断するには、`type`を引数としてメソッドに渡します。

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

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

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

$user->updateDefaultPaymentMethod($paymentMethod);

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

$user->updateDefaultPaymentMethodFromStripe();
exclamation

顧客のデフォルトの支払い方法は、請求と新しいサブスクリプションの作成にのみ使用できます。Stripeによる制限のため、単一の請求には使用できません。

支払方法の追加

新しい支払い方法を追加するには、課金対象モデルの`addPaymentMethod`メソッドを呼び出し、支払い方法識別子を渡します。

$user->addPaymentMethod($paymentMethod);
lightbulb

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

支払方法の削除

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

$paymentMethod->delete();

`deletePaymentMethod`メソッドは、課金対象モデルから特定の支払い方法を削除します。

$user->deletePaymentMethod('pm_visa');

`deletePaymentMethods`メソッドは、課金対象モデルのすべての支払い方法情報を削除します。

$user->deletePaymentMethods();

デフォルトでは、このメソッドはすべてのタイプの支払い方法を削除します。特定のタイプの支払い方法を削除するには、`type`を引数としてメソッドに渡すことができます。

$user->deletePaymentMethods('sepa_debit');
exclamation

ユーザーがアクティブなサブスクリプションを持っている場合、アプリケーションはデフォルトの支払い方法を削除することを許可してはなりません。

サブスクリプション

サブスクリプションは、顧客の定期的な支払いを設定する方法を提供します。Cashierによって管理されるStripeサブスクリプションは、複数のサブスクリプション価格、サブスクリプション数量、試用版などをサポートしています。

サブスクリプションの作成

サブスクリプションを作成するには、まず課金対象モデルのインスタンスを取得します。これは通常、`App\Models\User`のインスタンスになります。モデルインスタンスを取得したら、`newSubscription`メソッドを使用してモデルのサブスクリプションを作成できます。

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

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

Stripeの支払い方法識別子またはStripeの`PaymentMethod`オブジェクトを受け取る`create`メソッドは、サブスクリプションを開始し、課金対象モデルのStripe顧客IDおよびその他の関連する請求情報でデータベースを更新します。

exclamation

支払い方法識別子を`create`サブスクリプションメソッドに直接渡すと、ユーザーの保存された支払い方法にも自動的に追加されます。

請求書メールによる定期的な支払いの収集

顧客の定期的な支払いを自動的に徴収する代わりに、定期的な支払いが発生するたびに顧客に請求書をメールで送信するようにStripeに指示できます。その後、顧客は請求書を受け取ったら手動で支払うことができます。請求書を介して定期的な支払いを徴収する場合、顧客は前もって支払い方法を提供する必要はありません。

$user->newSubscription('default', 'price_monthly')->createAndSendInvoice();

サブスクリプションがキャンセルされるまでに顧客が請求書を支払う必要がある時間は、`days_until_due`オプションによって決まります。デフォルトでは、これは30日です。ただし、必要に応じて、このオプションに特定の値を指定できます。

$user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [
'days_until_due' => 30
]);

数量

サブスクリプションを作成するときに価格に特定の数量を設定する場合は、サブスクリプションを作成する前にサブスクリプションビルダーで`quantity`メソッドを呼び出す必要があります。

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

追加の詳細

Stripeでサポートされている追加の顧客またはサブスクリプションオプションを指定する場合は、`create`メソッドの2番目と3番目の引数として渡すことで指定できます。

$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
], [
'metadata' => ['note' => 'Some extra information.'],
]);

クーポン

サブスクリプションを作成するときにクーポンを適用する場合は、`withCoupon`メソッドを使用できます。

$user->newSubscription('default', 'price_monthly')
->withCoupon('code')
->create($paymentMethod);

または、Stripeプロモーションコードを適用する場合は、`withPromotionCode`メソッドを使用できます。

$user->newSubscription('default', 'price_monthly')
->withPromotionCode('promo_code_id')
->create($paymentMethod);

指定されたプロモーションコードIDは、プロモーションコードに割り当てられたStripe API IDであり、顧客向けのプロモーションコードではありません。指定された顧客向けプロモーションコードに基づいてプロモーションコードIDを見つける必要がある場合は、`findPromotionCode`メソッドを使用できます。

// Find a promotion code ID by its customer facing code...
$promotionCode = $user->findPromotionCode('SUMMERSALE');
 
// Find an active promotion code ID by its customer facing code...
$promotionCode = $user->findActivePromotionCode('SUMMERSALE');

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

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

クーポンインスタンスを使用すると、割引額と、クーポンが固定割引またはパーセントベースの割引を表すかどうかを判断できます。

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

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

$discount = $billable->discount();
 
$discount = $subscription->discount();

返されるLaravel\Cashier\Discountインスタンスは、基になるStripe\Discountオブジェクトインスタンスをデコレートします。couponメソッドを呼び出すことで、この割引に関連付けられたクーポンを取得できます。

$coupon = $subscription->discount()->coupon();

顧客またはサブスクリプションに新しいクーポンまたはプロモーションコードを適用する場合は、applyCouponまたはapplyPromotionCodeメソッドを使用して行うことができます。

$billable->applyCoupon('coupon_id');
$billable->applyPromotionCode('promotion_code_id');
 
$subscription->applyCoupon('coupon_id');
$subscription->applyPromotionCode('promotion_code_id');

プロモーションコードに割り当てられたStripe API IDを使用する必要があり、顧客向けのプロモーションコードではないことに注意してください。顧客またはサブスクリプションには、一度に1つのクーポンまたはプロモーションコードのみを適用できます。

この件に関する詳細については、クーポンおよびプロモーションコードに関するStripeドキュメントを参照してください。

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

既にデフォルトの支払い方法が設定されている顧客にサブスクリプションを追加する場合は、サブスクリプションビルダーのaddメソッドを呼び出すことができます。

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

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

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

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

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

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

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

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

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

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureUserIsSubscribed
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->user() && ! $request->user()->subscribed('default')) {
// This user is not a paying customer...
return redirect('/billing');
}
 
return $next($request);
}
}

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

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

subscribedToProductメソッドは、指定されたStripe製品の識別子に基づいて、ユーザーが特定の製品を購読しているかどうかを判断するために使用できます。Stripeでは、製品は価格の集合体です。この例では、ユーザーのdefaultサブスクリプションがアプリケーションの「premium」製品をアクティブに購読しているかどうかを判断します。指定されたStripe製品識別子は、Stripeダッシュボードの製品識別子のいずれかに対応している必要があります。

if ($user->subscribedToProduct('prod_premium', 'default')) {
// ...
}

subscribedToProductメソッドに配列を渡すことにより、ユーザーのdefaultサブスクリプションがアプリケーションの「basic」または「premium」製品をアクティブに購読しているかどうかを判断できます。

if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {
// ...
}

subscribedToPriceメソッドは、顧客のサブスクリプションが特定の価格IDに対応しているかどうかを判断するために使用できます。

if ($user->subscribedToPrice('price_basic_monthly', 'default')) {
// ...
}

recurringメソッドは、ユーザーが現在購読していて、試用期間が終了しているかどうかを判断するために使用できます。

if ($user->subscription('default')->recurring()) {
// ...
}
exclamation

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

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

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

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

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

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

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

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

未完了および延滞ステータス

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

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

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

サブスクリプションの支払いが未完了の場合、latestPayment識別子を渡して、ユーザーをCashierの支払い確認ページにリダイレクトする必要があります。 サブスクリプションインスタンスで使用可能なlatestPaymentメソッドを使用して、この識別子を取得できます。

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

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

use Laravel\Cashier\Cashier;
 
/**
* Register any application services.
*/
public function register(): void
{
Cashier::keepPastDueSubscriptionsActive();
Cashier::keepIncompleteSubscriptionsActive();
}
exclamation

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

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

ほとんどのサブスクリプション状態はクエリスコープとしても使用できるため、特定の状態にあるサブスクリプションをデータベースで簡単にクエリできます。

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

使用可能なスコープの完全なリストを以下に示します。

Subscription::query()->active();
Subscription::query()->canceled();
Subscription::query()->ended();
Subscription::query()->incomplete();
Subscription::query()->notCanceled();
Subscription::query()->notOnGracePeriod();
Subscription::query()->notOnTrial();
Subscription::query()->onGracePeriod();
Subscription::query()->onTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();

価格の変更

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

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

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

価格を切り替え、顧客が現在試用中の試用期間をキャンセルする場合は、skipTrialメソッドを呼び出すことができます。

$user->subscription('default')
->skipTrial()
->swap('price_yearly');

価格を切り替え、次の請求サイクルを待たずに顧客にすぐに請求する場合は、swapAndInvoiceメソッドを使用できます。

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

日割り計算

デフォルトでは、Stripeは価格を切り替えるときに料金を日割り計算します。 noProrateメソッドを使用して、料金を日割り計算せずにサブスクリプションの価格を更新できます。

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

サブスクリプションの日割り計算の詳細については、Stripeドキュメントを参照してください。

exclamation

swapAndInvoiceメソッドの前にnoProrateメソッドを実行しても、日割り計算には影響しません。 請求書は常に発行されます。

サブスクリプション数量

サブスクリプションは「数量」の影響を受ける場合があります。 たとえば、プロジェクト管理アプリケーションは、プロジェクトごとに月額10ドルを請求する場合があります。 incrementQuantityメソッドとdecrementQuantityメソッドを使用して、サブスクリプション数量を簡単に増減できます。

use App\Models\User;
 
$user = User::find(1);
 
$user->subscription('default')->incrementQuantity();
 
// Add five to the subscription's current quantity...
$user->subscription('default')->incrementQuantity(5);
 
$user->subscription('default')->decrementQuantity();
 
// Subtract five from the subscription's current quantity...
$user->subscription('default')->decrementQuantity(5);

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

$user->subscription('default')->updateQuantity(10);

noProrateメソッドを使用して、料金を日割り計算せずにサブスクリプションの数量を更新できます。

$user->subscription('default')->noProrate()->updateQuantity(10);

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

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

サブスクリプションが複数製品のサブスクリプションである場合、数量を増減する価格のIDを、増減メソッドの2番目の引数として渡す必要があります。

$user->subscription('default')->incrementQuantity(1, 'price_chat');

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

複数製品のサブスクリプションを使用すると、単一のサブスクリプションに複数の請求製品を割り当てることができます。 たとえば、基本サブスクリプション価格が月額10ドルで、ライブチャットアドオン製品を月額15ドルで提供するカスタマーサービス「ヘルプデスク」アプリケーションを構築しているとします。 複数製品のサブスクリプションの情報は、Cashierのsubscription_itemsデータベーステーブルに格納されます。

newSubscriptionメソッドの2番目の引数として価格の配列を渡すことにより、特定のサブスクリプションに複数の製品を指定できます。

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

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

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

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

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

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

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

特定の数量で価格を追加する場合は、数量をaddPriceまたはaddPriceAndInvoiceメソッドの2番目の引数として渡すことができます。

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

removePriceメソッドを使用して、サブスクリプションから価格を削除できます。

$user->subscription('default')->removePrice('price_chat');
exclamation

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

価格の切り替え

複数製品のサブスクリプションに添付されている価格を変更することもできます。 たとえば、顧客がprice_chatアドオン製品を含むprice_basicサブスクリプションを持っており、顧客をprice_basicからprice_pro価格にアップグレードする場合を考えてみます。

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

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

swap メソッドにキーと値のペアの配列を渡すことで、サブスクリプションアイテムのオプションを指定することもできます。たとえば、サブスクリプション価格の数量を指定する必要がある場合があります。

$user = User::find(1);
 
$user->subscription('default')->swap([
'price_pro' => ['quantity' => 5],
'price_chat'
]);

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

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

日割り計算

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

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

数量

個々のサブスクリプション価格の数量を更新する場合は、メソッドに追加の引数として価格のIDを渡すことで、既存の数量メソッドを使用できます。

$user = User::find(1);
 
$user->subscription('default')->incrementQuantity(5, 'price_chat');
 
$user->subscription('default')->decrementQuantity(3, 'price_chat');
 
$user->subscription('default')->updateQuantity(10, 'price_chat');
exclamation

サブスクリプションに複数の価格がある場合、Subscription モデルの stripe_price 属性と quantity 属性は null になります。個々の価格属性にアクセスするには、Subscription モデルで使用可能な items リレーションシップを使用する必要があります。

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

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

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

findItemOrFail メソッドを使用して、特定の価格を取得することもできます。

$user = User::find(1);
 
$subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat');

複数サブスクリプション

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

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

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

この例では、顧客の月額水泳サブスクリプションを開始しました。ただし、後で年間サブスクリプションに切り替えることもできます。顧客のサブスクリプションを調整する場合、swimming サブスクリプションの価格を交換するだけです。

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

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

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

使用量に基づく課金

従量課金制 を使用すると、請求サイクル中の製品の使用量に基づいて顧客に課金できます。たとえば、顧客が月に送信するテキストメッセージまたはメールの数に基づいて課金できます。

従量課金制の使用を開始するには、まず Stripe ダッシュボードで 従量課金制モデルメーター を使用して新しい製品を作成する必要があります。メーターを作成した後、関連付けられたイベント名とメーターIDを保存します。これは、使用量を報告および取得するために必要になります。次に、meteredPrice メソッドを使用して、従量制価格IDを顧客のサブスクリプションに追加します。

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

Stripe Checkout を介して従量制サブスクリプションを開始することもできます。

$checkout = Auth::user()
->newSubscription('default', [])
->meteredPrice('price_metered')
->checkout();
 
return view('your-checkout-view', [
'checkout' => $checkout,
]);

使用量の報告

顧客がアプリケーションを使用すると、正確に請求できるように、Stripe に使用量が報告されます。メーターイベントの使用量を報告するには、Billable モデルの reportMeterEvent メソッドを使用できます。

$user = User::find(1);
 
$user->reportMeterEvent('emails-sent');

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

$user = User::find(1);
 
$user->reportMeterEvent('emails-sent', quantity: 15);

メーターの顧客のイベント概要を取得するには、Billable インスタンスの meterEventSummaries メソッドを使用できます。

$user = User::find(1);
 
$meterUsage = $user->meterEventSummaries($meterId);
 
$meterUsage->first()->aggregated_value // 10

メーターイベント概要の詳細については、Stripe の メーターイベント概要オブジェクトのドキュメント を参照してください。

すべてのメーターを一覧表示するには、Billable インスタンスの meters メソッドを使用できます。

$user = User::find(1);
 
$user->meters();

サブスクリプション税

exclamation

税率を手動で計算する代わりに、Stripe Tax を使用して税金を自動的に計算することができます。

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

/**
* The tax rates that should apply to the customer's subscriptions.
*
* @return array<int, string>
*/
public function taxRates(): array
{
return ['txr_id'];
}

taxRates メソッドを使用すると、顧客ごとに税率を適用できます。これは、複数の国と税率にまたがるユーザーベースに役立つ場合があります。

複数の製品を含むサブスクリプションを提供している場合は、請求可能モデルに priceTaxRates メソッドを実装することで、価格ごとに異なる税率を定義できます。

/**
* The tax rates that should apply to the customer's subscriptions.
*
* @return array<string, array<int, string>>
*/
public function priceTaxRates(): array
{
return [
'price_monthly' => ['txr_id'],
];
}
exclamation

taxRates メソッドは、サブスクリプション料金にのみ適用されます。Cashier を使用して「1回限り」の料金を請求する場合、その時点で税率を手動で指定する必要があります。

税率の同期

taxRates メソッドによって返されるハードコードされた税率 ID を変更する場合、ユーザーの既存のサブスクリプションの税設定は同じままになります。新しい taxRates 値で既存のサブスクリプションの税値を更新する場合は、ユーザーのサブスクリプションインスタンスで syncTaxRates メソッドを呼び出す必要があります。

$user->subscription('default')->syncTaxRates();

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

免税

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

use App\Models\User;
 
$user = User::find(1);
 
$user->isTaxExempt();
$user->isNotTaxExempt();
$user->reverseChargeApplies();
exclamation

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

サブスクリプションのアンカー日付

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

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

サブスクリプション請求サイクルの管理の詳細については、Stripe 請求サイクルのドキュメント を参照してください。

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

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

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

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

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

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

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

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

$user->subscription('default')->cancelNow();

サブスクリプションをすぐにキャンセルし、請求されていない残りの従量制使用量または新規/保留中の日割り計算請求項目を請求する場合は、ユーザーのサブスクリプションで `cancelNowAndInvoice` メソッドを呼び出します。

$user->subscription('default')->cancelNowAndInvoice();

特定の時点でサブスクリプションをキャンセルすることもできます。

$user->subscription('default')->cancelAt(
now()->addDays(10)
);

最後に、関連付けられたユーザーモデルを削除する前に、必ずユーザーのサブスクリプションをキャンセルしてください。

$user->subscription('default')->cancelNow();
 
$user->delete();

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

顧客がサブスクリプションをキャンセルし、再開する場合、サブスクリプションで resume メソッドを呼び出すことができます。サブスクリプションを再開するには、顧客はまだ「猶予期間」内でなければなりません。

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

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

サブスクリプションの試用

前払いでの支払方法

前もって支払い方法の情報を収集しながら、顧客に試用期間を提供する場合は、サブスクリプションを作成するときに trialDays メソッドを使用する必要があります。

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

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

exclamation

試用期間の終了日前に顧客のサブスクリプションがキャンセルされない場合、試用期間が終了するとすぐに課金されるため、ユーザーに試用期間の終了日を通知する必要があります。

trialUntil メソッドを使用すると、試用期間の終了時期を指定する DateTime インスタンスを提供できます。

use Carbon\Carbon;
 
$user->newSubscription('default', 'price_monthly')
->trialUntil(Carbon::now()->addDays(10))
->create($paymentMethod);

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

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

endTrial メソッドを使用して、サブスクリプションの試用をすぐに終了できます。

$user->subscription('default')->endTrial();

既存の試用期間が期限切れかどうかを判断するには、hasExpiredTrial メソッドを使用できます。

if ($user->hasExpiredTrial('default')) {
// ...
}
 
if ($user->subscription('default')->hasExpiredTrial()) {
// ...
}

Stripe / Cashier での試用日数の定義

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

前払いなしの支払方法

ユーザーの支払い方法の情報を前もって収集せずに試用期間を提供する場合は、ユーザーレコードの trial_ends_at 列を目的の試用終了日に設定できます。これは通常、ユーザー登録時に行われます。

use App\Models\User;
 
$user = User::create([
// ...
'trial_ends_at' => now()->addDays(10),
]);
exclamation

課金対象モデルのクラス定義内で、trial_ends_at 属性に 日付キャスト を追加してください。

Cashier はこのタイプのトライアルを「ジェネリックトライアル」と呼びます。既存のサブスクリプションに紐付けられていないためです。課金対象モデルインスタンスの onTrial メソッドは、現在の日付が trial_ends_at の値を超えていない場合に true を返します。

if ($user->onTrial()) {
// User is within their trial period...
}

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

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

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

if ($user->onTrial()) {
$trialEndsAt = $user->trialEndsAt('main');
}

ユーザーが「ジェネリック」トライアル期間内で、まだ実際のサブスクリプションを作成していないことを明確に知りたい場合は、onGenericTrial メソッドを使用することもできます。

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

試用の延長

extendTrial メソッドを使用すると、サブスクリプション作成後にトライアル期間を延長できます。トライアルが既に期限切れで、顧客が既にサブスクリプションの請求を受けている場合でも、トライアルの延長を提供できます。トライアル期間中に費やされた時間は、顧客の次回の請求から差し引かれます。

use App\Models\User;
 
$subscription = User::find(1)->subscription('default');
 
// End the trial 7 days from now...
$subscription->extendTrial(
now()->addDays(7)
);
 
// Add an additional 5 days to the trial...
$subscription->extendTrial(
$subscription->trial_ends_at->addDays(5)
);

Stripe Webhookの処理

lightbulb

ローカル開発中に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に作成します。

php artisan cashier:webhook

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

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

作成されるWebhookは、Cashierのバージョンと互換性のあるStripe APIバージョンを使用します。別のStripeバージョンを使用する場合は、--api-version オプションを指定できます。

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

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

php artisan cashier:webhook --disabled
exclamation

受信Stripe Webhookリクエストは、Cashierに含まれる Webhook署名検証 ミドルウェアで保護してください。

WebhookとCSRF保護

Stripe WebhookはLaravelの CSRF保護 をバイパスする必要があるため、Laravelが受信Stripe WebhookのCSRFトークンの検証を試みないようにする必要があります。これを実現するには、アプリケーションの bootstrap/app.php ファイルで stripe/* をCSRF保護から除外する必要があります。

->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'stripe/*',
]);
})

Webhookイベントハンドラの定義

Cashierは、請求の失敗やその他の一般的なStripe Webhookイベントのサブスクリプションキャンセルを自動的に処理します。ただし、処理したい追加のWebhookイベントがある場合は、Cashierによってディスパッチされる次のイベントをリッスンすることで処理できます。

  • Laravel\Cashier\Events\WebhookReceived
  • Laravel\Cashier\Events\WebhookHandled

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

<?php
 
namespace App\Listeners;
 
use Laravel\Cashier\Events\WebhookReceived;
 
class StripeEventListener
{
/**
* Handle received Stripe webhooks.
*/
public function handle(WebhookReceived $event): void
{
if ($event->payload['type'] === 'invoice.payment_succeeded') {
// Handle the incoming event...
}
}
}

Webhook署名の検証

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

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

単発請求

シンプルチャージ

顧客に対して1回限りの請求を行う場合は、課金対象モデルインスタンスの charge メソッドを使用できます。charge メソッドの2番目の引数として 支払い方法識別子 を提供する必要があります。

use Illuminate\Http\Request;
 
Route::post('/purchase', function (Request $request) {
$stripeCharge = $request->user()->charge(
100, $request->paymentMethodId
);
 
// ...
});

charge メソッドは、3番目の引数として配列を受け取り、基になるStripe請求の作成に任意のオプションを渡すことができます。請求の作成時に使用できるオプションの詳細については、Stripeドキュメント を参照してください。

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

基になる顧客またはユーザーなしで charge メソッドを使用することもできます。これを実現するには、アプリケーションの課金対象モデルの新しいインスタンスで charge メソッドを呼び出します。

use App\Models\User;
 
$stripeCharge = (new User)->charge(100, $paymentMethod);

請求が失敗した場合、charge メソッドは例外をスローします。請求が成功した場合、メソッドから Laravel\Cashier\Payment のインスタンスが返されます。

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

charge メソッドは、アプリケーションで使用される通貨の最小単位で支払い金額を受け入れます。たとえば、顧客が米ドルで支払う場合、金額はセントで指定する必要があります。

請求書による請求

1回限りの請求を行い、顧客にPDF請求書を提供する必要がある場合があります。invoicePrice メソッドを使用すると、まさにそれができます。たとえば、顧客に新しいシャツ5枚の請求書を発行してみましょう。

$user->invoicePrice('price_tshirt', 5);

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

$user->invoicePrice('price_tshirt', 5, [
'discounts' => [
['coupon' => 'SUMMER21SALE']
],
], [
'default_tax_rates' => ['txr_id'],
]);

invoicePrice と同様に、tabPrice メソッドを使用して、複数のアイテム(請求書あたり最大250アイテム)を顧客の「タブ」に追加してから顧客に請求することで、1回限りの請求を作成できます。たとえば、顧客にシャツ5枚とマグカップ2個の請求書を発行できます。

$user->tabPrice('price_tshirt', 5);
$user->tabPrice('price_mug', 2);
$user->invoice();

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

$user->invoiceFor('One Time Fee', 500);

invoiceFor メソッドは使用できますが、事前定義された価格で invoicePrice および tabPrice メソッドを使用することをお勧めします。そうすることで、Stripeダッシュボード内で製品ごとの売上に関するより優れた分析とデータにアクセスできます。

exclamation

invoiceinvoicePrice、および invoiceFor メソッドは、請求の失敗を再試行するStripe請求書を作成します。請求書が失敗した請求を再試行しない場合は、最初の請求の失敗後にStripe APIを使用して請求書を閉じる必要があります。

支払インテントの作成

課金対象モデルインスタンスで pay メソッドを呼び出すことで、新しいStripe Payment Intentを作成できます。このメソッドを呼び出すと、Laravel\Cashier\Payment インスタンスにラップされたPayment Intentが作成されます。

use Illuminate\Http\Request;
 
Route::post('/pay', function (Request $request) {
$payment = $request->user()->pay(
$request->get('amount')
);
 
return $payment->client_secret;
});

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

pay メソッドを使用する場合、Stripeダッシュボード内で有効になっているデフォルトの支払い方法が顧客に利用可能です。または、特定の支払い方法のみを使用できるようにする場合は、payWith メソッドを使用できます。

use Illuminate\Http\Request;
 
Route::post('/pay', function (Request $request) {
$payment = $request->user()->payWith(
$request->get('amount'), ['card', 'bancontact']
);
 
return $payment->client_secret;
});
exclamation

pay および payWith メソッドは、アプリケーションで使用される通貨の最小単位で支払い金額を受け入れます。たとえば、顧客が米ドルで支払う場合、金額はセントで指定する必要があります。

請求の払い戻し

Stripe請求を払い戻す必要がある場合は、refund メソッドを使用できます。このメソッドは、Stripe Payment Intent ID を最初の引数として受け取ります。

$payment = $user->charge(100, $paymentMethodId);
 
$user->refund($payment->id);

請求書

請求書の取得

invoices メソッドを使用して、課金対象モデルの請求書の配列を簡単に取得できます。invoices メソッドは、Laravel\Cashier\Invoice インスタンスのコレクションを返します。

$invoices = $user->invoices();

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

$invoices = $user->invoicesIncludingPending();

findInvoice メソッドを使用して、IDで特定の請求書を取得できます。

$invoice = $user->findInvoice($invoiceId);

請求書情報の表示

顧客の請求書を一覧表示する場合、請求書のメソッドを使用して関連する請求書情報を表示できます。たとえば、すべての請求書を表に一覧表示し、ユーザーがそれらを簡単にダウンロードできるようにすることができます。

<table>
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->date()->toFormattedDateString() }}</td>
<td>{{ $invoice->total() }}</td>
<td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
</tr>
@endforeach
</table>

今後の請求書

顧客の次回の請求書を取得するには、upcomingInvoice メソッドを使用できます。

$invoice = $user->upcomingInvoice();

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

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

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

previewInvoice メソッドを使用すると、価格を変更する前に請求書をプレビューできます。これにより、特定の価格変更が行われたときに顧客の請求書がどのように表示されるかを確認できます。

$invoice = $user->subscription('default')->previewInvoice('price_yearly');

複数の新しい価格で請求書をプレビューするために、価格の配列を previewInvoice メソッドに渡すことができます。

$invoice = $user->subscription('default')->previewInvoice(['price_yearly', 'price_metered']);

請求書PDFの生成

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

composer require dompdf/dompdf

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

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

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

return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
'street' => 'Main Str. 1',
'location' => '2000 Antwerp, Belgium',
'phone' => '+32 499 00 00 00',
'email' => '[email protected]',
'url' => 'https://example.com',
'vendorVat' => 'BE123456789',
]);

downloadInvoice メソッドでは、3 番目の引数でカスタムファイル名を指定することもできます。このファイル名には自動的に .pdf が付加されます。

return $request->user()->downloadInvoice($invoiceId, [], 'my-invoice');

カスタムインボイスレンダラー

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

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

インボイスレンダラーコントラクトを実装したら、アプリケーションの 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 を渡す必要があります。

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

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

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

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

use Illuminate\Http\Request;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout(['price_tshirt' => 1], [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

success_url チェックアウトオプションを定義する際に、URL を呼び出すときにクエリ文字列パラメータとしてチェックアウトセッション ID を追加するように Stripe に指示できます。そのためには、リテラル文字列 {CHECKOUT_SESSION_ID}success_url クエリ文字列に追加します。Stripe は、このプレースホルダーを実際のチェックアウトセッション ID に置き換えます。

use Illuminate\Http\Request;
use Stripe\Checkout\Session;
use Stripe\Customer;
 
Route::get('/product-checkout', function (Request $request) {
return $request->user()->checkout(['price_tshirt' => 1], [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
]);
});
 
Route::get('/checkout-success', function (Request $request) {
$checkoutSession = $request->user()->stripe()->checkout->sessions->retrieve($request->get('session_id'));
 
return view('checkout.success', ['checkoutSession' => $checkoutSession]);
})->name('checkout-success');

プロモーションコード

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

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

単発請求チェックアウト

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

use Illuminate\Http\Request;
 
Route::get('/charge-checkout', function (Request $request) {
return $request->user()->checkoutCharge(1200, 'T-Shirt', 5);
});
exclamation

checkoutCharge メソッドを使用すると、Stripe は常に Stripe ダッシュボードに新しい製品と価格を作成します。そのため、Stripe ダッシュボードで事前に製品を作成し、代わりに checkout メソッドを使用することをお勧めします。

サブスクリプションチェックアウト

exclamation

サブスクリプションに Stripe Checkout を使用するには、Stripe ダッシュボードで customer.subscription.created webhook を有効にする必要があります。この webhook は、データベースにサブスクリプションレコードを作成し、関連するすべてのサブスクリプションアイテムを保存します。

Stripe Checkout を使用してサブスクリプションを開始することもできます。Cashier のサブスクリプションビルダーメソッドでサブスクリプションを定義した後、checkout メソッドを呼び出すことができます。顧客がこのルートにアクセスすると、Stripe の Checkout ページにリダイレクトされます。

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->checkout();
});

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

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

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

use Illuminate\Http\Request;
 
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_monthly')
->allowPromotionCodes()
->checkout();
});
exclamation

残念ながら、Stripe Checkout は、サブスクリプションの開始時にすべてのサブスクリプション課金オプションをサポートしているわけではありません。サブスクリプションビルダーで anchorBillingCycleOn メソッドを使用したり、比例配分動作を設定したり、支払い動作を設定したりしても、Stripe Checkout セッション中には効果がありません。Stripe Checkout Session API ドキュメント を参照して、使用可能なパラメータを確認してください。

Stripe Checkout と試用期間

もちろん、Stripe Checkout を使用して完了するサブスクリプションを構築する際に、試用期間を定義できます。

$checkout = Auth::user()->newSubscription('default', 'price_monthly')
->trialDays(3)
->checkout();

ただし、試用期間は少なくとも 48 時間にする必要があります。これは、Stripe Checkout でサポートされる試用期間の最小値です。

サブスクリプションと Webhook

Stripe と Cashier は webhook 経由でサブスクリプションステータスを更新するため、顧客が支払い情報を入力した後にアプリケーションに戻ったときに、サブスクリプションがまだアクティブになっていない可能性があります。このシナリオを処理するために、ユーザーに支払いまたはサブスクリプションが保留中であることを知らせるメッセージを表示することをお勧めします。

税 ID の収集

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

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

このメソッドが呼び出されると、顧客が会社として購入しているかどうかを示すことができる新しいチェックボックスが顧客に表示されます。その場合、税 ID 番号を提供する機会が与えられます。

exclamation

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

ゲストチェックアウト

Checkout::guest メソッドを使用して、「アカウント」を持っていないアプリケーションのゲストのチェックアウトセッションを開始できます。

use Illuminate\Http\Request;
use Laravel\Cashier\Checkout;
 
Route::get('/product-checkout', function (Request $request) {
return Checkout::guest()->create('price_tshirt', [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

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

use Illuminate\Http\Request;
use Laravel\Cashier\Checkout;
 
Route::get('/product-checkout', function (Request $request) {
return Checkout::guest()
->withPromotionCode('promo-code')
->create('price_tshirt', [
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});

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

支払い失敗の処理

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

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

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

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

  • クレジットカード
  • Alipay
  • Bancontact
  • BECS Direct Debit
  • EPS
  • Giropay
  • iDEAL
  • SEPA Direct Debit

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

支払い例外は、Billable トレイトを使用するモデルの chargeinvoiceFor、および invoice メソッドに対してスローされる場合があります。サブスクリプションを操作する場合、SubscriptionBuildercreate メソッド、および SubscriptionSubscriptionItem モデルの incrementAndInvoiceswapAndInvoice メソッドは、不完全な支払い例外をスローする可能性があります。

既存のサブスクリプションに不完全な支払いがあるかどうかを判断するには、課金対象モデルまたはサブスクリプションインスタンスで hasIncompletePayment メソッドを使用します。

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

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

use Laravel\Cashier\Exceptions\IncompletePayment;
 
try {
$user->charge(1000, 'pm_card_threeDSecure2Required');
} catch (IncompletePayment $exception) {
// Get the payment intent status...
$exception->payment->status;
 
// Check specific conditions...
if ($exception->payment->requiresPaymentMethod()) {
// ...
} elseif ($exception->payment->requiresConfirmation()) {
// ...
}
}

支払いの確認

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

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

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

強力な顧客認証

あなたのビジネスまたは顧客のいずれかがヨーロッパに拠点を置いている場合、EU の強力な顧客認証(SCA)規制を遵守する必要があります。これらの規制は、支払詐欺を防ぐために、2019 年 9 月に欧州連合によって課されました。幸いなことに、Stripe と Cashier は SCA 準拠アプリケーションの構築に対応しています。

exclamation

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

追加の確認が必要な支払い

SCA 規制では、支払いを確認して処理するために、追加の検証が必要になることがよくあります。これが発生すると、Cashier は Laravel\Cashier\Exceptions\IncompletePayment 例外をスローして、追加の検証が必要であることを通知します。これらの例外の処理方法の詳細については、失敗した支払いの処理 に関するドキュメントを参照してください。

Stripe または Cashier によって表示される支払い確認画面は、特定の銀行またはカード発行会社の支払いフローに合わせて調整でき、追加のカード確認、一時的な少額料金、個別のデバイス認証、またはその他の形式の検証が含まれる場合があります。

不完全および期限切れの状態

支払いに追加の確認が必要な場合、サブスクリプションは、stripe_status データベース列で示されるように、incomplete または past_due の状態のままになります。支払い確認が完了し、Stripe から webhook を介してアプリケーションに完了が通知されるとすぐに、Cashier は顧客のサブスクリプションを自動的にアクティブにします。

incomplete および past_due 状態の詳細については、これらの状態に関する追加ドキュメント を参照してください。

オフセッション支払い通知

SCA規制により、顧客はサブスクリプションがアクティブな場合でも、定期的に支払い詳細を確認する必要があります。Cashierは、オフセッションの支払い確認が必要な場合に顧客に通知を送信できます。たとえば、サブスクリプションの更新時に発生する可能性があります。 Cashierの支払い通知は、CASHIER_PAYMENT_NOTIFICATION 環境変数を通知クラスに設定することで有効にできます。デフォルトでは、この通知は無効になっています。もちろん、Cashierにはこの目的で使用できる通知クラスが含まれていますが、必要に応じて独自の通知クラスを提供することもできます。

CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment

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

exclamation

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

Stripe SDK

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

$stripeSubscription = $subscription->asStripeSubscription();
 
$stripeSubscription->application_fee_percent = 5;
 
$stripeSubscription->save();

updateStripeSubscriptionメソッドを使用して、Stripeサブスクリプションを直接更新することもできます。

$subscription->updateStripeSubscription(['application_fee_percent' => 5]);

Stripe\StripeClientクライアントを直接使用したい場合は、Cashierクラスでstripeメソッドを呼び出すことができます。たとえば、このメソッドを使用してStripeClientインスタンスにアクセスし、Stripeアカウントから価格のリストを取得できます。

use Laravel\Cashier\Cashier;
 
$prices = Cashier::stripe()->prices->all();

テスト

Cashierを使用するアプリケーションをテストする場合、Stripe APIへの実際のHTTPリクエストをモックできます。ただし、これには、Cashier自身の動作を部分的に再実装する必要があります。そのため、テストで実際のStripe APIにアクセスすることをお勧めします。これは時間がかかりますが、アプリケーションが期待どおりに動作しているという確信が高まり、時間のかかるテストは独自のPest / PHPUnitテストグループに配置できます。

テストする際は、Cashier自体に優れたテストスイートが既に存在するため、独自のアプリケーションのサブスクリプションと支払いフローのテストのみに焦点を当て、すべての基礎となるCashierの動作をテストする必要はないことに注意してください。

開始するには、Stripeシークレットの**テスト**バージョンをphpunit.xmlファイルに追加します。

<env name="STRIPE_SECRET" value="sk_test_<your-key>"/>

これで、テスト中にCashierと対話するたびに、実際のAPIリクエストがStripeテスト環境に送信されます。便宜上、テスト中に使用できるサブスクリプション/価格でStripeテストアカウントを事前に設定する必要があります。

lightbulb

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