Laravel E-Commerce Application Development – Payment Processing
Laravel E-Commerce Application Development ( 27 Lessons )
In this course, you’ll learn how to create an E-Commerce Website from scratch in Laravel. The process has never been easier I’ll take you from the very beginning stages of setting up Laravel till the last steps of adding products to the cart. If you’ve good understanding & experience in PHP & MySQL then this course is for you.
see full series- Laravel E-Commerce Application Development – Introduction
- Laravel E-Commerce Application Development – Initial Project Setup
- Laravel E-Commerce Application Development – Assets Setup Using Laravel Mix
- Laravel E-Commerce Application Development – Admin Model and Migration
- Laravel E-Commerce Application Development – Backend Admin Authentication
- Laravel E-Commerce Application Development – Base Controller and Repository
- Laravel E-Commerce Application Development – Settings Section Part 1
- Laravel E-Commerce Application Development – Settings Section Part 2
- Laravel E-Commerce Application Development – Categories Section Part 1
- Laravel E-Commerce Application Development – Categories Section Part 2
- Laravel E-Commerce Application Development – Attributes Section Part 1
- Laravel E-Commerce Application Development – Attributes Section Part 2
- Laravel E-Commerce Application Development – Attributes Section Part 3
- Laravel E-Commerce Application Development – Brands Section
- Laravel E-Commerce Application Development – Products Section Part 1
- Laravel E-Commerce Application Development – Products Section Part 2
- Laravel E-Commerce Application Development – Products Section Part 3
- Laravel E-Commerce Application Development – Products Section Part 4
- Laravel E-Commerce Application Development – Frontend Login & Registration
- Laravel E-Commerce Application Development – Categories Navigation
- Laravel E-Commerce Application Development – Catalog Listing
- Laravel E-Commerce Application Development – Product Details Page
- Laravel E-Commerce Application Development – Shopping Cart
- Laravel E-Commerce Application Development – Checkout
- Laravel E-Commerce Application Development – Payment Processing
- Laravel E-Commerce Application Development – Order Management
- Laravel E-Commerce Application Development – Wrap Up
This is part 24 of the Laravel E-Commerce Application Development series. In this part, we will complete payment processing for orders using PayPal payment provider.
I assume you should have the e-commerce application project on your machine or you can grab it from Laravel E-Commerce Application repository, we will start from where we left it in the last part.
Let me explain how the PayPal Payment Processing works before jumping right into the coding.
We will start from where we left in the previous post where we saved the order details into the database. After saving the order into the database, we will send the order to another payment service class where we will create the PayPal transaction request. After creating the transaction through PayPal API we will get an approval link. Then we head over the user to that approval link.
On the PayPal page, the user will be asked to log in or use a credit/debit card to make their purchase. When the purchase will be completed, PayPal will redirect the user to a URL which we will provide while creating the PayPal transaction. On our side, we have to implement the logic to get the transaction id if the status is approved from the PayPal and then save those details in the orders table. Finally, we will show the customer a success page where we will display the order number for user record. During the whole process, we will also catch the exceptions if anything goes wrong.
Here is how it should look like after completing this Laravel tutorial.

