Accepting Payments for Multiple Items with PayPal in PHP

PayPal is the most popular online payment system that helps to easily integrate payment gateway on the website. PayPal payments standard is the most simple way to implement the payment system to your website and accept payment from the buyers online. With the PayPal standard payment gateway, a buy now button is provided that allows the buyer to purchase a single item at a time. If you want to allow the buyer to purchase multiple items at a time, PayPal Add to Cart button is the easiest option to accept payment for one or more items with a single checkout flow. In this tutorial, we’ll show how to integrate PayPal standard payment gateway in PHP for sending multiple items to PayPal checkout.

In the example script, we will implement the following functionality to demonstrate the PayPal Cart Payment Gateway integration in PHP.

  • Fetch the products from the database and listed on the web page with the PayPal Add to Cart button.
  • The Add to Cart button adds the selected items to the PayPal cart.
  • On checkout, the buyer will redirect to the PayPal site to complete the payment.
  • After the payment, the buyer will redirect back to the website, and the order and payment details will be shown.
  • The transaction data is stored in the database through the PayPal IPN.

Before getting started to integrate PayPal shopping cart payment gateway in PHP, take a look at the file structure.

paypal_shopping_cart_with_php/
├── config.php
├── dbConnect.php
├── index.php
├── success.php
├── cancel.php
├── paypal_ipn.php
├── css/
│   └── style.css
└── images/

Create PayPal Sandbox Account

Before making the payment gateway live, the payment process needs to be tested. PayPal sandbox environment allows you to test PayPal payment gateway on your website without real payment. If you don’t have a PayPal sandbox account, create a sandbox account first.

Create Database Tables

To store product, order, and payment information 4 tables are required in the database.

The following SQL creates a products table in the MySQL database to store the product information.

CREATE TABLE `products` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) 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,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

The following SQL creates a orders table in the MySQL database to store the order information.

CREATE TABLE `orders` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `total_qty` int(11) NOT NULL,
 `total_amount` float(10,2) NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

The following SQL creates a order_items table in the MySQL database to store the information about order items.

CREATE TABLE `order_items` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `order_id` int(11) NOT NULL,
 `product_id` int(11) NOT NULL,
 `quantity` int(5) NOT NULL,
 `gross_amount` float(10,2) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `order_id` (`order_id`),
 KEY `product_id` (`product_id`),
 CONSTRAINT `order_items_ibfk_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
 CONSTRAINT `order_items_ibfk_2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

The following SQL creates a payments table in the MySQL database to store the payment information.

CREATE TABLE `payments` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `order_id` int(11) NOT NULL,
 `payer_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `payer_email` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `txn_id` varchar(255) 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(25) COLLATE utf8_unicode_ci NOT NULL,
 PRIMARY KEY (`id`),
 KEY `order_id` (`order_id`),
 CONSTRAINT `payments_ibfk_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

PayPal Settings and Database Configuration (config.php)

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

PayPal Constants:

  • PAYPAL_ID – Specify the email of the PayPal Business account.
  • PAYPAL_SANDBOX – Specify whether you using the Sandbox environment (TRUE/FALSE).
  • CONTINUE_SHOPPING_URL – Specify the URL where the products are listed.
  • 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.
<?php  
// PayPal configuration 
define('PAYPAL_ID''Insert_PayPal_Business_Email');
define('PAYPAL_SANDBOX'TRUE); //TRUE or FALSE 

define('CONTINUE_SHOPPING_URL''http://www.example.com/index.php'); 
define('PAYPAL_RETURN_URL''http://www.example.com/success.php'); 
define('PAYPAL_CANCEL_URL''http://www.example.com/cancel.php'); 
define('PAYPAL_NOTIFY_URL''http://www.example.com/paypal_ipn.php'); 
define('PAYPAL_CURRENCY''USD'); 
 
// Database configuration 
define('DB_HOST''MySQL_Database_Host'); 
define('DB_USERNAME''MySQL_Database_Username'); 
define('DB_PASSWORD''MySQL_Database_Password'); 
define('DB_NAME''MySQL_Database_Name'); 
 
// Change not required 
define('PAYPAL_URL', (PAYPAL_SANDBOX == true)?"https://www.sandbox.paypal.com/cgi-bin/webscr":"https://www.paypal.com/cgi-bin/webscr");

Database Connection (dbConnect.php)

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

<?php  
// Connect with the database 
$db = new mysqli(DB_HOSTDB_USERNAMEDB_PASSWORDDB_NAME); 
 
// Display error if failed to connect 
if ($db->connect_errno) { 
    
printf("Connect failed: %s\n"$db->connect_error); 
    exit(); 
}

Products (index.php)

