> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Cashier (Stripe)

> Laravel Cashier (Stripe) を使って Subscription 課金、1回払い、請求書、Webhook を実装する基本フローを解説します。

## 概要

Laravel Cashier (Stripe) は、Stripe の課金機能を Laravel から扱うための公式パッケージです。あなたは Subscription の作成・状態確認・キャンセル、1回払い、請求書のダウンロード、Webhook 処理を統一された API で実装できます。

## インストールと設定

まず Cashier をインストールし、必要なテーブルを作成してください。

```shell theme={null}
composer require laravel/cashier
php artisan vendor:publish --tag="cashier-migrations"
php artisan migrate
```

次に、`App\Models\User` などの課金対象モデルに `Billable` トレイトを追加します。

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}
```

`.env` に Stripe のキーを設定します。

```ini theme={null}
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
```

## 顧客管理

Stripe Customer をまだ作成していない可能性がある場合は、`createOrGetStripeCustomer()` を使います。

```php theme={null}
$stripeCustomer = $user->createOrGetStripeCustomer();
```

明示的に作成するなら `createAsStripeCustomer()` を使います。

```php theme={null}
$stripeCustomer = $user->createAsStripeCustomer();
```

## Subscription

### 新規作成

`newSubscription()` と `create()` で Subscription を開始します。
`$paymentMethodId` には Stripe.js などで取得した Payment Method ID を渡します。

```php theme={null}
$user->newSubscription('default', 'price_monthly')
    ->create($paymentMethodId);
```

`price_monthly` は例です。実装時は Stripe ダッシュボードで作成した実際の Price ID を指定してください。

### 状態確認

`subscribed()` で有効な Subscription かどうかを確認できます。

```php theme={null}
if ($user->subscribed('default')) {
    // Active subscription...
}
```

### キャンセルと再開

```php theme={null}
$user->subscription('default')->cancel();

if ($user->subscription('default')->onGracePeriod()) {
    // The user is on the grace period...
}

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

```mermaid theme={null}
stateDiagram-v2
    [*] --> Active
    Active --> GracePeriod: cancel()
    GracePeriod --> Active: resume()
    GracePeriod --> Canceled: grace period ends
    Active --> Canceled: cancelNow()
```

## 1回払い (Charge)

`charge()` で 1 回だけ課金できます。金額は通貨の最小単位で渡してください（例: USD なら `100` は `$1.00`）。
ここでも `$paymentMethodId` には Stripe で作成した Payment Method ID を使います。

```php theme={null}
$payment = $user->charge(100, $paymentMethodId);
```

課金に失敗した場合、`charge()` は例外をスローします。

## Payment Element

