PayPal - IPN Notifications - Forum

Forum Navigation
You need to log in to create posts and topics.

PayPal - IPN Notifications

Hi..

10 years ago i created a web product and i got payments through PayPal. I setup back then the IPN for notifications. Well after 10 years everything looks more difficult to setup. So i will need any help to make it work.

The idea is that when a payment is made Paypal sends a notification to a specific URL you have defined (IPN listener) and from there you can do what ever you want. So i setup a php file called paypal_ipn.php who takes the response from Paypal and enters the data into a table in databse called transactions.

Well a setup everything but i don't receive any notification or the script is not working properly. Any ideas ?

I have upload the template and the sql file for the database.

Any help is appreciated

Uploaded files:
  • You need to login to have access to uploads.

@smartmedia this is a ChatGPT comment on your PHP code:

The code looks fine in terms of syntax, but there are a few things that could be improved:

  • Security: This code does not properly validate the input from PayPal and could potentially be vulnerable to attacks. It is recommended to use a library or a framework that has built-in security features to handle IPN requests.
  • Error handling: The code does not have proper error handling. If there is an error with the connection to the database or with the PayPal IPN request, the script will terminate and no error message will be displayed.
  • PayPal URL: The PayPal URL used in the code is for the live production environment. For testing purposes, it is recommended to use the PayPal sandbox URL.
  • Hard-coded credentials: The PayPal credentials and database credentials are hard-coded in the script, which is not recommended. It is recommended to store these credentials in a secure configuration file or environment variables.
  • Deprecated MySQL functions: The code uses the deprecated MySQL functions. It is recommended to use the improved MySQLi or PDO extension for database connectivity.

Overall, it is recommended to use a more secure and robust solution for handling PayPal IPN requests.

When I ask him for a suggestion this is what I get:

here are a few things you can improve in this code:

  • Store sensitive information, such as your PayPal API credentials, in a secure manner. One way to do this is by using environment variables or a configuration file that is not accessible via the web.
  • Sanitize the user input before using it in the SQL query. This helps prevent SQL injection attacks.
  • Use prepared statements instead of interpolating user input into the SQL query. This helps prevent SQL injection attacks and also makes the code more readable.
  • Add error handling to catch any errors that may occur during the execution of the script.

Here's an example of how you could implement some of these improvements:

<?php
// PayPal IPN listener

// PayPal API credentials
$paypal_url = "https://ipnpb.paypal.com/cgi-bin/webscr";
$paypal_email = "your_paypal_email_address";
$paypal_currency = "USD";

// Connect to database
$mysqli = new mysqli("localhost", "username", "password", "database_name");
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

// Read POST data from PayPal
$post_data = file_get_contents("php://input");
$post_array = array();
parse_str($post_data, $post_array);

// Add custom parameters to POST data
$post_array['cmd'] = '_notify-validate';
$post_array['custom'] = $_POST['custom'];

// Validate IPN with PayPal
$curl = curl_init();
curl_setopt_array($curl, array(
    CURLOPT_URL => $paypal_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post_array),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_CAINFO => dirname(__FILE__) . '/cacert.pem', // Download from https://curl.haxx.se/docs/caextract.html
));
$response = curl_exec($curl);
curl_close($curl);

// Check response from PayPal
if (strcmp($response, "VERIFIED") == 0) {
    // Process verified IPN
    $stmt = $mysqli->prepare("INSERT INTO transactions (txn_id, payer_email, amount, fee, status, currency, item_name, item_number, quantity, custom, address_street, address_city, address_state, address_zip, address_country, first_name, last_name, payer_id, payment_type, payment_date, ipn_track_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    $stmt->bind_param("sssssssssssssssssssss", $_POST['txn_id'], $_POST['payer_email'], $_POST['mc_gross'], $_POST['mc_fee'], $_POST['payment_status'], $_POST['mc_currency'], $_POST['item_name'], $_POST['item_number'], $_POST['quantity'], $_POST['custom'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], $_POST['first_name'], $_POST['last_name'], $_POST['payer_id'], $_POST['payment_type'], $_POST['payment_date'], $_POST['ipn_track_id']);
    if (!$stmt->execute()) {
        error_log("Error: " . $mysqli->error);
    }
} else if (strcmp($response, "INVALID") == 0) {
    // Handle invalid IPN
}

// Close database connection
$mysqli->close();
?>

And also says:
Please note that this is just an example and you should modify it to fit your specific needs.

Hope it helps.

Vadim and smartmedia have reacted to this post.
Vadimsmartmedia

Hi..

This is an update. After spend 1 month to figure out why the script it wasn't play i fix it. This is a tested and working code for IPN notifications.

<?php
// PayPal IPN listener

require_once 'config.php';

try {
    $pdo = new PDO("mysql:host={$dbServerNames[0]};dbname={$dbNames[0]}", $dbUserNames[0], $dbPasswords[0]);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    die("Database connection failed: " . $e->getMessage());
}

$paypal_url = "https://ipnpb.paypal.com/cgi-bin/webscr";
$paypal_email = "smartmediagr@gmail.com";
$paypal_currency = "EUR";

$post_data = file_get_contents("php://input");
$post_array = array();
parse_str($post_data, $post_array);

// Add custom parameters to POST data
$post_array['cmd'] = '_notify-validate';
if (isset($post_array['custom'])) {
        $post_array['custom'] = $post_array['custom'];
    }

// Validate IPN with PayPal
$curl = curl_init();
curl_setopt_array($curl, array(
    CURLOPT_URL => $paypal_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post_array),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_CAINFO => dirname(__FILE__) . '/cacert.pem'));
    
