
This tutorial will help you to integrate PayPal payment gateway using PHP with an example. I will walk you through a step by step process and make it easy for you do the integration.
You will learn about three things today,
PayPal provides different options to integrate the gateway like,
In this example, we will use “PayPal Payments Standard” method for integrating the payment gateway.
If you wish to process card payments refer the Stripe payment gateway integration using PHP articles.
Below screenshot shows the file structure of this PHP example code created for integrating Payment Gateway using PayPal Payments Standard method.
The landing page shows an entry point for proceeding with the PayPal payment transaction process. It will show a sample product tile containing PayPal payment fields following by a Pay Now button.
The return.php and the cancel.php files are the acknowledgement pages to which the user will be redirected appropriately. The notify.php is the IPN listener connect database to save payment transaction response and data.
DBController.php file contains the code to create database connection object and to handle queries on payment database update.
In this HTML code we can see the PayPal Payments Standard form fields with a custom Pay Now button. This example code targets the PayPal sandbox URL on the form action.
The PayPal payment form must contain the business email address, order information like item_number, name, amount, currency, and the callback and return page URLs.
By submitting this form, the payment parameters will be posted to the PayPal server. So, the user will be redirected to the PayPal Login and asked for payment confirmation.
<body> <div id="payment-box"> <img src="images/camera.jpg" /> <h4 class="txt-title">A6900 MirrorLess Camera</h4> <div class="txt-price">$289.61</div> <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_top"> <input type='hidden' name='business' value='PayPal Business Email'> <input type='hidden' name='item_name' value='Camera'> <input type='hidden' name='item_number' value='CAM#N1'> <input type='hidden' name='amount' value='10'> <input type='hidden' name='no_shipping' value='1'> <input type='hidden' name='currency_code' value='USD'> <input type='hidden' name='notify_url' value='http://sitename/paypal-payment-gateway-integration-in-php/notify.php'> <input type='hidden' name='cancel_return' value='http://sitename/paypal-payment-gateway-integration-in-php/cancel.php'> <input type='hidden' name='return' value='http://sitename/paypal-payment-gateway-integration-in-php/return.php'> <input type="hidden" name="cmd" value="_xclick"> <input type="submit" name="pay_now" id="pay_now" Value="Pay Now"> </form> </div> </body>
PayPal provides notifications using different mechanisms. It will be useful for updating your backend processes, sending order notification and similar transactional jobs. Following are the different types of notifications provided by PayPal.
Notifications should be used as part of the payment gateway integration process. Imagine you are running a website that sells digital goods online. At the end of the payment process, you are obliged to send the digital product via email.
You should not do this in the thank-you page. As there is no guarantee that this page will be displayed in the user’s browser. There can be network failures, user’s might close the browser and there are so many variables involved in this.
The dependable way of doing this is using PayPal notifications. You get a callback from PayPal to your backend server (asynchronously) and you can manage the backend process from there.
PayPal notifies the merchants about the events related to their transactions. This automatic callback can be used to perform administrative tasks and to fulfil orders.
Notifications are done by PayPal on different type of events like, payments received, authorizations made, recurring payments, subscriptions, chargebacks, disputes, reversals and refunds.
An integral part of the payment gateway integration process in the ability to receive PayPal notification and process backend administrative processes.
The order fulfillment is an important step in a shopping cart software. It should be done via the PayPal notification and never should be done as part of the thank you or order completion page.
The notify.php is created as the instant payment notification listener. This listener URL will be sent to PayPal via the form seen above. It can also be set in the PayPal REST API app settings. Paypal provides the IPN listener code samples for various languages.
This listener will be called asynchronously by PayPal to notify payment processing response. The payment response will be posted to this URL.
Below code verifies the payment status from the response returned by PayPal. If the payment is verified and completed then the result will be updated in a database table.
<?php // CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory. // Especially useful if you encounter network errors or other intermittent problems with IPN (validation). // Set this to 0 once you go live or don't require logging. define("DEBUG", 1); // Set to 0 once you're ready to go live define("USE_SANDBOX", 1); define("LOG_FILE", "ipn.log"); // 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 if(USE_SANDBOX == true) { $paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"; } else { $paypal_url = "https://www.paypal.com/cgi-bin/webscr"; } $ch = curl_init($paypal_url); 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_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); if(DEBUG == true) { curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLINFO_HEADER_OUT, 1); } // CONFIG: Optional proxy configuration //curl_setopt($ch, CURLOPT_PROXY, $proxy); //curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); // Set TCP timeout to 30 seconds curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close')); // CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path // of the certificate as shown below. Ensure the file is readable by the webserver. // This is mandatory for some environments. //$cert = __DIR__ . "./cacert.pem"; //curl_setopt($ch, CURLOPT_CAINFO, $cert); $res = curl_exec($ch); if (curl_errno($ch) != 0) // cURL error { if(DEBUG == true) { error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE); } curl_close($ch); exit; } else { // Log the entire HTTP response if debug is switched on. if(DEBUG == true) { error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE); error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE); } curl_close($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) { // assign posted variables to local variables $item_name = $_POST['item_name']; $item_number = $_POST['item_number']; $payment_status = $_POST['payment_status']; $payment_amount = $_POST['mc_gross']; $payment_currency = $_POST['mc_currency']; $txn_id = $_POST['txn_id']; $receiver_email = $_POST['receiver_email']; $payer_email = $_POST['payer_email']; include("DBController.php"); $db = new DBController(); // check whether the payment_status is Completed $isPaymentCompleted = false; if($payment_status == "Completed") { $isPaymentCompleted = true; } // check that txn_id has not been previously processed $isUniqueTxnId = false; $param_type="s"; $param_value_array = array($txn_id); $result = $db->runQuery("SELECT * FROM payment WHERE txn_id = ?",$param_type,$param_value_array); if(empty($result)) { $isUniqueTxnId = true; } // check that receiver_email is your PayPal email // check that payment_amount/payment_currency are correct if($isPaymentCompleted) { $param_type = "sssdss"; $param_value_array = array($item_number, $item_name, $payment_status, $payment_amount, $payment_currency, $txn_id); $payment_id = $db->insert("INSERT INTO payment(item_number, item_name, payment_status, payment_amount, payment_currency, txn_id) VALUES(?, ?, ?, ?, ?, ?)", $param_type, $param_value_array); } // process payment and mark item as paid. if(DEBUG == true) { error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE); } } else if (strcmp ($res, "INVALID") == 0) { // log for manual investigation // Add business logic here which deals with invalid IPN messages if(DEBUG == true) { error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE); } } ?>
This file handles database related functions using prepared statement with MySQLi. This is important, always remember to use Prepared Statements as it will safeguard you from SQL injections and a primary step towards your security of the application.
<?php class DBController { private $host = ""; private $user = ""; private $password = ""; private $database = ""; private $conn; function __construct() { $this->conn = $this->connectDB(); } function connectDB() { $conn = mysqli_connect($this->host, $this->user, $this->password, $this->database); return $conn; } function runQuery($query, $param_type, $param_value_array) { $sql = $this->conn->prepare($query); $this->bindQueryParams($sql, $param_type, $param_value_array); $sql->execute(); $result = $sql->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } function bindQueryParams($sql, $param_type, $param_value_array) { $param_value_reference[] = & $param_type; for ($i = 0; $i < count($param_value_array); $i ++) { $param_value_reference[] = & $param_value_array[$i]; } call_user_func_array(array( $sql, 'bind_param' ), $param_value_reference); } function insert($query, $param_type, $param_value_array) { $sql = $this->conn->prepare($query); $this->bindQueryParams($sql, $param_type, $param_value_array); $sql->execute(); } } ?>
This section shows the payment database table structure in .sql format. This is provided with the downloadable source code with sql/payment.sql file. Import this file in your test environment to run this example.
-- -- Table structure for table `payment` -- CREATE TABLE IF NOT EXISTS `payment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `item_number` varchar(255) NOT NULL, `item_name` varchar(255) NOT NULL, `payment_status` varchar(255) NOT NULL, `payment_amount` double(10,2) NOT NULL, `payment_currency` varchar(255) NOT NULL, `txn_id` varchar(255) NOT NULL, `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) );
Test! Test! Test! It is important. We are processing payments and you cannot be casual about this implementation. Every bit of your PHP script should be tested thoroughly before going live.
PayPal provides excellent toolset to perform the end to end testing of the payment gateway integration. It provides a sandbox environment using which you can simulate the complete payment process including the payments and fulfillments step.
Testing the IPN is critical as it takes care of the order fulfilment process. Go through the below steps to setup a PayPal Sandbox account and test you PHP code for payment gateway integration.
Once everything is working good with the entire payment transaction processing flow, then it will be a time to go live with your PayPal payment gateway integration using PHP code. Change the sandbox mode to live mode and do the following.
It requires to do the following changes in the code.
Replace the sandbox business account email with live email address.
<input type='hidden' name='business' value='PayPal Business Email'>
Change the PayPal Payments Standard form action URL as shown below.
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
In notify.php, set the USE_SANDBOX constant to 0.
define("USE_SANDBOX", 1);
Below screenshot shows the product tile which includes PayPal payments standard form and custom Pay Now button.
After successful payment transaction, the user will be automatically redirected to the return URL as specified in the payment form. The return page will acknowledge the user as shown below.
The return url is nothing but a thank you page or order completion page. This is the last step in the payment gateway integration process.
An important thing to know is that the order fulfilment steps should not be done sequentially in the return URL call. It should be done via the IPN callback or the other PayPal notifications.
This code is complicated and I don’t understand it! but it works. Thanks
Erik,
Welcome. Let me know which part is difficult for you to understand and I will try my best to help you.
hello this demo working properly, thank you so much
Welcome, Desai.
The best article of Paypal gateway I’ve ever read, great job Vincy ;)
Hey, thank you Leonardo. These nice words keep me motivated. Thank you.
Thanks but notify.php is not working
The callback may not happen due to many issues. Right from basic Internet failure to sending wrong parameters. You need to debug. If you can post some stack trace, I can help you out.
Payment transactions are working, but data is not saving in DB. Could you please help me in this
Khaja, you need to check DB connection credentials and watchout for errors.
Nice tutorial..but can you write more articles on other payment gateways?
Thank you, John. So far, I have written on PayPal, Stripe, Authorize.net, Sage Pay and CCAvenue. Let me know which payment gateway you are looking for and I will write on it. Thanks.
can you make a payment gateway on jazzcash and easypessa.
Sure Muhammad, I will try to write on it soon.
Hi Erik,
Thank you so much for sharing this code, I have tried this and it is working very fine but there is a doubt in my mind about that is it secured way to integrate this with using email only, I mean what about those API Token I have received from paypal account. I didn’t find any configuration for them in your code.
Thanks
Hi Chandan,
Yes, of course, it is a secure way of integrating with PayPal. Absolutely no issues with it.
You need that token when you make REST API calls to PayPal. Here in this scenario, we do not require it and so it is not needed.
Are you have any code base for paypal payout feature in PHP?
If yes please share your experience.
Hi Mohtashim, I will soon write an article for that.
Very good article, accurate and comprehensive.
Exactly what I was looking for the way I need it!
Thanks!
Welcome, Szoupi. Keep reading and sharing.
Can you write articles on transfer money payout from stripe to stripe account?
if you have any idea about that, please share link?
Sure, I will write about it Vijendra.
Thanks !
Welcome Vijay.
Nice article
Thank you Victor.
Hi Vincy, I am very fond of Your work, keep up the good work You do.
I am from Africa/Rwanda, I really tried Stripe IPN and 2checkout, unfortunately my country is not eligible for certain payment gateways. Which payment processor would You recommended for me in the fact that I can develop an E – Commerce web without restrictions. Thank You
Hi,
Thank you for nice words. Actually it is not a recommendation as I have not used these payment providers. But following is a list of providers in Rwanda, Paysera payment gateway, Pesapal, BitPay, CoinPayments.
Thanks very much Vincy.
Where are (cancel.php & return.php) files?
Hi Abdo,
cancel.php – if the user cancels the payment in PayPal site, then this page will be shown.
return.php – On payment, this page will be shown.