【Laravel5.8+Stripe⑤】Laravel CasherとStripeを導入して管理者権限を設定する その1

f:id:nekorokkekun:20190827102831p:plain:w1000
こちらの連載記事では、LaravelとStripeを使用して企業サイト兼Eコマース(ECサイト)を作成していきます。

Laravelのプロジェクト作成からStripeの実装まで行い、最終的に単発決済・サブスクリプションの実装までを目指します。

シリーズ

【Laravel5.8+Stripe⓪】ECサイト作成チュートリアル概要 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe①】ベースプロジェクトの作成 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe②】メールフォームの実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe③】ユーザー認証機能のカスタマイズ - Laravelとねころっけくん5.8
【Laravel5.8+Stripe④】ディスカウントページを作成する - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑤】Laravel CasherとStripeを導入して管理者権限を設定する その1 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑥】Laravel CasherとStripeを導入して管理者権限を設定する その2 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑦】サブスクリプション決済の作成 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑧】請求書ダウンロード機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑨】サブスクリプションプラン変更機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑩】サブスクリプション中止機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe11】Webhookの実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe12】クーポン機能を実装する - Laravelとねころっけくん5.8
【Laravel5.8+Stripe13】ショッピングカートの実装 - Laravelとねころっけくん5.8


ちなみにこちらの記事は、Easy E-Commerce Using Laravel and Stripeという書籍をもとに執筆しています。


今回は、Stripeのチェックアウト機能をサイトに統合して、顧客が製品の購入を開始できるようにします。

前提

Stripeアカウントを所有していること
アカウント作成は電話番号さえあればできるため作成しておきましょう。

Laravel Casherの準備

キャッシャーは、Laravelの作成者であるTaylor Otwellが作成および管理するパッケージで、LaravelアプリケーションへのStripeの統合を大幅に簡素化します。

開発者がサブスクリプションを管理できるようにするために作成された最新のアップデートでは、1回限りの請求(電子製品または物理的な製品を購入する理由で顧客のクレジットカードに1回請求)を実行できるようになりました。

これは、キャッシャーを使用して、簡単に製品を販売できることを意味します。

まずはLaravel Casherをインストールします。

$ composer require laravel/cashier

次にusersテーブルへ2つのカラムを追加します。

        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->string('stripe_subscription')->nullable(); // 追加
            $table->string('stripe_plan')->nullable(); // 追加
            $table->rememberToken();
            $table->timestamps();
        });

usersテーブルの用意

改めてmigrateを実行してみます。

$ php artisan migrate:fresh
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.02 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.02 seconds)
Migrating: 2019_05_03_000001_create_customer_columns
Migrated:  2019_05_03_000001_create_customer_columns (0.03 seconds)
Migrating: 2019_05_03_000002_create_subscriptions_table
Migrated:  2019_05_03_000002_create_subscriptions_table (0.02 seconds)
Migrating: 2019_08_15_221247_add_user_location
Migrated:  2019_08_15_221247_add_user_location (0.01 seconds)

そうすると、create_customer_columnsというusersテーブルにカラムを追加するための機能が自動的に備わっていることがわかります。


mysqlに入り、usersテーブルを見てみると…

mysql> desc users;
+---------------------+---------------------+------+-----+---------+----------------+
| Field               | Type                | Null | Key | Default | Extra          |
+---------------------+---------------------+------+-----+---------+----------------+
| id                  | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name                | varchar(255)        | NO   |     | NULL    |                |
| email               | varchar(255)        | NO   | UNI | NULL    |                |
| email_verified_at   | timestamp           | YES  |     | NULL    |                |
| password            | varchar(255)        | NO   |     | NULL    |                |
| stripe_subscription | varchar(255)        | NO   |     | NULL    |                |
| stripe_plan         | varchar(255)        | NO   |     | NULL    |                |
| remember_token      | varchar(100)        | YES  |     | NULL    |                |
| created_at          | timestamp           | YES  |     | NULL    |                |
| updated_at          | timestamp           | YES  |     | NULL    |                |
| stripe_id           | varchar(255)        | YES  | MUL | NULL    |                |
| card_brand          | varchar(255)        | YES  |     | NULL    |                |
| card_last_four      | varchar(4)          | YES  |     | NULL    |                |
| trial_ends_at       | timestamp           | YES  |     | NULL    |                |
| address             | varchar(255)        | NO   |     | NULL    |                |
| city                | varchar(255)        | NO   |     | NULL    |                |
| state               | varchar(255)        | NO   |     | NULL    |                |
| zip                 | varchar(255)        | NO   |     | NULL    |                |
+---------------------+---------------------+------+-----+---------+----------------+

