【Stripe】Laravelでサブスク決済 【定期課金】

LaravelでStripeと連携してサブスク決済を行う

パッケージ入れる

composer require laravel/cashier
php artisan migrate

上記マイグレーションにより、
usersテーブルに課金関連のカラムを追加 & subscriptionsテーブルなどが作成される

Userテーブルを課金対象にする

  • app/Models/User.php
    Billableトレイト (=サブスクにまつわる様々なメソッドが使える)をuseする
use Laravel\Cashier\Billable; // 追加

class User extends Authenticatable
{
    use Billable; // 追加
}

Stripeキーの設定

  • .env
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
CASHIER_CURRENCY=jpy
CASHIER_CURRENCY_LOCALE=ja_JP
CASHIER_LOGGER=daily

フォームの実装

  • routes/web.php
use App\Http\Controllers\RegisterController;

Route::get('/register', [RegisterController::class, 'form'])->name('register');
Route::post('/register', [RegisterController::class, 'register']);

  • resources/views/register.blade.php
@extends('layouts.app')

@section('title', '新規登録')

@section('content')
    <form method="POST" action="{{ route('register') }}" id="registerForm">
        @csrf
        <input type="hidden" name="paymentMethodId" value="{{ old('paymentMethodId') }}"/>

        <div>
            <label for="name">お名前</label>
            <input id="name" type="text" name="name" value="{{ old('name') }}" autofocus required>

            @error('name')
            <span>
                <strong>{{ $message }}</strong>
            </span>
            @enderror
        </div>

        <div>
            <label for="email">メールアドレス</label>
            <input id="email" type="text" name="email" value="{{ old('email') }}" required>

            @error('email')
            <span>
                <strong>{{ $message }}</strong>
            </span>
            @enderror
        </div>

        <div>
            <label for="password">パスワード</label>
            <input id="password" type="password" name="password" required>

            @error('password')
            <span>
                <strong>{{ $message }}</strong>
            </span>
            @enderror
        </div>

        <div>
            <label>クレジットカード情報</label>
            <input id="card-holder-name" type="text" placeholder="カード名義人">
            <!-- Stripe要素のプレースホルダ -->
            <div id="card-element"></div>
            <span class="card-error-txt">
                <strong></strong>
            </span>
        </div>

        <button type="button" id="register-btn">
            会員登録をする
        </button>
    </form>

    <!-- Stripe JS読み込み -->
    <script src="https://js.stripe.com/v3/"></script>
    <script>
        const stripe = Stripe('Stripeのpublicキーを入れる');

        const elements = stripe.elements();
        const cardElement = elements.create('card', {
            // 郵便番号を非表示にする
            hidePostalCode: true,
        });

        cardElement.mount('#card-element');

        const cardHolderName = document.getElementById('card-holder-name');
        const cardButton = document.getElementById('register-btn');

        cardButton.addEventListener('click', async (e) => {
            const {paymentMethod, error} = await stripe.createPaymentMethod(
                'card', cardElement, {
                    billing_details: {name: cardHolderName.value}
                }
            );

            // カードの検証に失敗した場合
            if (error) {
                $('.card-error-txt strong').html(error.message)
            // カードの検証に成功した場合
            } else {
                // 支払いIDをセットする
                $('[name=paymentMethodId]').val(paymentMethod.id)
                // フォームを送信
                $('#registerForm').submit()
            }
        });
    </script>
@endsection

  • 上記画面を開くとこんな感じのフォームが表示される

  • デフォルトだと郵便番号が表示される

非表示にするためにJSで以下オプションを追加している

   const cardElement = elements.create('card', {
            hidePostalCode: true,
    });

RegisterController

  • app/Http/Controllers/RegisterController
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    /**
     * 登録フォーム
     *
     * @return \Illuminate\View\View
     */
    public function form()
    {
        return view('register');
    }

    /**
     * 登録処理
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
     */
    public function register(Request $request)
    {
       $data = $request->all();
       
       // ユーザー登録
       $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        // サブスク登録
        $user->newSubscription(
            'default', 'price_monthly'
        )->create($data['paymentMethodId']);

        // 任意の場所にリダイレクト
        return redirect(route('home'));
    }

}

ダッシュボード確認

ダッシュボードのイベントにて確認ができる

テーブル確認

  • usersテーブル
  • subscriptionsテーブル

サブスクの最新の状態を取得したい場合

以下のように取得、チェックが可能。
サブスクが失敗して無効になっている場合は、Stripeポータルに誘導することで簡単に更新してもらえる

$user->subscription()->syncStripeStatus();

if ($user->subscription()->stripe_status !== 'active') {
  $billingUrl = auth()->user()->billingPortalUrl();
  abort(403, "サブスクリプションが無効です。下記よりお支払い方法を設定してください。<br><a href='$billingUrl' target='_blank'>お支払い方法の更新</a>");
}

\ 案件のご依頼・ご相談はこちらから /