Paypal Payment Gateway Integration in Laravel [Step by Step]

In this post, we learn Paypal payment gateway integration in Laravel step by step, Paypal Is a world-famous payment gateway and this is too easy with Laravel.

For this tutorial, we use omnipay/paypal package to payment integration in Laravel 5.7 we start with a fresh Laravel project so we need to create a new Laravel Project. To create a fresh project use this command.

We are going to integrate PayPal Express Checkout in this article below.

Step:1 Create a new Project

laravel new paypal

Step:2  install the package omnipayvia the composer.

composer require league/omnipay omnipay/paypal

And generate the default layout of Laravel using the flowing command.

php artisan make:auth

Step: 3 Create some routes

Now add new routes for the PayPal integration under app/routes/web.php

Route::get('/paypal/{order?}','PayPalController@form')->name('order.paypal');
Route::post('/checkout/payment/{order}/paypal','PayPalController@checkout')->name('checkout.payment.paypal');
Route::get('/paypal/checkout/{order}/completed','PayPalController@completed')->name('paypal.checkout.completed');
Route::get('/paypal/checkout/{order}/cancelled','PayPalController@cancelled')->name('paypal.checkout.cancelled');
Route::post('/webhook/paypal/{order?}/{env?}','PayPalController@webhook')->name('webhook.paypal.ipn');
Route::get('payment-completed/{order}','PayPalController@paymentCompleted')->name('paymentCompleted');

Step: 4 Create Order table.

Create a  migration, modal, and Controller for an order using this command.

php artisan make:model Order -mcr

Adding the required models for this implementation.

app/Order.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    const PAYMENT_COMPLETED = 1;
    const PAYMENT_PENDING = 0;

    /**
     * @var string
     */
    protected $table = 'orders';

    /**
     * @var array
     */
    protected $dates = ['deleted_at'];

    /**
     * @var array
     */
    protected $fillable = ['transaction_id', 'amount', 'payment_status'];
}

Step: 5 Update migration table 

We already generated a new migration table for order, now add some columns.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTableOrders extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id');
            $table->integer('service_id');
            $table->string('transaction_id')->nullable();
            $table->float('amount')->unsigned()->nullable();
            $table->integer('payment_status')->unsigned()->default(0);
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('orders');
    }
}

Step:8 Create new Migration table Service

Using this flowing command.

php artisan make:model Service -mcr

Now, add some columns in Service table.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateServicesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('services', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('amount');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('services');
    }
}

then use this flowing command to migrate this service table.

php artisan migrate

Add some dummy data into the Service table.

Paypal payment gateway integration in Laravel 5.7 step by step

Step: 7 Add helper 

Adding a new helper class for generating gateway object and routes to interact with PayPal API.

app/PayPal.php

<?php

namespace App;

use Omnipay\Omnipay;

/**
 * Class PayPal
 * @package App
 */
class PayPal
{
    /**
     * @return mixed
     */
    public function gateway()
    {
        $gateway = Omnipay::create('PayPal_Express');

        $gateway->setUsername(config('services.paypal.username'));
        $gateway->setPassword(config('services.paypal.password'));
        $gateway->setSignature(config('services.paypal.signature'));
        $gateway->setTestMode(config('services.paypal.sandbox'));

        return $gateway;
    }

    /**
     * @param array $parameters
     * @return mixed
     */
    public function purchase(array $parameters)
    {
        $response = $this->gateway()
            ->purchase($parameters)
            ->send();

        return $response;
    }

    /**
     * @param array $parameters
     */
    public function complete(array $parameters)
    {
        $response = $this->gateway()
            ->completePurchase($parameters)
            ->send();

        return $response;
    }

    /**
     * @param $amount
     */
    public function formatAmount($amount)
    {
        return number_format($amount, 2, '.', '');
    }

    /**
     * @param $order
     */
    public function getCancelUrl($order)
    {
        return route('paypal.checkout.cancelled', $order->id);
    }

    /**
     * @param $order
     */
    public function getReturnUrl($order)
    {
        return route('paypal.checkout.completed', $order->id);
    }

    /**
     * @param $order
     */
    public function getNotifyUrl($order)
    {
        $env = config('services.paypal.sandbox') ? "sandbox" : "live";

        return route('webhook.paypal.ipn', [$order->id, $env]);
    }
}

Step: 7 updates config/service.php

Laravel comes with a config file  config/service.php to be used with third-party API services. For this implementation add following credentials under the same file as below.

'paypal' => [
        'username' => env('PAYPAL_USERNAME'),
        'password' => env('PAYPAL_PASSWORD'),
        'signature' => env('PAYPAL_SIGNATURE'),
        'sandbox' => env('PAYPAL_SANDBOX'),
    ],

Step:8 Create a new controller 

To create a new controller use this flowing command.

php artisan make:controller PayPalController

