PayPal Subscriptions Payment Gateway Integration in PHP


PayPal Subscriptions payments let you accept payment for your member subscription service. If you have a membership website and want to add subscription feature, PayPal subscription API helps to do it easily. PayPal Subscription button is a quick and effective way to allow your website’s members to purchase the membership.

In this tutorial, we’ll show how you can integrate PayPal subscription payment gateway in PHP. PayPal subscription integration is same like the PayPal standard payment gateway integration but has some different variables. In the example code, we will create a subscribe button for registered members to make payment for subscription. Also, the subscription and payment details will be stored in the database for tracking subscription validity of the members.

Before you get started to integrate PayPal subscription payment gateway in PHP, create a PayPal Sandbox account. PayPal Sandbox allows the developer to do test transaction before making the payment gateway live. If you are new in PayPal integration, see this guide to create a PayPal Sandbox account – How to Create PayPal Sandbox Test Account

Database Table Creation

At least two tables are needed in the MySQL database to store the users and subscriptions details.

The users table holds the member’s information of the website. The following SQL creates a users table with some basic required fields.

CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `subscription_id` int(11) NOT NULL DEFAULT '0',
 `first_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `last_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `gender` enum('Male','Female') COLLATE utf8_unicode_ci NOT NULL,
 `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL,
 `created` datetime NOT NULL,
 `modified` datetime NOT NULL,
 `status` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

The user_subscriptions table is used to store the subscription validity and payment information of the members.

CREATE TABLE `user_subscriptions` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `user_id` int(11) NOT NULL DEFAULT '0',
 `payment_method` enum('paypal') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'paypal',
 `validity` int(5) NOT NULL COMMENT 'in month(s)',
 `valid_from` datetime NOT NULL,
 `valid_to` datetime NOT NULL,
 `item_number` varchar(255) 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(10) COLLATE utf8_unicode_ci NOT NULL,
 `subscr_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `payer_email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `payment_status` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Database Configuration (dbConfig.php)

The dbConfig.php file is used to connect with the MySQL database. Specify database host ($dbHost), username ($dbUsername), password ($dbPassword), and name ($dbName) as per the MySQL credentials.

<?php
//Database credentials
$dbHost 'localhost';
$dbUsername 'root';
$dbPassword '*****';
$dbName 'codexworld';

//Connect with the database
$db = new mysqli($dbHost$dbUsername$dbPassword$dbName);

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

Member Subscription (index.php)

At first, the logged in user ID is fetched from PHP SESSION and some useful PayPal variables are defined to use in PayPal form.

  • $paypalURL – Specify PayPal URL to redirect user for payment.
  • $paypalID – Specify the PayPal business email.
  • $successURL – Specify the URL where the user will be redirected after payment.
  • $cancelURL – Specify the URL where the user will be redirected if wish to cancel the payment.
  • $notifyURL – Specify the IPN URL which will be used to validate the transaction and insert the subscription data into the database.
<?php
//start session
session_start();

//get logged in user ID from sesion
$loggedInUserID $sessData['userID'];

//PayPal variables
$paypalURL     'https://www.sandbox.paypal.com/cgi-bin/webscr';
$paypalID     'Insert_PayPal_Business_Email';
$successURL 'http://www.codexworld.com/success.php';
$cancelURL     'http://www.codexworld.com/index.php';
$notifyURL     'http://www.codexworld.com/paypal_ipn.php';

$itemName 'Member Subscriptions';
$itemNumber 'MS'.$loggedInUserID;

//subscription price for one month
$itemPrice 25.00;
?>

In HTML section a dropdown will be displayed to select the validity and the respective price will be shown under the validity dropdown. The user needs to click on the Subscription button to buy their selected subscription. Follow the comment tags (<!– –>) to know more about the PayPal HTML form field variable.

<p>Choose Validity:
    <select name="validity" onchange="getSubsPrice(this);">
        <option value="1" selected="selected">1 Month</option>
        <option value="3">3 Month</option>
        <option value="6">6 Month</option>
        <option value="9">9 Month</option>
        <option value="12">12 Month</option>
    </select>
</p>
<p>Price: <span id="subPrice"><?php echo '$'.$itemPrice.' USD'?></span></p>
<form action="<?php echo $paypalURL?>" method="post">
    <!-- identify your business so that you can collect the payments -->
    <input type="hidden" name="business" value="<?php echo $paypalID?>">
    <!-- specify a subscriptions button. -->
    <input type="hidden" name="cmd" value="_xclick-subscriptions">
    <!-- specify details about the subscription that buyers will purchase -->
    <input type="hidden" name="item_name" value="<?php echo $itemName?>">
    <input type="hidden" name="item_number" value="<?php echo $itemNumber?>">
    <input type="hidden" name="currency_code" value="USD">
    <input type="hidden" name="a3" id="paypalAmt" value="<?php echo $itemPrice?>">
    <input type="hidden" name="p3" id="paypalValid" value="1">
    <input type="hidden" name="t3" value="M">
    <!-- custom variable user ID -->
    <input type="hidden" name="custom" value="<?php echo $loggedInUserID?>">
    <!-- specify urls -->
    <input type="hidden" name="cancel_return" value="<?php echo $cancelURL?>">
    <input type="hidden" name="return" value="<?php echo $successURL?>">
    <input type="hidden" name="notify_url" value="<?php echo $notifyURL?>">
    <!-- display the payment button -->
    <input class="paypal_button" type="submit" value="Buy Subscription">
</form>

The following JavaScript code is used to change the subscription price based on the validity.

<script>
function getSubsPrice(obj){
    var month = obj.value;
    var price = (month * <?php echo $itemPrice?>);
    document.getElementById('subPrice').innerHTML = '$'+price+' USD';
    document.getElementById('paypalValid').value = month;
    document.getElementById('paypalAmt').value = price;
}
</script>

Payment Success (success.php)

After payment on PayPal site, the buyer will be redirected to this page. The transaction details are fetched from the database based on the txn_id received by the $_GET method using PHP & MySQL and the payment status and information shows to the user.

<?php
//Include DB configuration file
include 'dbConfig.php';

if(!empty(
$_GET['item_number']) && !empty($_GET['tx']) && !empty($_GET['amt']) && $_GET['st'] == 'Completed'){
    
//get transaction information from query string
    
$item_number $_GET['item_number'];
    
$txn_id $_GET['tx'];
    
$payment_gross $_GET['amt'];
    
$currency_code $_GET['cc'];
    
$payment_status $_GET['st'];
    
$custom $_GET['cm'];
    
    
//Check if subscription data exists with the TXN ID
    
$prevPaymentResult $db->query("SELECT * FROM user_subscriptions WHERE txn_id = '".$txn_id."'");
    
    if(
$prevPaymentResult->num_rows 0){
        
//get subscription info from database
        
$paymentRow $prevPaymentResult->fetch_assoc();
        
        
//prepare subscription html to display
        
$phtml  '<h5 class="success">Thanks for payment, your payment was successful. Payment details are given below.</h5>';
        
$phtml .= '<div class="paymentInfo">';
        
$phtml .= '<p>Payment Reference Number: <span>MS'.$paymentRow['id'].'</span></p>';
        
$phtml .= '<p>Transaction ID: <span>'.$paymentRow['txn_id'].'</span></p>';
        
$phtml .= '<p>Paid Amount: <span>'.$paymentRow['payment_gross'].' '.$paymentRow['currency_code'].'</span></p>';
        
$phtml .= '<p>Validity: <span>'.$paymentRow['valid_from'].' to '.$paymentRow['valid_to'].'</span></p>';
        
$phtml .= '</div>';
    }else{
        
$phtml '<h5 class="error">Your payment was unsuccessful, please try again.</h5>';
    }
}elseif(!empty(
$_GET['item_number']) && !empty($_GET['tx']) && !empty($_GET['amt']) && $_GET['st'] != 'Completed'){
    
$phtml '<h5 class="error">Your payment was unsuccessful, please try again.</h5>';
}
?> <!DOCTYPE html> <html> <head> <title>PayPal Subscriptions Payment Payment Status</title> <meta charset="utf-8"> </head> <body> <div class="container"> <h1>PayPal Subscriptions Payment Status</h1> <!-- render subscription details --> <?php echo !empty($phtml)?$phtml:''?> </body> </html>

Validate and Get Transaction Details with IPN (paypal_ipn.php)

PayPal Instant Payment Notification (IPN) is used to validate the transaction. PayPal posts the payment information to the IPN URL after payment.

To use this feature, IPN must be enabled in your PayPal account. See this step-by-step guide to enable IPN in PayPal – How to enable PayPal Instant Payment Notification

In this IPN file (paypal_ipn.php), we will validate the transaction and insert the payment details in the database. After inserting the transaction data in user_subscriptions, the subscription ID also be updated in the users table.

<?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 "https://www.sandbox.paypal.com/cgi-bin/webscr";
$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) {
    
//Include DB configuration file
    
include 'dbConfig.php';
    
    
$unitPrice 25;
    
    
//Payment data
    
$subscr_id $_POST['subscr_id'];
    
$payer_email $_POST['payer_email'];
    
$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'];
    
$custom $_POST['custom'];
    
$subscr_month = ($payment_gross/$unitPrice);
    
$subscr_days = ($subscr_month*30);
    
$subscr_date_from date("Y-m-d H:i:s");
    
$subscr_date_to date("Y-m-d H:i:s"strtotime($subscr_date_from' + '.$subscr_days.' days'));
    
    if(!empty(
$txn_id)){
        
//Check if subscription data exists with the same TXN ID.
        
$prevPayment $db->query("SELECT id FROM user_subscriptions WHERE txn_id = '".$txn_id."'");
        if(
$prevPayment->num_rows 0){
            exit();
        }else{
            
//Insert tansaction data into the database
            
$insert $db->query("INSERT INTO user_subscriptions(user_id,validity,valid_from,valid_to,item_number,txn_id,payment_gross,currency_code,subscr_id,payment_status,payer_email) VALUES('".$custom."','".$subscr_month."','".$subscr_date_from."','".$subscr_date_to."','".$item_number."','".$txn_id."','".$payment_gross."','".$currency_code."','".$subscr_id."','".$payment_status."','".$payer_email."')");
            
            
//Update subscription id in users table
            
if($insert){
                
$subscription_id $db->insert_id;
                
$update $db->query("UPDATE users SET subscription_id = {$subscription_id} WHERE id = {$custom}");
            }
        }
    }
}
die;

Make PayPal Payment Gateway Live

After testing the PayPal subscriptions payment gateway on Sandbox environment, now it’s time to make PayPal payment gateway live. You need to do the following changes to live PayPal subscriptions payment.

In the index.php file, change the $paypalURL and $paypalID variable value with live PayPal URL and business email.

$paypalURL 'https://www.paypal.com/cgi-bin/webscr';
$paypalID  'Insert_PayPal_Business_Email';

In the paypal_ipn.php file, change the $paypalURL variable value with live PayPal URL.

$paypalURL "https://www.paypal.com/cgi-bin/webscr";

You are done! Now you can accept payment for subscription on your website with PayPal.

Are you want to get implementation help, or modify or extend the functionality of this script? Submit paid service request

Recommended Tutorials For You

Leave a reply