Integration PayPal Payment Gateway in PHP

Integration PayPal Payment Gateway in PHP is one of the most important things to do in todays world to accept payment from you client or user’s.

So, Today we are going to learn how to integrate PayPal payment gateway in PHP.

PayPal Holdings, Inc. is an American multinational financial technology company operating an online payments system in the majority of countries that support online money transfers, and serves as an electronic alternative to traditional paper methods such as checks and money orders.

Also Read : Drag & Drop Reorder Images in PHP (2022)

Steps to Integration PayPal Payment Gateway in PHP

  • Step 1: File’s Structure
  • Step 2: PayPal Sandbox Account and Settings
  • Step 3: Create Database and Table’s
  • Step 4: PayPal Settings and Database Configuration (config.php & dbConnect.php)
  • Step 5: Product Page (index.php)
  • Step 6: Payment Success Page (success.php)
  • Step 7: Payment Cancellation (cancel.php)
  • Step 8: Instant Payment Notification Page (ipn.php)
  • Step 9: Adding Stylesheet (style.css)
  • Step 10: Testing Application
  • Step 11: Conclusion

File’s Structure:

Before starting the tutorial let know the file structure of the Integration PayPal Payment Gateway in PHP application.

File Structure - Integration PayPal Payment Gateway in PHP
File8217s Structure

PayPal Sandbox Account and Settings:

We need a merchant and buyer PayPal account for testing the application. So, Next step is to create PayPal Sandbox Account.

Go to the PayPal Developer page and log in to your PayPal account. If you don’t have any PayPal account, register for a PayPal account first.

Click Accounts and Create both Business and Personal Account as shown below:

Creating merchant and buyer PayPal account - PayPal Sandbox Account and Settings
Creating merchant and buyer PayPal account 8211 PayPal Sandbox Account and Settings

Now, We have to make some settings in business account:

  • Configure PayPal Auto Return and Payment Data Transfer

Login into you Business Account. Go to Account Settings -> Website payments -> Website preferences -> Update

Configure PayPal Auto Return and Payment Data Transfer
Configure PayPal Auto Return and Payment Data Transfer

Update the URL as shown below:

Configure PayPal Auto Return and Payment Data Transfer
Configure PayPal Auto Return and Payment Data Transfer
  • Setup PayPal Instant Payment Notification (IPN)

Now, We are update Instant Payment Notification (IPN) Settings. Go to Account Settings -> Website payments -> Instant payment notifications -> Update:

Setup PayPal Instant Payment Notification (IPN)
Setup PayPal Instant Payment Notification (IPN)

Enable the IPN and Enter the URL as Shown below:

Setup PayPal Instant Payment Notification (IPN)
Setup PayPal Instant Payment Notification (IPN)

So Now, Everything is done with Settings up PayPal Account.

Also Read : Create a custom WordPress Plugin from scratch (2022)

Create Database and Table’s:

Now its time to create our database and its table’s.

For Creating a database open phpmyadmin and create a database:

Create Database
Create Database

Now, We are going to create two table’s payments and products. To Create this table’s run the following query in SQL.

payments table:

