Laravel Stripe Payment

Created At: 2023-05-26 00:17:43 Updated At: 2023-06-02 23:31:25

Here in this tutorial we will see how make a restful API call that tries to make a Stripe payment from front end to your Laravel application. 

Make sure that you have Stripe sdk installed in project. If you did not installed you may run the below command to run it.

composer require stripe/stripe-php

It will install the Stripe sdk in the vendor directory.

Below the the end point or API call code. This code also depends on Order and Course Model. Make sure you have followed the tutorials to have them.

Here's the code for PayController

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Stripe\Webhook;
use Stripe\Customer;
use Stripe\Price;
use Stripe\Stripe;
use Stripe\Checkout\Session;
use Stripe\Exception\UnexpectedValueException;
use Stripe\Exception\SignatureVerificationException;
use Illuminate\Support\Carbon;
use App\Models\Course;
use App\Models\Order;

class PayController extends Controller
{
    //
    public function checkout(Request $request){
     try{
            $user = $request->user();
            $token = $user->token;
            $courseId= $request->id;

            /*
                Stripe api key
            */
            Stripe::setApiKey('your secret key');


            $courseResult = Course::where('id', '=', $courseId)->first();
            //invalid request
            if(empty($courseResult)){
                return response()->json([
                    'code' => 400,
                    'msg' => "Course does not exist",
                    'data'=>''
                ], 400);
            }

            $orderMap = [];

            $orderMap['course_id']= $courseId;
            $orderMap['user_token']=$token;
            $orderMap['status'] = 1;

            /*
                if the order has been placed before or not
                So we need Order model/table
            */

            $orderRes = Order::where($orderMap)->first();
            if(!empty($orderRes)){
                return response()->json([
                    'code' => 400,
                    'msg' => "You already bought this course",
                    'data' => $orderRes
                ], 400);
            }
            //new order for the user and let's submit
            $YOUR_DOMAIN = env('APP_URL');
            $map = [];
            $map['user_token'] = $token;
            $map['course_id'] = $courseId;
            $map['total_amount'] = $courseResult->price;
            $map['status'] = 0;
            $map['created_at'] = Carbon::now();
            $orderNum = Order::insertGetId($map);
            //create payment session

            $checkOutSession = Session::create(
                [
                    'line_items'=>[[
                        'price_data'=>[
                            'currency'=>'USD',
                            'product_data'=>[
                                'name'=>$courseResult->name,
                                'description'=>$courseResult->description,
                            ],
                            'unit_amount'=>intval(($courseResult->price)*100),
                        ],
                        'quantity'=>1,
                    ]],
                    'payment_intent_data'=>[
                        'metadata'=>['order_num'=>$orderNum, 'user_token'=>$token],
                    ],
                    'metadata' => ['order_num' => $orderNum, 'user_token' => $token],
                    'mode'=>'payment',
                    'success_url'=> $YOUR_DOMAIN . 'success',
                    'cancel_url'=> $YOUR_DOMAIN . 'cancel'
                ]
            );


            return response()->json([
                'code' => 200,
                'msg' => "success",
                'data'=>$$checkOutSession->url
            ], 200);
     }catch(\Throwable $th){


            return response()->json([
                'error' => $th->getMessage(),
            ], 500);
     }
    }
}

You need to notice that in checkout() function in Stripe::setApiKey("Your key"), you need to have your Api key and put in the function.

Web hook

Web hook is needed to comminicate between two servers. Stripe payment depends on web hook as well. We have to define our end points in stripe dashboard and the same would be would be in our api.php file. We have to define the body of the end point in PayController.

In the above image we have defined our routes with domain url together

    public function web_go_hooks()
    {
       // Log::info("11211-------");
        Stripe::setApiKey('sk_test_1NDjUSDcNOyMHK5HXM82Vp9SmGNrUbu4wTpn4KsvytjoVxnJXo6243K262SqdHHypMIloirm1xSVEnqW0edTSH1N00h9q3RWf3');
        $endpoint_secret = 'whsec_gX3qTxVVau1wapvQijmX6XJhAGirvwf';
        $payload = @file_get_contents('php://input');
        $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
        $event = null;
      //  Log::info("payload----" . $payload);

        try {
            $event = \Stripe\Webhook::constructEvent(
                $payload,
                $sig_header,
                $endpoint_secret
            );
        } catch (\UnexpectedValueException $e) {
            // Invalid payload
          //  Log::info("UnexpectedValueException" . $e);
            http_response_code(400);
            exit();
        } catch (\Stripe\Exception\SignatureVerificationException $e) {
            // Invalid signature
        //    Log::info("SignatureVerificationException" . $e);
            http_response_code(400);
            exit();
        }
     //   Log::info("event---->" . $event);
        // Handle the checkout.session.completed event
        if ($event->type == 'checkout.session.completed') {
            $session = $event->data->object;
           // Log::info("event->data->object---->" . $session);
            $metadata = $session["metadata"];
            $order_num = $metadata->order_num;
            $user_token = $metadata->user_token;
          //  Log::info("order_id---->" . $order_num);
            $map = [];
            $map["status"] = 1;
            $map["updated_at"] = Carbon::now();
            $whereMap = [];
            $whereMap["user_token"] = $user_token;
            $whereMap["id"] = $order_num;
            Order::where($whereMap)->update($map);
        }


        http_response_code(200);
    }

Success callback

Success end point gets called once you finish payment successfully. We are doing a web view here. So we need to define our html file and it's content. Create a file success.blade.php in your resources->views folder and put the code below.

<!DOCTYPE html>
<html>
<head>
  <title>Thanks for your order!</title>
  <link rel="stylesheet" href="css/style.css">
  <script src="js/client.js" defer></script>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <section>
    <div class="product Box-root">
      <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="16px" viewBox="0 0 14 16" version="1.1">
          <defs/>
          <g id="Flow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
              <g id="0-Default" transform="translate(-121.000000, -40.000000)" fill="#E184DF">
                  <path d="M127,50 L126,50 C123.238576,50 121,47.7614237 121,45 C121,42.2385763 123.238576,40 126,40 L135,40 L135,56 L133,56 L133,42 L129,42 L129,56 L127,56 L127,50 Z M127,48 L127,42 L126,42 C124.343146,42 123,43.3431458 123,45 C123,46.6568542 124.343146,48 126,48 L127,48 Z" id="Pilcrow"/>
              </g>
          </g>
      </svg>
      <div class="description Box-root">
        <h3>Your payment is successful !!</h3>
      </div>
    </div>
    <button id="checkout-and-portal-button" onclick="postMessage();">Go Back</button>
  </section>
</body>
<script type='text/javascript'>
function postMessage(){
    Pay.postMessage('success');
}

</script>
</html>

Download the css and js file

Checkout end point

Comment

Add Reviews