$response = curl_exec($curl);
curl_close($curl);

// Check response from PayPal
if (strcmp($response, "VERIFIED") == 0) {
    // Process verified IPN
    $txn_id = $post_array['txn_id'];
    $payer_email = $post_array['payer_email'];
    $mc_gross = $post_array['mc_gross'];
    $mc_fee = $post_array['mc_fee'];
    $payment_status = $post_array['payment_status'];
    $currency = $post_array['mc_currency'];
    $item_name = $post_array['item_name'];
    $item_number = $post_array['item_number'];
    $quantity = $post_array['quantity'];
    $custom = $post_array['custom'];
    $address_street = $post_array['address_street'];
    $address_city = $post_array['address_city'];
    $address_state = $post_array['address_state'];
    $address_zip = $post_array['address_zip'];
    $address_country = $post_array['address_country'];
    $first_name = $post_array['first_name'];
    $last_name = $post_array['last_name'];
    $payer_id = $post_array['payer_id'];
    $payment_type = $post_array['payment_type'];
    
    $payment_date = $post_array['payment_date'];
    $payment_date = urldecode("$payment_date");
    $payment_date = date('Y-m-d H:i:s', strtotime($payment_date));

    
    $mc_currency = $post_array['mc_currency'];
    $ipn_track_id = $post_array['ipn_track_id'];


    // Insert transaction into database
    $stmt = $pdo->prepare("INSERT INTO transactions (txn_id, payer_email, mc_gross, mc_fee, payment_status, currency, item_name, item_number, quantity, custom, address_street, address_city, address_state, address_zip, address_country, first_name, last_name, payer_id, payment_type, payment_date, ipn_track_id) VALUES (:txn_id, :payer_email, :mc_gross, :mc_fee, :payment_status, :currency, :item_name, :item_number, :quantity, :custom, :address_street, :address_city, :address_state, :address_zip, :address_country, :first_name, :last_name, :payer_id, :payment_type, :payment_date, :ipn_track_id)");

$stmt->bindParam(':txn_id', $txn_id);
$stmt->bindParam(':payer_email', $payer_email);
$stmt->bindParam(':mc_gross', $mc_gross);
$stmt->bindParam(':mc_fee', $mc_fee);
$stmt->bindParam(':payment_status', $payment_status);
$stmt->bindParam(':currency', $currency);
$stmt->bindParam(':item_name', $item_name);
$stmt->bindParam(':item_number', $item_number);
$stmt->bindParam(':quantity', $quantity);
$stmt->bindParam(':custom', $custom);
$stmt->bindParam(':address_street', $address_street);
$stmt->bindParam(':address_city', $address_city);
$stmt->bindParam(':address_state', $address_state);
$stmt->bindParam(':address_zip', $address_zip);
$stmt->bindParam(':address_country', $address_country);
$stmt->bindParam(':first_name', $first_name);
$stmt->bindParam(':last_name', $last_name);
$stmt->bindParam(':payer_id', $payer_id);
$stmt->bindParam(':payment_type', $payment_type);
$stmt->bindParam(':payment_date', $payment_date);
$stmt->bindParam(':ipn_track_id', $ipn_track_id);

$stmt->execute();

// Close database connection
$pdo = null;

// Return an empty response to PayPal
header('HTTP/1.1 200 OK');
}
?>

In order the code to work u must do the following.

  1. Download from https://curl.se/docs/caextract.html the cacert.pem and put it in the same folder the paypal_ipn.php is.
  2. Enable your IPN Notifications from you Paypal account and enter the URL where the paypal_ipn.php is saved.
  3. Create a table in your database and name it as you want, in this example is "transactions"
  4. Add all the fields in the table.
    CREATE TABLE transactions (
        txn_id VARCHAR(255),
        payer_email VARCHAR(255),
        mc_gross DECIMAL(10,2),
        mc_fee DECIMAL(10,2),
        payment_status VARCHAR(255),
        currency VARCHAR(255),
        item_name VARCHAR(255),
        item_number VARCHAR(255),
        quantity INT,
        custom VARCHAR(255),
        address_street VARCHAR(255),
        address_city VARCHAR(255),
        address_state VARCHAR(255),
        address_zip VARCHAR(255),
        address_country VARCHAR(255),
        first_name VARCHAR(255),
        last_name VARCHAR(255),
        payer_id VARCHAR(255),
        payment_type VARCHAR(255),
        payment_date DATETIME,
        mc_currency VARCHAR(255),
        ipn_track_id VARCHAR(255)
    );

    You done, normally you should take notifications every time you get paid from PayPal.

luishp, Vadim and 2 other users have reacted to this post.
luishpVadimDarbdenralsusan

@smartmedia thanks for sharing!! If I understand it correctly, whenever you receive a payment a new database record is saved with all the payment data. Is that correct?

smartmedia has reacted to this post.
smartmedia

Hi @luishp

Yea, that's correct. IPN is the best and accurate way to know if a payment has been made and in which state it is (Pending - Complete - Rejected). Saving the transaction into your database you gives you the ability to create automations, to keep orders history of your clients made. The code also is validating the accuracy of data received from PayPal and prevents SQL injections.

The only problem is the delay response from PayPal, i measured to all most 3 minutes to receive the answer. I guess this is happening cause PayPal managing millions of transactions worldwide.

I am trying to find a way to overcome that. I will make a post relative to paypal plugin in future cause i have some issues to discuss.

luishp has reacted to this post.
luishp