Initially, all the products are fetched from the database and listed on the webpage.

  • A PayPal Add to Cart button is attached with each product.
  • To use PayPal AddToCart payment gateway, you need to submit a form with some predefined PayPal HTML form field variables.
  • Follow the comment tags () to know about the form hidden fields.
<?php 
// Include configuration file 
include_once 'config.php'
 
// Include database connection file 
include_once 'dbConnect.php';
?> <div class="container">     <?php
        
// Fetch products from the database
        
$results $db->query("SELECT * FROM products");
        while(
$row $results->fetch_assoc()){
    
?> <div class="pro-box"> <img src="images/<?php echo $row['image']; ?>"/> <p>Name: <?php echo $row['name']; ?></p> <p>Price: $<?php echo $row['price']; ?></p> <form target="_self" action="<?php echo PAYPAL_URL?>" method="post"> <!-- Identify your business so that you can collect the payments. --> <input type="hidden" name="business" value="<?php echo PAYPAL_ID?>"> <!-- Specify a PayPal Shopping Cart Add to Cart button. --> <input type="hidden" name="cmd" value="_cart"> <input type="hidden" name="add" value="1"> <!-- 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="shopping_url" value="<?php echo CONTINUE_SHOPPING_URL?>"> <input type="hidden" name="cancel_return" value="<?php echo PAYPAL_CANCEL_URL?>"> <input type="hidden" name="return" value="<?php echo PAYPAL_RETURN_URL?>"> <input type="hidden" name="notify_url" value="<?php echo PAYPAL_NOTIFY_URL?>"> <!-- Display the payment button. --> <input type="image" name="submit" src="https://www.paypalobjects.com/webstatic/en_US/i/btn/png/btn_addtocart_120x26.png" alt="Add to Cart"> <img alt="" width="1" height="1" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif"> </form> </div> <?php } ?> </div>

Payment Success (success.php)

After the payment is successful on PayPal, the buyer will be redirected to this page.

  • The transaction ID is retrieved from the URL using the PHP $_GET method.
  • Fetch the payment information from the database based on the transaction ID.
  • Display order and payment details on the web page
<?php 
// Include configuration file 
include_once 'config.php'
 
// Include database connection file 
include_once 'dbConnect.php'

$paymentData '';
if(!empty(
$_GET['tx']) && !empty($_GET['amt']) && $_GET['st'] == 'Completed'){
    
// Get transaction information from URL 
    
$txn_id $_GET['tx']; 
    
$payment_gross $_GET['amt']; 
    
$currency_code $_GET['cc']; 
    
$payment_status $_GET['st'];
    
    
// 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){
        
// Get subscription info from database
        
$paymentData $prevPaymentResult->fetch_assoc();
        
        
// Order details
        
$orderResult $db->query("SELECT * FROM orders WHERE id = ".$paymentData['order_id']);
        
$orderData $orderResult->fetch_assoc();
        
        
// Order items
        
$orderItemsResult $db->query("SELECT i.*,pr.name FROM payments as p LEFT JOIN order_items as i ON i.order_id=p.order_id LEFT JOIN products as pr ON pr.id = i.product_id WHERE p.id = ".$paymentData['id']);
    }
}
?> <div class="status"> <?php if(!empty($paymentData)){ ?> <h1 class="success">Your Payment has been Successful!</h1> <h4>Order Information</h4> <p><b>Order ID:</b> <?php echo $orderData['id']; ?></p> <p><b>Total Items:</b> <?php echo $orderData['total_qty']; ?></p> <p><b>Order Total:</b> <?php echo $orderData['total_amount']; ?></p> <h4>Payment Information</h4> <p><b>Reference Number:</b> <?php echo $paymentData['id']; ?></p> <p><b>Transaction ID:</b> <?php echo $paymentData['txn_id']; ?></p> <p><b>Paid Amount:</b> <?php echo $paymentData['payment_gross'].' '.$paymentData['currency_code']; ?></p> <p><b>Payment Status:</b> <?php echo $paymentData['payment_status']; ?></p> <h4>Order Items</h4> <?php if($orderItemsResult->num_rows 0){ ?> <table> <tr> <th>#</th> <th>Product Name</th> <th>Quantity</th> <th>Gross Amount</th> </tr> <?php $i=0; while($item $orderItemsResult->fetch_assoc()){ $i++; ?> <tr> <td align="center"><?php echo $i?></td> <td align="center"><?php echo $item['name']; ?></td> <td align="center"><?php echo $item['quantity']; ?></td> <td align="center"><?php echo '$'.$item['gross_amount']; ?></td> </tr> <?php ?> </table> <?php ?> <?php }else{ ?> <h1 class="error">Your payment was unsuccessful, please try again.</h1> <?php ?> </div>

Note that: The transaction data is validated and inserted into the database using the IPN script.

Payment Cancellation (cancel.php)

If the buyer wishes to cancel payment at the PayPal checkout page, they will be redirected to this page.

<div class="container">
    <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>

Validate Transaction and Store Payment Information

The PayPal IPN feature is used to validate the transaction and store the payment information in the database.

  • If IPN is enabled, PayPal will send the transaction data to the Notify URL (http://www.example.com/paypal_ipn.php).
  • The IPN script will validate the transaction and insert the order data in the database using PHP and MySQL.
<?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 instead.
 */        
$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 && get_magic_quotes_gpc() == 1) {
        
$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($chCURLOPT_HTTP_VERSIONCURL_HTTP_VERSION_1_1);
curl_setopt($chCURLOPT_POST1);
curl_setopt($chCURLOPT_RETURNTRANSFER,1);
curl_setopt($chCURLOPT_POSTFIELDS$req);
curl_setopt($chCURLOPT_SSLVERSION6);
curl_setopt($chCURLOPT_SSL_VERIFYPEER1);
curl_setopt($chCURLOPT_SSL_VERIFYHOST2);
curl_setopt($chCURLOPT_FORBID_REUSE1);

// Set TCP timeout to 30 seconds
curl_setopt($chCURLOPT_CONNECTTIMEOUT30);
curl_setopt($chCURLOPT_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") == || strcasecmp($res"VERIFIED") == 0) {
    
    
// Retrieve transaction data from PayPal
    
$paypalInfo $_POST;

    
$txn_id = !empty($paypalInfo['txn_id'])?$paypalInfo['txn_id']:'';
    
$payment_gross =  !empty($paypalInfo['mc_gross'])?$paypalInfo['mc_gross']:0;
    
$currency_code $paypalInfo['mc_currency'];
    
$payment_status = !empty($paypalInfo['payment_status'])?$paypalInfo['payment_status']:'';
    
$payerFirstName = !empty($_POST['first_name'])?$_POST['first_name']:'';
    
$payer_name = !empty($_POST['last_name'])?$payerFirstName.' '.$_POST['last_name']:$payerFirstName;
    
$payer_name filter_var($payer_name,FILTER_SANITIZE_STRING,FILTER_FLAG_STRIP_HIGH);
    
$payer_email $paypalInfo['payer_email'];
    
    
$num_cart_items $_POST['num_cart_items'];
    
    if(!empty(
$txn_id)){
        
// Check if transaction data exists with the same TXN ID
        
$prevPayment $db->query("SELECT id FROM payments WHERE txn_id = '".$txn_id."'");
        
        if(
$prevPayment->num_rows 0){
            exit();
        }else{
            
// Insert order data into the database
            
$insertOrder $db->query("INSERT INTO orders(total_qty,total_amount) VALUES($num_cart_items,'".$payment_gross."')");
            
            if(
$insertOrder){
                
$order_id $db->insert_id;
                
                
// Insert tansaction data into the database
                
$insertPayment $db->query("INSERT INTO payments(order_id,payer_name,payer_email,txn_id,payment_gross,currency_code,payment_status) VALUES($order_id,'".$payer_name."','".$payer_email."','".$txn_id."','".$payment_gross."','".$currency_code."','".$payment_status."')");
                
                if(
$insertPayment){
                    
$payment_id $db->insert_id;
                    
                    
// Insert order items into the database
                    
for($i=1;$i<=$num_cart_items;$i++){
                        
$order_item_number $_POST['item_number'.$i];
                        
$order_item_quantity $_POST['quantity'.$i];
                        
$order_item_gross_amount $_POST['mc_gross_'.$i];
                        
$insertOrderItem $db->query("INSERT INTO order_items(order_id,product_id,quantity,gross_amount) VALUES('".$order_id."','".$order_item_number."','".$order_item_quantity."','".$order_item_gross_amount."')");
                    }
                }
            }
        }
    }
}
die;

Configure PayPal Auto Return and Payment Data Transfer

Make sure you have configured Auto Return for Website Payments on your PayPal business account. Otherwise, you’ll not get the transaction information from PayPal in the success.php file. See the following guide to enable Auto Return, Payment Data Transfer, and set Return URL on your PayPal account.

Enable PayPal Instant Payment Notification (IPN)

The IPN option must be enabled in the PayPal account. See the step-by-step guide to enable IPN feature in PayPal.

Make PayPal Payment Gateway Live

When the application payment flow is working properly with the Sandbox account, 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.

PayPal Pro Payment Gateway Integration in PHP

Do you want to get implementation help, or enhance the functionality of this script? Click here to Submit Service Request

6 Comments

  1. Shashi Said...
  2. Aniruddha Banik Said...
  3. Fran Said...
  4. Krishna Said...
    • CodexWorld Said...

Leave a reply

keyboard_double_arrow_up