Stripe の [Payment Element](https://stripe.com/docs/payments/payment-element) を使うと、カード・Apple Pay・Google Pay・iDEAL など複数の決済手段を統一的に扱えます。

### Subscription での利用

Setup Intent を作成してビューに渡します。

```php theme={null}
return view('subscribe', [
    'intent' => $user->createSetupIntent()
]);
```

Blade ビューで Payment Element をマウントし、Setup Intent の `client_secret` を渡します。

```html theme={null}
<div id="payment-element"></div>
<button id="submit">Subscribe</button>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements({
        clientSecret: '{{ $intent->client_secret }}'
    });

    const paymentElement = elements.create('payment');

    paymentElement.mount('#payment-element');

    document.getElementById('submit').addEventListener('click', async () => {
        const { error } = await stripe.confirmSetup({
            elements,
            confirmParams: {
                return_url: '{{ route("subscription.complete") }}',
            },
        });

        if (error) {
            // エラーメッセージをユーザーに表示...
        }
    });
</script>
```

Stripe のリダイレクト先 URL には `setup_intent` パラメータが付与されます。これを使って Payment Method ID を取得し、Subscription を作成します。

```php theme={null}
use Illuminate\Http\Request;

Route::get('/subscription/complete', function (Request $request) {
    $setupIntent = $request->user()->findSetupIntent(
        $request->setup_intent
    );

    $paymentMethod = $setupIntent->payment_method;

    $request->user()
        ->newSubscription('default', 'price_xxx')
        ->create($paymentMethod);

    return redirect('/dashboard');
})->name('subscription.complete');
```

### 1回払いでの利用

1 回払いでは `pay()` で Payment Intent を作成します。Order モデルには `user_id`、`amount`、`status`、`stripe_payment_intent_id` カラムが必要です。

```php theme={null}
use App\Models\Order;
use Illuminate\Http\Request;

Route::post('/pay', function (Request $request) {
    $amount = 1000;

    $payment = $request->user()->pay($amount);

    $order = Order::create([
        'user_id' => $request->user()->id,
        'amount' => $amount,
        'status' => 'pending',
        'stripe_payment_intent_id' => $payment->id,
    ]);

    return view('checkout', [
        'clientSecret' => $payment->client_secret,
        'order' => $order,
    ]);
});
```

Payment Element をマウントして決済を確定します。

```html theme={null}
<div id="payment-element"></div>
<button id="submit">Pay Now</button>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements({
        clientSecret: '{{ $clientSecret }}'
    });

    const paymentElement = elements.create('payment');

    paymentElement.mount('#payment-element');

    document.getElementById('submit').addEventListener('click', async () => {
        const { error } = await stripe.confirmPayment({
            elements,
            confirmParams: {
                return_url: '{{ route("payment.complete") }}',
            },
        });

        if (error) {
            // エラーメッセージをユーザーに表示...
        }
    });
</script>
```

リダイレクト後は `payment_intent` パラメータで Order を検索し、Payment Intent が認証済みユーザーのものであり `succeeded` ステータスであることを確認してから注文を確定します。

```php theme={null}
use App\Models\Order;
use Illuminate\Http\Request;

Route::get('/payment/complete', function (Request $request) {
    $order = Order::where('user_id', $request->user()->id)
        ->where('stripe_payment_intent_id', $request->payment_intent)
        ->firstOrFail();

    $paymentIntent = $request->user()
        ->stripe()
        ->paymentIntents
        ->retrieve($request->payment_intent);

    if ($paymentIntent->customer === $request->user()->stripe_id &&
        $paymentIntent->status === 'succeeded') {
        $order->update(['status' => 'paid']);

        // 注文を確定する処理...
    }

    return redirect('/dashboard');
})->name('payment.complete');
```

```mermaid theme={null}
sequenceDiagram
    participant User as ユーザー
    participant App as Laravel<br>アプリ
    participant Stripe

    User->>App: POST /pay
    App->>Stripe: pay() で PaymentIntent 作成
    Stripe-->>App: client_secret
    App-->>User: checkout ビュー表示
    User->>Stripe: confirmPayment()
    Stripe-->>User: return_url へリダイレクト
    User->>App: GET /payment/complete
    App->>Stripe: PaymentIntent 取得・検証
    App->>App: order.status = paid
    App-->>User: ダッシュボードへリダイレクト
```

## 請求書

請求書一覧は `invoices()` で取得できます。

```php theme={null}
$invoices = $user->invoices();
```

PDF ダウンロードには `dompdf/dompdf` をインストールし、`downloadInvoice()` を使います。`$invoiceId` には `invoices()` で取得した請求書 ID を渡してください。

```shell theme={null}
composer require dompdf/dompdf
```

```php theme={null}
return $user->downloadInvoice($invoiceId);
```

## Webhook の設定

Cashier は Stripe Webhook 用のルートを自動登録し、デフォルトで `/stripe/webhook` を使います。Stripe ダッシュボードにこの URL を設定してください。

Webhook の作成は `cashier:webhook` で実行できます。

```shell theme={null}
php artisan cashier:webhook
```

CSRF 保護から `stripe/*` を除外してください。

```php theme={null}
->withMiddleware(function (Middleware $middleware): void {
    $middleware->preventRequestForgery(except: [
        'stripe/*',
    ]);
})
```

`STRIPE_WEBHOOK_SECRET` を `.env` に設定すると、Cashier の署名検証ミドルウェアで Webhook リクエストを検証できます。


## Related topics

- [Laravel 10 から 11 へのアップグレード](/jp/blog/upgrade-10-to-11.md)
- [2026年6月 Laravel アップデート](/jp/blog/changelog/202606.md)
- [サービスコンテナ](/jp/service-container.md)
- [laravel/agent-skills — Laravel公式AIエージェントスキル集](/jp/blog/agent-skills-introduction.md)
- [Laravel Bluesky](/jp/packages/laravel-bluesky/index.md)