CREATE TABLE `payments` (
 `payment_id` int(11) NOT NULL AUTO_INCREMENT,
 `item_number` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `txn_id` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `payment_gross` float(10,2) NOT NULL,
 `currency_code` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
 `payment_status` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
 PRIMARY KEY (`payment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

products table:

CREATE TABLE `products` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
 `image` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `price` float(10,2) NOT NULL,
 `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1=Active | 0=Inactive',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

This will create a both table.

Database Table's
Database Table8217s

Now, Insert some products data to the products table for testing.

Products table
Products table

Note: Add the images to the images directory as shown below.

Products Images
Products Images

PayPal Settings and Database Configuration (config.php & dbConnect.php):

In the config.php file, constant variables of the PayPal and database settings are defined.

PayPal Constants:

  • PAYPAL_ID – Specify the email of the PayPal Business account.
  • PAYPAL_SANDBOX – Specify whether you use Sandbox environment (TRUE/FALSE).
  • PAYPAL_RETURN_URL – Specify the URL where the buyer will be redirected after payment.
  • PAYPAL_CANCEL_URL – Specify the URL where the buyer will be redirected after payment cancellation.
  • PAYPAL_NOTIFY_URL – Specify the URL where the transaction data will be sent for verification through PayPal IPN.
  • PAYPAL_CURRENCY – Specify the currency code.

Database Constants:

  • DB_HOST – Specify the database host.
  • DB_USERNAME – Specify the database username.
  • DB_PASSWORD – Specify the database password.
  • DB_NAME – Specify the database name.

Add the following code to config.php :

<?php

/* 
PayPal Setting and Database configuration
*/

//Paypal Settings and Configuration
define('PAYPAL_ID','merchant@laraveltuts.com');
define('PAYPAL_SANDBOX', TRUE); //TRUE OR FALSE

define('PAYPAL_RETURN_URL','https://laraveltuts.com/paypal/success.php');
define('PAYPAL_CANCEL_URL','https://laraveltuts.com/paypal/cancel.php');
define('PAYPAL_NOTIFY_URL','https://laraveltuts.com/paypal/ipn.php');
define('PAYPAL_CURRENCY','USD');

//Database Configuration
define('DB_HOST','localhost');
define('DB_USERNAME','root');
define('DB_PASSWORD','');
define('DB_NAME','paypal_integration');

//Change Not Required
define('PAYPAL_URL', (PAYPAL_SANDBOX == true) ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr");

Change the Value as per your PayPal and Database Requirement.

Database Connection (dbConnect.php):

The dbConnect.php file is used to connect the database using PHP and MySQL.

Add the following code to dbConnect.php :

<?php

//Connecting to Database
$db = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);

//Display error if failed to connect to database
if($db->connect_error){
    printf("Connect failed: %s\n", $db->connect_error);
    exit();
}

Also Read : Create CRUD Application with Laravel and Vue.js

Product Page (index.php):

Now, We are going to create the Product page where its will list all the products with PayPal button for buying.

Add the following code to index.php :

<?php
//Include Configuration file
include_once 'config.php';

//Include Database Connection file
include_once 'dbConnect.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Integration PayPal Payment Gateway in PHP - LaravelTuts</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="container">
        <?php
            //Fetch products from the database
            $results = $db->query("SELECT * FROM products WHERE status = 1");
            
            while($row = $results->fetch_assoc()){
            ?>
                <div class="card">
                    <img src="images/<?php echo $row['image']; ?>" />
                    <div class="body">
                        <h5><?php echo $row['name'] ?></h5>
                        <h6>Price: <?php echo '$'.$row['price'].' '.PAYPAL_CURRENCY; ?></h6>

                        <!-- Paypal payment form for displaying the buy button -->
                        <form action="<?php echo PAYPAL_URL; ?>" method="POST">
                            <!-- Identify your bussiness so that you can collect the payment -->
                            <input type="hidden" name="business" value="<?php echo PAYPAL_ID; ?>">

                            <!-- Specify a buy now button -->
                            <input type="hidden" name="cmd" value="_xclick">

                            <!-- Specify details about the item that buyers will purchase -->
                            <input type="hidden" name="item_name" value="<?php echo $row['name']; ?>">
                            <input type="hidden" name="item_number" value="<?php echo $row['id']; ?>">
                            <input type="hidden" name="amount" value="<?php echo $row['price']; ?>">
                            <input type="hidden" name="currency_code" value="<?php echo PAYPAL_CURRENCY; ?>">

                            <!-- Specify URLs -->
                            <input type="hidden" name="return" value="<?php echo PAYPAL_RETURN_URL; ?>">
                            <input type="hidden" name="cancel_return" value="<?php echo PAYPAL_CANCEL_URL; ?>">

                            <!-- Display the payment button -->
                            <input type="image" name="submit" style="border:0;" src="https://paypalobjects.com/en_US/i/btn/btn_buynow_LG.gif">
                        </form>
                    </div>
                </div>
            <?php
            }
        ?>
    </div>
</body>
</html>

Payment Success Page (success.php):

After successful payment on PayPal, the buyer is redirected to this page with all the payment details:

Add the following code to success.php :

<?php
//Include Configuration File
include_once 'config.php';

//Inlcude Database Connection File
include_once 'dbConnect.php';

//If Transaction Data is Available in the URL
if(!empty($_GET['item_number']) && !empty($_GET['tx']) && !empty($_GET['amt']) && !empty($_GET['cc']) && !empty($_GET['st'])){
    //Get Transaction Information from URL
    $item_number = $_GET['item_number'];
    $txn_id = $_GET['tx'];
    $payment_gross = $_GET['amt'];
    $currency_code = $_GET['cc'];
    $payment_status = $_GET['st'];

    //Get Product infomation from the database
    $productResult = $db->query("SELECT * FROM products WHERE id = ".$item_number);
    $productRow = $productResult->fetch_assoc();

    //Check if transaction data exists with the same TXN ID
    $prevPaymentResult = $db->query("SELECT * FROM payments WHERE txn_id = '".$txn_id."'");

    if($prevPaymentResult->num_rows > 0){
        $paymentRow = $prevPaymentResult->fetch_assoc();
        $payment_id = $paymentRow['payment_id'];
        $payment_gross = $paymentRow['payment_gross'];
        $payment_status = $paymentRow['payment_status'];
    }else{
        //Insert transaction data into the database
        $insert = $db->query("INSERT INTO payments(item_number,txn_id,payment_gross,currency_code,payment_status) VALUES('".$item_number."','".$txn_id."','".$payment_gross."','".$currency_code."','".$payment_status."')");
        $payment_id = $db->insert_id;
    }  
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PayPal Payment Status</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="container">
        <div class="main">
            <div class="status">
                <?php if(!empty($payment_id)){ ?>
                    <h1 class="success">Your Payment has been successful</h1>

                    <h4>Payment Information</h4>
                    <p><b>Reference Number:</b> <?php echo $payment_id; ?></p>
                    <p><b>Transaction ID:</b> <?php echo $txn_id; ?></p>
                    <p><b>Paid Amount:</b> <?php echo $payment_gross; ?></p>
                    <p><b>Payment Status:</b> <?php echo $payment_status; ?></p>

                    <h4>Product Information</h4>
                    <p><b>Name:</b> <?php echo $productRow['name']; ?></p>
                    <p><b>Price:</b> <?php echo $productRow['price']; ?></p>
                <?php }else{ ?>
                    <h1 class="error">Your Payment has failed</h1>
                <?php } ?>
            </div>
            <a href="index.php" class="btn-link">Back to Products</a>
        </div>
    </div>
</body>
</html>

Payment Cancellation (cancel.php):

If the buyer cancel the PayPal payment, the buyer is redirected to this page.

Add the following code to cancel.php :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PayPal Payment Status</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="container">
        <div class="main">
            <div class="status">
                <h1 class="error">Your PayPal Transaction has been canceled!</h1>
            </div>
            <a href="index.php" class="btn-link">Back to Products</a>
        </div>
    </div>
</body>
</html>

Instant Payment Notification Page (ipn.php):

Once IPN is enabled, PayPal will send the transaction data to the Notify URL (http://www.example.com/ipn.php). Place the following code in the ipn.php file to validate the transaction and insert payment information into the database.

Also Read : How to send mail using Gmail in Laravel 9?

Add the following code to ipn.php :

<?php

//Include Configuration File
include_once 'config.php';

//Include Database Connection File
include_once 'dbConnect.php';

/*
Read Post Data
Reading posted data directly from $_POST causes serialization
Issues with array data in POST
Reading raw POST data from input stream intend.
*/

$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();

foreach($raw_post_array as $keyval){
    $keyval = explode('=', $keyval);
    if(count($keyval) == 2)
        $myPost[$keyval[0]] = urldecode($keyval[1]);
}

//Read the post from paypal system and add 'cmd'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')){
    $get_magic_quotes_exists = true;
}

foreach($myPost as $key => $value){
    if($get_magic_quotes_exists == true){
        $value = urlencode(stripslashes($value));
    }else{
        $value = urlencode($value);
    }
    $req .= "&$key=$value";
}

/*
Post IPN data back to paypal to validate the IPN data is genuine
Without this step anyone can fake IPN data
*/

$paypalURL = PAYPAL_URL;
$ch = curl_init($paypalURL);
if($ch == FALSE){
    return FALSE;
}

curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);

//Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: company-name'));
$res = curl_exec($ch);

/*
Inspect IPN validation result and act accordingly 
Split response headers and payload, a better way for strcmp
*/

$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));

