Laravel Stripe決済実装
実行環境
- mysql5.7
- phpmyadmin
- Laravel Framework 8.83.26
- PHP Version 8.1.13
- Apache/2.4.54 (Debian)
Stripe
API型決済ライブラリ 手数料 3.6%
テストモードあり 会員登録後 APIキー発行
新規アカウント作成後
新規ビジネスからアカウント名を追加
認証情報の準備
公開可能キー、シークレットキーを.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>