このようになっています。
サブスクリプション用のカラムは自動で追加されないため、手動でstripe_subscriptionとstripe_planだけは追加しておく必要があります。

User.phpへの追記

次にUserモデルへの追記を行います。
以下の2点を追加してください。
/app/User.php

// 省略
use Illuminate\Auth\Passwords\CanResetPassword as CanResetPassword;
use Laravel\Cashier\Billable; 

class User extends Authenticatable
{
use Notifiable, CanResetPassword, Billable; //CanResetPassword, Billableを追加
// 省略
protected $dates = ['trial_ends_at', 'subscription_ends_at']; 

UserモデルにBillableを追加することにより、特定のユーザーと連携しながらLaravel Casherを通じてさまざまな決済方法を利用できます。

また、サブスクリプションの試用日とサブスクリプション終了日とのやり取りを少し簡単にする必要があります。

内部的には、これらの列を日付として処理する必要があることをLaravelに伝えます。2つ目の追記によって数字列が自動的にCarbonオブジェクトに変換されます。

CarbonはPHPのDateTimeクラスの拡張機能であり、日付の操作を非常に簡単にします。

この機能は、サブスクリプションが実装される場面で活用できます。

Stripe API keyの取得と設定

次にStripe API keyを取得し、プロジェクトのファイル内に設定していきましょう。

まずはStripeのダッシュボードにアクセシして、サイドメニューの開発者>APIキーを選択しましょう。

するとテスト用のAPIキーとシークレットAPIキーが取得できます。
本番まではこちらのテスト用APIキーを使用しましょう。
f:id:nekorokkekun:20190817103747p:plain


取得したAPIキーを、まず.envに設定します。

STRIPE_API_PUBLIC=pk_test_KEY_GOES_HERE
STRIPE_API_SECRET=sk_test_KEY_GOES_HERE

次に、/config/services.phpに追記します。

    'stripe' => [
        'model'  => 'User',
        'secret' => env('STRIPE_API_SECRET'),
    ],

今後もStripeのAPIキーを埋め込む場面がありますが、.envに登録しておくと何度も引用しなくて良いので便利ですね。

Productモデルの作成

次にECサイトで販売する商品を登録するためのProductモデルを作成していきましょう。

$ php artisan make:model Product -m

/database/migrations/2019_08_17_014731_create_products_table.php

        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('sku'); // SKUとは在庫管理を行うときの最小の単位です
            $table->string('name');
            $table->text('description');
            $table->decimal('price', 6, 2);
            $table->boolean('is_downloadble');
            $table->timestamps();
        });
$table->boolean('is_downloadble')->default(false);

という項目は、商品自体がダウンロード可能なもの(電子書籍など)か、そうでないか(服や食材など)という意味になります。

扱う商品がダウンロード可能なものメインであれば、defaultをtrueに設定してもいいでしょう。

Controllerの作成

次に商品を表示するためのControllerを作成していきましょう。

$ php artisan make:controller ProductController

/app/Http/Controllers/ProductController.php

    public function index(){
        $products = Product::orderBy('name', 'asc')->get();
        return view('product.index', compact('products'));
    }
    
    public function show($id){
        $product = Product::findOrFail($id);
        return view('product.show', compact('product'));
    }

/routes/web.php

Route::get('products', 'ProductController@index');
Route::get('products/{id}', 'ProductController@show');

usersテーブルへ管理者フラグの追加

商品の追加は管理者のみが行えるようにしたいため、usersテーブルへ管理者フラグ用のカラムを追加します。

$ php artisan make:migration add_is_admin_to_user_table --table=users

/database/migrations/2019_08_17_021139_add_is_admin_to_user_table.php

    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->boolean('is_admin')->default(false);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('is_admin');
        });
    }