Updating User Model for Order Relationship
Ohh we forgot to add the relationship between a User and a Order. Open the user model class and add below.
public function orders() { return $this->hasMany(Order::class); }
Installing PayPal PHP SDK
To use PayPal API in our application we will need to install the PayPal SDK for PHP. PayPal provides many different types of payment solutions so you will find many Laravel packages but I prefer to use the official PayPal PHP SDK.
To install the PayPal SDK, head over to your command line terminal and run the below command.
composer install paypal/rest-api-sdk-php
Before proceeding make sure you have saved the PayPal API keys in your settings section.
Creating PayPal Payment Processing Class
We will start by creating a seprate payment processing class, so create a new folder in app/
folder and name it Services. Create a new PHP class file in this folder and name it PayPalService.php.
Add the below code in this newly created file.
namespace App\Services; use PayPal\Api\Payer; use PayPal\Api\Item; use Mockery\Exception; use PayPal\Api\Amount; use PayPal\Api\Payment; use PayPal\Api\Details; use PayPal\Api\ItemList; use PayPal\Rest\ApiContext; use PayPal\Api\Transaction; use PayPal\Api\RedirectUrls; use PayPal\Api\PaymentExecution; use PayPal\Auth\OAuthTokenCredential; use PayPal\Exception\PayPalConnectionException; class PayPalService { // }
As you can see this class is empty but I have included all the required classes for making a PayPal payment work.
Updating placeOrder Method in Checkout Controller
Now we have an empty Payment Processing class, before making any implementation I would like to add this class in our CheckoutController class. Open the CheckoutController controller and include the PayPalService class like below.
use App\Services\PayPalService;
Next declare a new protected property and name it $payPal
.
protected $payPal;
Now we will update our constructor to inject the PayPalService class in it.
public function __construct(OrderContract $orderRepository, PayPalService $payPal) { $this->payPal = $payPal; $this->orderRepository = $orderRepository; }
Next, update the placeOrder()
method with the below one.
public function placeOrder(Request $request) { // Before storing the order we should implement the // request validation which I leave it to you $order = $this->orderRepository->storeOrderDetails($request->all()); // You can add more control here to handle if the order // is not stored properly if ($order) { $this->payPal->processPayment($order); } return redirect()->back()->with('message','Order not placed'); }
After storing the order in the database, we send the order to the PayPalService class’s processPayment() method which will implement in the coming section.
Setting Up PayPal API Keys in Constructor
In this section, we will update our PayPalService class and firstly set up our API keys using the constructor method of this class.
I will share the snippet by snippet code for explaination, later in this post you will find the full PayPalService class. Also I will be adding some inline comments for seprating each section.
We will start by declaring a protected property for PayPal API.
protected $payPal;
Now we will create a constructor method and set the PayPal API connection.
public function __construct() { if (config('settings.paypal_client_id') == '' || config('settings.paypal_secret_id') == '') { return redirect()->back()->with('error', 'No PayPal settings found.'); } $this->payPal = new ApiContext( new OAuthTokenCredential( config('settings.paypal_client_id'), config('settings.paypal_secret_id') ) ); // To use PayPal in live mode you have to add // the below, I prefer to use the sandbox mode only. //$this->payPal->setConfig( // array('mode' => 'live') //); }
In this constructor method, firstly we will check if the PayPal API keys are set up in the settings config, if not then we send the user back with a message.
Next, we are setting the payPal
property to the new APIContext class which takes a new object of the OAuthTokenCredential class and pass the API keys to it.
Creating processPayment Method
In your CheckoutController you can see we are calling a processPayment() method. Let’s add that to our PayPalService class.
public function processPayment($order) { }
Now we will start to fill this function with PayPal payment logic.
Add below code snippet in the processPayment() method.
// Add shipping amount if you want to charge for shipping $shipping = sprintf('%0.2f', 0); // Add any tax amount if you want to apply any tax rule $tax = sprintf('%0.2f', 0);
In the above code, we are setting up the shipping and tax values for PayPal.
Next, add the below code snippet.
// Create a new instance of Payer class $payer = new Payer(); $payer->setPaymentMethod("paypal");
In this we are making a new instance of Payer class and setting the payment method to paypal
.
Next, we will add the order items to our PayPal transaction.
// Adding items to the list $items = array(); foreach ($order->items as $item) { $orderItems[$item->id] = new Item(); $orderItems[$item->id]->setName($item->product->name) ->setCurrency(config('settings.currency_code')) ->setQuantity($item->quantity) ->setPrice(sprintf('%0.2f', $item->price)); array_push($items, $orderItems[$item->id]); } $itemList = new ItemList(); $itemList->setItems($items);
In this code block, we are looping through the $order
items and adding them to the $items
array. Note that each item will be an instance of the PayPal\Api\Item class.
Next we are creating a new instance of the PayPal\Api\ItemList class and setting items by passing the $items
array we just created.
Now we will create details for this PayPal transaction by creating an instance of the PayPal\Api\Details class.
// Setting Shipping Details $details = new Details(); $details->setShipping($shipping) ->setTax($tax) ->setSubtotal(sprintf('%0.2f', $order->grand_total));
As you can see we have passed the $shipping
and $tax
rates to this instance class along with the total price of this order.
Now we will set up the currency details by creating a new instance of the PayPal\Api\Amount class.
// Create chargeable amount $amount = new Amount(); $amount->setCurrency(config('settings.currency_code')) ->setTotal(sprintf('%0.2f', $order->grand_total)) ->setDetails($details);
Firstly setting the currency which we have in our settings config and then passing the total amount of the order. Lastly we are setting the details by passing $details
.
Now we have the items list and the amount for PayPal API, we will now create a new instance of PayPal\Api\Transaction class.
// Creating a transaction $transaction = new Transaction(); $transaction->setAmount($amount) ->setItemList($itemList) ->setDescription($order->user->full_name) ->setInvoiceNumber($order->order_number);
Starting by creating a new instance of Transaction class and then setting the amount to $amount
and in description I am passing on the user name. Next set the invoice number to our order’s order number.
Next, we will create new instance of the PayPal\Api\RedirectUrls. This class takes two values, return URL (after successful completion where PayPal will redirect the user) and the cancel URL (if the user cancels the payment where he should be redirected).
// Setting up redirection urls $redirectUrls = new RedirectUrls(); $redirectUrls->setReturnUrl(route('checkout.payment.complete')) ->setCancelUrl(route('checkout.index'));
As you can see for return URL, I am passing a new route which we will add in the next section and for cancel URL I am returning the user back to the checkout page.
Now we will create the payment using the PayPal\Api\Payment class.
// Creating payment instance $payment = new Payment(); $payment->setIntent("sale") ->setPayer($payer) ->setRedirectUrls($redirectUrls) ->setTransactions(array($transaction));
As you can see that we created the new instance of Payment class and set the intent to sale. Next, we pass the redirect URLs and the transaction of our order.
So far we have created the payment using the PayPal API, now we will call the create()
method which will create the PayPal payment and return us an approval URL where we can redirect our user.
Add the below code right after the above code block.
try { $payment->create($this->payPal); } catch (PayPalConnectionException $exception) { echo $exception->getCode(); // Prints the Error Code echo $exception->getData(); // Prints the detailed error message exit(1); } catch (Exception $e) { echo $e->getMessage(); exit(1); } $approvalUrl = $payment->getApprovalLink(); header("Location: {$approvalUrl}"); exit;
Let me go through line by line for explanation, we are trying to create a payment using the $payment
passing the payPal
property which actually holds our API keys. If the payment is not created, we are catching the exceptions and printing the errors.
In case we don’t get any errors and payment created successfully, we get the approval link by calling the getApprovalLink()
on $payment
.
Finally, we are setting the browser header to load the approval link and exit the function.
Adding Payment Complete Route
In the last section, we add the route to the return URL for PayPal payment let’s add that in our application.
Open the routes/web.php
file and right after the place order route add below.
Route::get('checkout/payment/complete', 'Site\CheckoutController@complete')->name('checkout.payment.complete');
Adding complete Method in Checkout Controller
Now we will add the complete()
method to our CheckoutController.
public function complete(Request $request) { $paymentId = $request->input('paymentId'); $payerId = $request->input('PayerID'); $status = $this->payPal->completePayment($paymentId, $payerId); $order = Order::where('order_number', $status['invoiceId'])->first(); $order->status = 'processing'; $order->payment_status = 1; $order->payment_method = 'PayPal -'.$status['salesId']; $order->save(); Cart::clear(); return view('site.pages.success', compact('order')); }
When a user make the payment on PayPal, PayPal will send the user back to this method long with paymentId and PayerID. Firstly, we will grab that using the Laravel’s Request class then we will send the both values to the our PayPalService class’s completePayment method (we will implement this in the next section).
Once, we get the sales id and invoice id from the PayPal, we open the order by finding it the order_number
.
PayPal’s invoice id will be same as our order number because that’s what we sent in the payment object.
We save the order to processing, set the payment status to 1 (completed) and then set the sales id generated by the PayPal to the payment_method
.
After all this process, we will create the shopping cart and return the user to the success page with the order details.
Adding completePayment Method in Payment Processing Class
In the previous section, we are calling completePayment()
so let’s implement that.
Add the below method in your PayPalService class.
public function completePayment($paymentId, $payerId) { $payment = Payment::get($paymentId, $this->payPal); $execute = new PaymentExecution(); $execute->setPayerId($payerId); try { $result = $payment->execute($execute, $this->payPal); } catch (PayPalConnectionException $exception) { $data = json_decode($exception->getData()); $_SESSION['message'] = 'Error, '. $data->message; // implement your own logic here to show errors from paypal exit; } if ($result->state === 'approved') { $transactions = $result->getTransactions(); $transaction = $transactions[0]; $invoiceId = $transaction->invoice_number; $relatedResources = $transactions[0]->getRelatedResources(); $sale = $relatedResources[0]->getSale(); $saleId = $sale->getId(); $transactionData = ['salesId' => $saleId, 'invoiceId' => $invoiceId]; return $transactionData; } else { echo "<h3>".$result->state."</h3>"; var_dump($result); exit(1); } }
In the above method, we are getting the payment from the PayPal API and creating a new instance of the PayPal/Api/PaymentExecution class by setting the payer id. Then we are calling the execute()
method on our $payment
by pssing the $execute
and the $this->payPal
which holds the API keys.
Upon successfull call to PayPal API, we get the state
of the order if it’s approved then we grab the invoice_number and salesId form the transaction of the current payment.
Above procedure in simple word is loading payment details from PayPal and checking its status to grab the further more information. You can read more about PayPal API on the PayPal’s Developer Documentation site.
After getting the required information, we send it back to the CheckoutController class.
Creating Order Success Page
In the section before the last one, we finally displayed the success page for order completion. Let’s create a new view in resources/views/site/pages folder and name it success.blade.php
.
Add the below markup in this file, which is simply showing the order number to the user.
@extends('site.app') @section('title', 'Order Completed') @section('content') <section class="section-pagetop bg-dark"> <div class="container clearfix"> <h2 class="title-page">Order Completed</h2> </div> </section> <section class="section-content bg padding-y border-top"> <div class="container"> <div class="row"> <main class="col-sm-12"> <p class="alert alert-success">Your order placed successfully. Your order number is : {{ $order->order_number }}.</p></main> </div> </div> </section> @stop
Great. We have completed the payment processing using PayPal for our eCommerce application. Before heading our to website and try it, I would like to create a page for authenticated users where we can show them their orders.
Adding Account Orders Route
Open the routes/web.php
file and add the below route after the order complete route.
Route::get('account/orders', 'Site\AccountController@getOrders')->name('account.orders');
Adding Dropdown Link to Header
Now open the resources/views/site/partials/header.blade.php file and add the below link just before the logout link.
<a class="dropdown-item" href="{{ route('account.orders') }}">Orders</a>
Creating Account Controller
Now open the command line terminal and run the below command to generate the AccountController class.
php artisan make:controller Site\AccountController
Adding getOrders Method to Account Controller
Open the AccountController class and add the below method in it.
namespace App\Http\Controllers\Site; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class AccountController extends Controller { public function getOrders() { $orders = auth()->user()->orders; return view('site.pages.account.orders', compact('orders')); } }
In this method, we are loading the orders for an authenticated user and passing on to a new view.
Creating Account Orders View
Create the orders.blade.php
file in resources/views/site/pages/account folder and add the below markup in it.
@extends('site.app') @section('title', 'Orders') @section('content') <section class="section-pagetop bg-dark"> <div class="container clearfix"> <h2 class="title-page">My Account - Orders</h2> </div> </section> <section class="section-content bg padding-y border-top"> <div class="container"> <div class="row"> </div> <div class="row"> <main class="col-sm-12"> <table class="table table-hover"> <thead> <tr> <th scope="col">Order No.</th> <th scope="col">First Name</th> <th scope="col">Last Name</th> <th scope="col">Order Amount</th> <th scope="col">Qty.</th> <th scope="col">Status</th> </tr> </thead> <tbody> @forelse ($orders as $order) <tr> <th scope="row">{{ $order->order_number }}</th> <td>{{ $order->first_name }}</td> <td>{{ $order->last_name }}</td> <td>{{ config('settings.currency_symbol') }}{{ round($order->grand_total, 2) }}</td> <td>{{ $order->item_count }}</td> <td><span class="badge badge-success">{{ strtoupper($order->status) }}</span></td> </tr> @empty <div class="col-sm-12"> <p class="alert alert-warning">No orders to display.</p> </div> @endforelse </tbody> </table> </main> </div> </div> </section> @stop
This view consists of a simple table which shows the order’s information. Now head over to your site and try to make an order then make a payment on PayPal using the sandbox account, you will be redirected back to the success page everything goes right.
I know you will face some problems if you are doing all this first time, most issues will be regarding the PayPal developer account or API keys. Make sure you have a developer account and generate the keys for sandbox mode.
PayPalService Class File
Here is the content of the full class.
namespace App\Services; use PayPal\Api\Payer; use PayPal\Api\Item; use Mockery\Exception; use PayPal\Api\Amount; use PayPal\Api\Payment; use PayPal\Api\Details; use PayPal\Api\ItemList; use PayPal\Rest\ApiContext; use PayPal\Api\Transaction; use PayPal\Api\RedirectUrls; use PayPal\Api\PaymentExecution; use PayPal\Auth\OAuthTokenCredential; use PayPal\Exception\PayPalConnectionException; class PayPalService { protected $payPal; public function __construct() { if (config('settings.paypal_client_id') == '' || config('settings.paypal_secret_id') == '') { return redirect()->back()->with('error', 'No PayPal settings found.'); } $this->payPal = new ApiContext( new OAuthTokenCredential( config('settings.paypal_client_id'), config('settings.paypal_secret_id') ) ); // To use PayPal in live mode you have to add // the below, I prefer to use the sandbox mode only. //$this->payPal->setConfig( // array('mode' => 'live') //); } public function processPayment($order) { // Add shipping amount if you want to charge for shipping $shipping = sprintf('%0.2f', 0); // Add any tax amount if you want to apply any tax rule $tax = sprintf('%0.2f', 0); // Create a new instance of Payer class $payer = new Payer(); $payer->setPaymentMethod("paypal"); // Adding items to the list $items = array(); foreach ($order->items as $item) { $orderItems[$item->id] = new Item(); $orderItems[$item->id]->setName($item->product->name) ->setCurrency(config('settings.currency_code')) ->setQuantity($item->quantity) ->setPrice(sprintf('%0.2f', $item->price)); array_push($items, $orderItems[$item->id]); } $itemList = new ItemList(); $itemList->setItems($items); // Setting Shipping Details $details = new Details(); $details->setShipping($shipping) ->setTax($tax) ->setSubtotal(sprintf('%0.2f', $order->grand_total)); // Create chargeable amount $amount = new Amount(); $amount->setCurrency(config('settings.currency_code')) ->setTotal(sprintf('%0.2f', $order->grand_total)) ->setDetails($details); // Creating a transaction $transaction = new Transaction(); $transaction->setAmount($amount) ->setItemList($itemList) ->setDescription($order->user->full_name) ->setInvoiceNumber($order->order_number); // Setting up redirection urls $redirectUrls = new RedirectUrls(); $redirectUrls->setReturnUrl(route('checkout.payment.complete')) ->setCancelUrl(route('checkout.index')); // Creating payment instance $payment = new Payment(); $payment->setIntent("sale") ->setPayer($payer) ->setRedirectUrls($redirectUrls) ->setTransactions(array($transaction)); try { $payment->create($this->payPal); } catch (PayPalConnectionException $exception) { echo $exception->getCode(); // Prints the Error Code echo $exception->getData(); // Prints the detailed error message exit(1); } catch (Exception $e) { echo $e->getMessage(); exit(1); } $approvalUrl = $payment->getApprovalLink(); header("Location: {$approvalUrl}"); exit; } public function completePayment($paymentId, $payerId) { $payment = Payment::get($paymentId, $this->payPal); $execute = new PaymentExecution(); $execute->setPayerId($payerId); try { $result = $payment->execute($execute, $this->payPal); } catch (PayPalConnectionException $exception) { $data = json_decode($exception->getData()); $_SESSION['message'] = 'Error, '. $data->message; // implement your own logic here to show errors from paypal exit; } if ($result->state === 'approved') { $transactions = $result->getTransactions(); $transaction = $transactions[0]; $invoiceId = $transaction->invoice_number; $relatedResources = $transactions[0]->getRelatedResources(); $sale = $relatedResources[0]->getSale(); $saleId = $sale->getId(); $transactionData = ['salesId' => $saleId, 'invoiceId' => $invoiceId]; return $transactionData; } else { echo "<h3>".$result->state."</h3>"; var_dump($result); exit(1); } } }
What’s Next
In the next post, we will back to the admin section and add a orders section from where we will be able to manage all incoming orders.
Code Repository
You can find the code base of this series on Laravel eCommerce Application repository.
If you have any question about this post, please leave a comment in the comment box below.