if(strcmp($res, "VERIFIED") == 0 || strcasecmp($res, "VERIFIED") == 0){

    //Retrive transaction infro from paypal
    $item_number = $_POST['item_number'];
    $txn_id = $_POST['txn_id'];
    $payment_gross = $_POST['mc_gross'];
    $currency_code = $_POST['mc_currency'];
    $payment_status = $_POST['payment_status'];

    //Check if transation data exists with the same TXN ID
    $prevPayment = $db->query("SELECT payment_id FROM payments WHERE txn_id= '".$txn_id."'");

    if($prevPayment->num_rows > 0){
        exit();
    }else{
        //Insert transaction data into the database
        $insert = $db->query("INSERT INTO payments(item_number,txn_id,payment_gross,currency_code,payment_status) VALUES('".$item_number."','".$txn_id."','".$payment_gross."','".$currency_code."','".$payment_status."')");
    }

}

Note: Once the PayPal IPN setup is completed, the database insertion code is not required in the success.php file.

Adding Stylesheet (style.css):

Add the following css to style.css for final touch to our application.

body{
    font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}
.container{
    width: 1280px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    justify-content: center;
}
.container .card{
    flex: 0 0 200px; margin: 10px;
    border: 1px solid #ccc;
    box-shadow: 3px 3px 6px rgba(0,0,0,0.3);
    text-align: center;
}
.container img{
    width: 100%;
    height: 131px;
    background-size: cover;
    object-fit: cover;
}
.container .body{
    padding-bottom: 10px;
}
.container h5{
    font-size: 20px;
    font-weight: bold;
    margin: 10px 0;
    color: #566270;
}
.container h6{
    font-size: 14px;
    font-weight: bold;
    margin: 10px 0;
    color: #566270;
}
.status{
    background: #f8f8f8;
    border: 1px solid #ccc;
    box-shadow: 3px 3px 6px rgba(0,0,0,0.3);
    padding: 20px;
    margin: 50px 0;
}
.status .success{
    text-align: center;
    font-size: 30px;
    font-weight: bold;
}
.status .error{
    text-align: center;
    font-size: 30px;
    font-weight: bold;
    color: red;
}
.container .main{
    width: 100%;
}
.status h4{
    font-size: 18px;
    font-weight: bold;
    margin: 10px 0;
    color: #0077ff;
}
.main p{
    font-size: 16px;
    color: #4b4b4b;
}
.btn-link{
    padding: 10px 15px;
    color: #0077ff;
    border: 1px solid #0077ff;
    box-shadow: 3px 3px 6px rgba(0,0,0,0.3);
    text-decoration: none;
}

Testing Application:

We had upload the PHP Application to our hosting with database. PayPal didn’t support localhost for IPN.

When the application payment flow testing is completed, you should make the PayPal payment gateway live.

In the config.php file,

  • Specify the email of the PayPal business account in PAYPAL_ID.
  • Set PAYPAL_SANDBOX to FALSE.
define('PAYPAL_ID', 'merchant@laraveltuts.com'); //Real ID
define('PAYPAL_SANDBOX', FALSE);

Result:

Conclusion:

We had learned how to Integrate PayPal Payment Gateway in PHP. Please note that we had hosted the PHP Application online for testing because PayPal didn’t support localhost url for Instant Payment Notification. Hope this tutorial helped you. If you have any problem with the tutorial contact us by commenting below.

Also Read : Install ReactJS in Laravel 9 Tutorial

5 Comments

Leave a Reply