To handle and maintain the request coming into the application we need to pass information to the controller. We are update PayPalController.php 

<?php

namespace App\Http\Controllers;

use App\Order;
use App\PayPal;
use App\Service;
use App\User;
use Illuminate\Http\Request;

/**
 * Class PayPalController
 * @package App\Http\Controllers
 */
class PayPalController extends Controller
{
    
    
    public function form(Request $request, $service_id)
    {

        $service = Service::findOrFail($service_id);


       
        $order = new Order;
        $transaction_id = rand(10000000,99999999);
        $order->user_id = 1 ;  //user id
        $order->transaction_id = $transaction_id;
        $order->service_id    = $service->id;
        $order->amount    = $service->amount;
        $order->save();

        // the above order is just for example.
        return view('form', compact('service','transaction_id'));
    }

    /**
     * @param $order_id
     * @param Request $request
     */
    public function checkout($transaction_id, Request $request)
    {
            $order = Order::where('transaction_id', decrypt($transaction_id))->first();

            $paypal = new PayPal;

            $response = $paypal->purchase([
                'amount' => $paypal->formatAmount($order->amount),
                'transactionId' => $order->transaction_id,
                'currency' => 'USD',
                'cancelUrl' => $paypal->getCancelUrl($order),
                'returnUrl' => $paypal->getReturnUrl($order),
            ]);

            if ($response->isRedirect()) {
                $response->redirect();
            }

            return redirect()->back()->with([
                 'message' => $response->getMessage(),
                
            ]);
    }

    /**
     * @param $order_id
     * @param Request $request
     * @return mixed
     */
    public function completed($order_id, Request $request)
    {

        $order = Order::findOrFail($order_id);

        $paypal = new PayPal;

        $response = $paypal->complete([
            'amount' => $paypal->formatAmount($order->amount),
            'transactionId' => $order->transaction_id,
            'currency' => 'USD',
            'cancelUrl' => $paypal->getCancelUrl($order),
            'returnUrl' => $paypal->getReturnUrl($order),
            'notifyUrl' => $paypal->getNotifyUrl($order),
        ]);

        if ($response->isSuccessful()) {
            $order->update([
                'transaction_id' => $response->getTransactionReference(),
                'payment_status' => Order::PAYMENT_COMPLETED,
            ]);

            return redirect()->route('paymentCompleted', encrypt($order_id))->with([
                'message' => 'You recent payment is sucessful with reference code ' . $response->getTransactionReference(),
            ]);
        }

        return redirect()->back()->with([
            'message' => $response->getMessage(),
        ]);

    }
    public function paymentCompleted($order)
    {
        # code...
        return "Thanks! payment completed";
    }

    /**
     * @param $order_id
     */
    public function cancelled($order_id)
    {
        $order = Order::findOrFail($order_id);

        return redirect()->route('order.paypal', encrypt($order_id))->with([
            'message' => 'You have cancelled your recent PayPal payment !',
        ]);
    }

        /**
     * @param $order_id
     * @param $env
     * @param Request $request
     */
    public function webhook($order_id, $env, Request $request)
    {
        // to do with new release of sudiptpa/paypal-ipn v3.0 (under development)
    }
    
}

Step:9 Create the view

Here we create two views one for send amount to Paypal and one for land after payment

form.blade.php

@extends('layouts.app')

@section('content')
  <div >
    <div >
      <div >
        @if(session()->has('message'))
          <p >
            {{ session('message') }}
          </p>
        @endif
        <div >
          <div >
            <img src="{{ asset('images/paypal.png') }}" >
          </div>
          <div >
            <img src="{{ asset('images/laravel.png') }}" >
          </div>
        </div>
        <p><strong>Order Overview !</strong></p>
        <hr>
        <p>Item : Yearly Subscription cost !</p>
        <p>Amount : ${{ $service->amount }}</p>
        <hr>
      </div>
      <div >
        <form method="POST" action="{{ route('checkout.payment.paypal', ['transaction_id' => encrypt($transaction_id)]) }}">
          {{ csrf_field() }}
          <button >
            <i aria-hidden="true"></i> Pay with PayPal
          </button>
        </form>
      </div>
    </div>
  </div>
@stop

Ok, Now We almost have done. 

Step:10 Update .env file with PayPal credential details.

PAYPAL_USERNAME = your_user_mail
PAYPAL_PASSWORD = your_password
PAYPAL_SIGNATURE = your_signature
PAYPAL_SANDBOX  = sandbox

Now, everything is ok, now visit this URL  //localhost:8000/paypal/1 Here 1 is service in which you want to buy.

Paypal payment gateway integration in Laravel 5.7 step by step

Here we store some record of failed and success payment payment_status = 1 show success payment and 0 show failed.

So here we complete tutorials Paypal payment gateway integration in Laravel 5.7 step by step