こちらも、先ほどのis_downloadable同様で管理者アカウントのみ管理者権限フラグを立てるイメージです。

特定のユーザーを管理者として識別するには、データベースにログインしてレコードを更新し、is_adminをtrueに設定する必要があります。

ここでマイグレートしておきましょう。

$ php artisan migrate
Migrating: 2019_08_17_014731_create_products_table
Migrated:  2019_08_17_014731_create_products_table (0.02 seconds)
Migrating: 2019_08_17_021139_add_is_admin_to_user_table
Migrated:  2019_08_17_021139_add_is_admin_to_user_table (0.01 seconds)

Administration Middlewareの作成

usersテーブルに追加された管理者フラグ(is_admin)を使用して、制限された管理コントローラーが使用するミドルウェアを作成し、要求ユーザーにアクセスを許可するかどうかを決定します。

まずはミドルウェアを作成していきましょう。

$ php artisan make:middleware AdminAuthentication

/app/Http/Middleware/AdminAuthentication.php

<?php

namespace App\Http\Middleware;
use Illuminate\Contracts\Auth\Guard; // Authを利用するためのuse
use Illuminate\Http\RedirectResponse; // リダイレクトクラスを利用するためのuse
use Closure;

class AdminAuthentication
{
    protected $auth;
    
    public function __construct(Guard $auth)
    {
        $this->auth = $auth;
    }

    public function handle($request, Closure $next)
    {
        if ($this->auth->check()) //リクエストするユーザーがログインしているかどうか
        {
            if ($this->auth->user()->is_admin == true) //ユーザーが管理者かどうか
            {
                return $next($request);
            }
        }
        // 管理者でない場合はリダイレクトでTOPページへ戻る
        return new RedirectResponse(url('/'));
    
    }
}

AdminAuthentication.phpへの変更を保存したら、ミドルウェアをアプリケーションに登録する必要があります。

以下のように追記をしましょう。
/app/Http/Kernel.php

    protected $routeMiddleware = [
        // 省略
        'admin' => \App\Http\Middleware\AdminAuthentication::class,
    ];

管理者用コントローラーを作成&ルーティング設定

次に管理者権限で実行する操作に関するコントローラーを作成していきます。

$ php artisan make:controller Admin/ProductController

ちなみに「Admin/ProductController」といったように「/」で単語を区切ることで、「ディレクトリ名/コントローラー名」という名前付けとなります。

つまりControllerディレクトリの中に、「Admin」というディレクトリが生成され、その直下に「ProductController」が生成されることとなるのです。

このメリットとしては、同名Controllerを生成できるというところです。今回のように管理者と一般ユーザーとで操作を分けたい場合には活用できるでしょう。

次にルーティングです。
Controllerディレクトリ>Adminディレクトリ>ProductControllerにはどのようにアクセスすればいいのでしょうか。

以下のように書きます。

/routes/web.php

Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'middleware' => 'admin'], function()
{
    Route::resource('products', 'ProductController');
});

このように書くことで、Adminディレクトリ直下のProductControllerにアクセスします。

また、先ほど作成したAdminMiddlewareを通すことで、ユーザーのis_adminカラムがtrueであるかどうかのチェックを行うことができます。

連載記事

【Laravel5.8+Stripe⓪】ECサイト作成チュートリアル概要 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe①】ベースプロジェクトの作成 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe②】メールフォームの実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe③】ユーザー認証機能のカスタマイズ - Laravelとねころっけくん5.8
【Laravel5.8+Stripe④】ディスカウントページを作成する - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑤】Laravel CasherとStripeを導入して管理者権限を設定する その1 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑥】Laravel CasherとStripeを導入して管理者権限を設定する その2 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑦】サブスクリプション決済の作成 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑧】請求書ダウンロード機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑨】サブスクリプションプラン変更機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe⑩】サブスクリプション中止機能の実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe11】Webhookの実装 - Laravelとねころっけくん5.8
【Laravel5.8+Stripe12】クーポン機能を実装する - Laravelとねころっけくん5.8
【Laravel5.8+Stripe13】ショッピングカートの実装 - Laravelとねころっけくん5.8