Integrate PayPal Plus on Laravel 8

20 Oct, 2020

If you are interested to integrate PayPal plus with Laravel 8, this tutorial will help you a lot. Today, I will guide you on how to integrate PayPal Plus with Laravel. Let's get started.

Requirements

PayPal Account

You need to have a Paypal account in order to process.

Create Sandbox Accounts.

Navigate to Sandbox > Accounts then click on the blue color Create Account button. On the popup box, set

  • Account Type: Business
  • Country: Brazil

Create Sandbox App

Navigate to Sandbox > My Apps & Credentials click on the blue color Create App button. In the form,

  • Set an app name. It can be anything you want.
  • Choose newly created sandbox business email.

Get APP Credentials

Once you have done successfully, navigate to the newly created app. You should able to see Client ID and Client Secret for the app.

Generate Fake Credit Card

Navigate to Mock > Credit Card Generator and then create a credit card. It will give you a card number, expired date and CVV.

Laravel Integration

Let's dig into laravel application now.

Step 1: Define Routes:

Go to web.php file and define a GET route /paypal.


Route::get('paypal', [PayPalController::class, 'index']);

Step 2: Create Controller:

Now let's create a controller called PayPalController.

php artisan make:controller PayPalController

In the index() method, first, let's send a request for creating the payment request as described in the documentation

    public function index()
    {
        $clientId = "PayPal Private Key";
        $secret = "PayPal Public Key";

        // Get Bearer Token
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, "https://api.sandbox.paypal.com/v1/oauth2/token");
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSLVERSION , 6);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
        curl_setopt($ch, CURLOPT_USERPWD, $clientId.":".$secret);
        curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=client_credentials");

        $token = json_decode(curl_exec($ch), true);
        $bearerToken = $token['access_token'];

        $total = 121;

        $postData = [
            'intent' => 'sale',
            'payer' => [
                'payment_method' => 'paypal',
            ],
                'transactions' => [
                    0 => [
                        'amount' => [
                            'currency' => 'BRL',
                            'total' => $total,
                            'details' => [
                                'shipping' => '0',
                                'subtotal' => $total,
                                'shipping_discount' => '0.00',
                                'insurance' => '0.00',
                                'handling_fee' => '0.00',
                                'tax' => '0.00',
                            ],
                        ],
                    'description' => 'This is the payment transaction description',
                    'payment_options' => [
                        'allowed_payment_method' => 'IMMEDIATE_PAY',
                    ],
                    'item_list' => [
                        'shipping_address' => [
                            'recipient_name' => 'PP Plus Recipient',
                            'line1' => 'Gregório Rolim de Oliveira, 42',
                            'line2' => 'JD Serrano II',
                            'city' => 'Votorantim',
                            'country_code' => 'BR',
                            'postal_code' => '18117-134',
                            'state' => 'São Paulo',
                            'phone' => '0800-761-0880',
                        ],
                        'items' => [
                            0 => [
                                'name' => 'handbag',
                                'description' => 'red diamond',
                                'quantity' => '1',
                                'price' => $total,
                                'tax' => '0',
                                'sku' => 'product34',
                                'currency' => 'BRL',
                            ],
                        ],
                    ],
                ],
            ],
            'redirect_urls' => [
                'return_url' => 'https://example.com/return',
                'cancel_url' => 'https://example.com/cancel',
            ],
        ];

        // Send request for Permission
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, 'https://api.sandbox.paypal.com/v1/payments/payment');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));

        $headers = array();
        $headers[] = 'Content-Type: application/json';
        $headers[] = 'Authorization: Bearer ' . $bearerToken;
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            echo 'Error:' . curl_error($ch);
        }

       $paymentData = json_decode($result, true);

       return view("paypal-plus")
            ->with('paymentData', $paymentData);
    }

Create a view page

