Laravel Stripe決済実装

実行環境
Stripe

API型決済ライブラリ 手数料 3.6%
テストモードあり 会員登録後 APIキー発行

stripe.com

新規アカウント作成後
新規ビジネスからアカウント名を追加

認証情報の準備

公開可能キー、シークレットキーを.envに追加


Stripeの使用方法

Laravel Casher (定期支払い向け)
Stripeが発行しているライブラリ

composerでインストール

$ composer require stripe/stripe-php

インストールできたらcomposer.jsonに追記される。


ルーティング設定
  • 決済処理のルーティング
  • 決済成功時、在庫を減らすルーティング
  • 決済キャンセル時、在庫を戻すルーティング
<?php
//cartのルート
Route::prefix('cart')-> 
  middleware('auth:users')->group(function(){
    Route::post('add', [CartController::class, 'add'])->name('cart.add');
    Route::get('/', [CartController::class, 'index'])->name('cart.index');
    Route::post('delete/{item}', [CartController::class, 'delete'])->name('cart.delete');
    //決済処理のルート
    Route::get('checkout', [CartController::class, 'checkout'])->name('cart.checkout');
    //決済成功時、cartを削除するルート
    Route::get('success', [CartController::class, 'success'])->name('cart.success');
    //決済キャンセル時、cartを戻すルート
    Route::get('cancel', [CartController::class, 'cancel'])->name('cart.cancel');
});
Controller作成

doc参照
stripe.com

ログインユーザーを取得

ユーザーに紐付いた商品($products)を取得

foreachでカートに入っている商品($products)、現在の在庫数($quantity)を取得し、
もし、カート内の在庫($product->pivot->quantity)が現在の在庫数($quantity)より多かったらリダイレクトをかける
そうでなければ、商品情報をstripe側に受け取れる形にして渡す→stripe側で用意してるパラメータを使用
(カート内の商品が在庫数より少なければ購入できるようにする。)

購入ボタンを押してstripe決済をしてから在庫を減らすと決済中に他のユーザーが在庫を変えた場合、決済完了後に減らす在庫がないなど発生するので、stripe決済の前に在庫を確認し、在庫を減らす処理を追加する。

envヘルパ関数でシークレットキーを取得し、支払い方法やカートに入った商品情報、決済成功時、キャンセル時のリダイレクト先などを$sessionへ格納
envヘルパ関数で公開可能キーの取得

<?php
public function checkout()
    {
        $user = User::findOrFail(Auth::id());
        $products = $user->products;
        //カートに入ってる商品
        $lineItems = [];
        foreach($products as $product){
            //現在の在庫数の取得
            $quantity ='';
            $quantity = Stock::where('product_id', $product->id)->sum('quantity');

            if($product->pivot->quantity > $quantity){
                return redirect()->route('user.cart.index');
            }else{
                $lineItem = [
                    'price_data' => [
                    'unit_amount' => $product->price,
                    'currency' => 'JPY',
 
                        'product_data' => [
                        'name' => $product->name,
                        'description' => $product->information,
                        ],
                    ],
                    'quantity' => $product->pivot->quantity,
                ];
                array_push($lineItems, $lineItem);
            }
        }
        //stripeに渡す前に在庫をへらす
        foreach($products as $product) {
            Stock::create([
            'product_id' => $product->id,
            'type' => \Constant::PRODUCT_LIST['reduce'],
            //カートの中の在庫数をへらす
            'quantity' => $product->pivot->quantity * -1
            ]);
        }

        \Stripe\Stripe::setApiKey(env('STRIPE_SECRET_KEY'));
        //支払い方法やカートに入った商品情報、リダイレクト先などを$sessionへ格納
        $session = \Stripe\Checkout\Session::create([
            //支払い方法
            'payment_method_types' => ['card'],
            'line_items' => [$lineItems],
            //1回払
            'mode' => 'payment',
            //支払い成功後のリダイレクト先
            'success_url' => route('user.cart.success'),
            'cancel_url' => route('user.cart.cancel'),
        ]);
        //公開可能キーの取得
        $publicKey = env('STRIPE_PUBLIC_KEY');
        //viewへ2つのキーが入った変数を渡す
        return view('user.checkout',compact('session', 'publicKey'));
    }
決済成功時
<?php
 public function success()
    {
        //決済成功時、cartを削除する
        Cart::where('user_id', Auth::id())->delete();
        return redirect()->route('user.items.index');
    }
決済キャンセル時

決済キャンセル時には決済前に在庫を減らしているので、それを戻す処理を追加する。

<?php
public function cancel()
    {
        //user情報取得
        $user = User::findOrFail(Auth::id());
        //stripe処理キャンセル時に在庫をふやす
        foreach($user->products as $product) {
            Stock::create([
            'product_id' => $product->id,
            'type' => \Constant::PRODUCT_LIST['add'],
            //カートの中の在庫数をふやす
            'quantity' => $product->pivot->quantity
            ]);
        }
        //キャンセル後カートへリダイレクト
        return redirect()->route('user.cart.index');
    }
view
  • checkoutボタンの追加
  • stripe.jsを読み込む
<div class="my-2">
  小計: {{ number_format($totalPrice)}}<span class="text-sm text-gray-700">円(税込)</span>
</div>
<div>
  <button onclick="location.href='{{ route('user.cart.checkout')}}'" class="flex ml-auto text-white bg-pink-500 border-0 py-2 px-6 focus:outline-none hover:bg-pink-600 rounded">購入する</button>
</div>
<p>決済ページへリダイレクトします。</p>
{{-- stripeの読み込み --}}
<script src="https://js.stripe.com/v3/"></script>
<script>
  // コントローラからpublicKey取得
const publicKey = '{{ $publicKey }}'
const stripe = Stripe(publicKey)
// 画面を読み込んだ瞬間実行
window.onload = function(){
  stripe.redirectToCheckout({
    // session->idで商品情報をstripeへ飛ばす
    sessionId:'{{ $session->id }}'}).then(function (result) {
    // エラーが発生した場合の遷移先
    window.location.href = '{{ route('user.cart.index') }}';
  });
}
</script>
動作確認

gyazo.com

gyazo.com