Now, let's create a view page called resources/views/paypal-plus.blade.php for rendering the paypal form.

    <script src="https://www.paypalobjects.com/webstatic/ppplusdcc/ppplusdcc.min.js" type="text/javascript">
    </script>

    <div id="ppplus"></div>

    <input type="hidden" id="paypal_approval_url" value="{{ $paymentData['links'][1]['href'] }}">
    <input type="hidden" id="paypal_payment_id" value="{{ $paymentData['id'] }}">

    <script type="application/javascript">
        var approvalUrl = document.getElementById('paypal_approval_url').value;
        var paymentId = document.getElementById('paypal_payment_id').value;

        var ppp = PAYPAL.apps.PPP({
            "approvalUrl": approvalUrl,
            "placeholder": "ppplus",
            "mode": "sandbox",
            "payerEmail": "[email protected]",
            "payerFirstName": "Thouhedul",
            "payerLastName": "Islam",
            "payerTaxId": "424.159.708-40",
            "country": "BR",
            "collectBillingAddress": false,
            onContinue: function (rememberedCards, payerId, token, term) {
                window.location = 'http://127.0.0.1:8000/paypal-approval?rememberedCards='
                    + rememberedCards 
                    + '&payerId=' + payerId
                    + '&token=' + token
                    + '&term=' + term
                    + '&paymentId=' + paymentId;
            },
        });
    </script>

    <button
        type="submit"
        id="continueButton"
        onclick="ppp.doContinue(); return false;"> Checkout
    </button>

Note here, I have set a redirect location once user submit the form. So, let's define a new route for that.

Define route for redirect location

Route::get('paypal-approval', [PayPalController::class, 'post']);

Create Post Method

To process, let's create a method called post(). $clientId = "PayPal Public ID"; $secret = "PayPal Private ID";

    public function post()
    {
        $payerId = request()->payerId;
        $rememberedCards = request()->rememberedCards;
        $token = request()->token;
        $paymentId = request()->paymentId;

        $clientId = "PayPal Private Key";
        $secret = "PayPal Public Key";

        // Get Bearer Token
        $btCh = curl_init();

        curl_setopt($btCh, CURLOPT_URL, "https://api.sandbox.paypal.com/v1/oauth2/token");
        curl_setopt($btCh, CURLOPT_HEADER, false);
        curl_setopt($btCh, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($btCh, CURLOPT_SSLVERSION , 6);
        curl_setopt($btCh, CURLOPT_POST, true);
        curl_setopt($btCh, CURLOPT_RETURNTRANSFER, true); 
        curl_setopt($btCh, CURLOPT_USERPWD, $clientId.":".$secret);
        curl_setopt($btCh, CURLOPT_POSTFIELDS, "grant_type=client_credentials");

        $token = json_decode(curl_exec($btCh), true);
        $bearerToken = $token['access_token'];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "https://api.sandbox.paypal.com/v1/payments/payment/$paymentId/execute");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n  \"payer_id\": \"$payerId\"\n}");

        $headers = array();
        $headers[] = 'Content-Type: application/json';
        $headers[] = 'Authorization: Bearer ' . $bearerToken;
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        $result = curl_exec($ch);

        if (curl_errno($ch)) {
            echo 'Error:' . curl_error($ch);
        }

        curl_close($ch);

        return $approvedData = json_decode($result, true);
    }

Testing

Now let's test the fake card that we have generated earlier. Now, you have to put card number, expired date, CVV and put any name. Once done, submit the form.

If everything goes smoothly, you will get some data like this. Where you will get "state": "approved" and should have "id": "PAYID-L6HGX3A4MT388022P233113E".

BTW, if you need to show any thank you page, then you can do operation based on state and id on the condition.

{
    "id": "PAYID-L6HGX3A4MT388022P233113E",
    "intent": "sale",
    "state": "approved",
    "cart": "66099532VT8552054",
    "payer": {
        "payment_method": "paypal",
        "status": "UNVERIFIED",
        "payer_info": {
            "email": "[email protected]",
            "first_name": "John",
            "last_name": "Doe",
            "payer_id": "CC3S9AQAUZH62",
            "shipping_address": {
                "recipient_name": "PP Plus Recipient",
                "line1": "Gregório Rolim de Oliveira, 42",
                "line2": "JD Serrano II",
                "city": "Votorantim",
                "state": "São Paulo",
                "postal_code": "18117-134",
                "country_code": "BR",
                "normalization_status": "UNKNOWN"
                },
            "tax_id_type": "BR_CPF",
            "tax_id": "42415970840",
            "country_code": "BR"
            }
        },
        ...

Hope this post will be helpful for you.

Hello. I’m Thouhedul Islam. Some people also know me as Suchi.

I am a Full-stack Developer. I love to play with PHP especially Laravel, JavaScript, MySql, and latest web technology. Currently, I am working for CartX as my day time job.

At night I love to solve problems for the community. I love to be active at Laracasts.