Jump to content
Larry Ullman's Book Forums
Thomas

Changes in Paypal IPN script requirements in June 2018

Recommended Posts

Hi Larry:
I am using your book "Effortless E-commerce with php and mysql 2nd edition" to create a subscription website. When using your example 1 scripts, everything worked fine except that the database was not updating. The log was showing a 400 - Bad request error.The reason I found in one of your forums was a change in paypal's IPN script requirements, however the solution mentioned there is no more valid as paypal has made further changes to the scripts. It is now a completely different script which is very different from the one used in your example. I have tried everything but its not working. Could you please update the ipn script (the ipn_log.php file) in your example so that your example is still usable.
 
Also would it be possible for you to send me a copy of the updated script by e-mail or a link to the updated script.
 
I like all your books and commend you on your presentation style, especially the fact that you make extremely complicated things so simple.
 
Thanks, and waiting for your reply
Thomas

Share this post


Link to post
Share on other sites

Thanks for the nice words. Very much appreciated! Can you clarify: are you having problems with the sandbox usage of PayPal or the production usage? 

Share this post


Link to post
Share on other sites

Hi Larry,

I am still struggling with the paypal IPN script. The problem right now is with the sandbox usage. I have not yet tried the production usage.

Share this post


Link to post
Share on other sites

Sorry to hear you're still having problems. I haven't been able to test anything yet, but I just found a version of an IPN PHP example from PayPal. The key bits are:

https://github.com/paypal/ipn-code-samples/blob/master/php/PaypalIPN.php#L63-L139

Some modifications would be required (including changing self::VALID to "VERIFIED" on line 135). Let me know if you need any more details on how to adapt this. 

Share this post


Link to post
Share on other sites

Hi Larry,

Thank you very much for your time. I have got the ipn script working, but it is not making any changes to the database. I have checked everything but don't seem to find the problem. I am stuck badly. Could you please have a look at the log file and my ipn script. Sorry to bother you, but I have tried everything I could...

Here is my script:

<?php

// This page handles the Instant Payment Notification communications with PayPal.
// Most of the code comes from PayPal's documentation.
// This script is created in Chapter 6.
// This is a modified version of ipn.php that logs all transactions to a text file.

// Require the configuration before any PHP code as the configuration controls error reporting:
require ('./includes/config.inc.php');
// The config file also starts the session.

// Open the text file:
// Change this path to make it accurate.
// The text file must be writable by PHP!
$file = fopen('../ipn.txt', 'a');

// Write the POST data to the file:
fwrite($file, "Received:\n");
fwrite($file, print_r($_POST, true));
fwrite($file, "\n");

// Start by creating a request variable:
$req = 'cmd=_notify-validate';

// Add each received key=value pair to the request:
foreach ($_POST as $key => $value) {
    $value = urlencode(stripslashes($value));
    $req .= "&$key=$value";
}

// Open a socket connection to PayPal:
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30); // Test
//$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30); // Live

if (!$fp) { // If we couldn't connect, send an email:

    trigger_error('Could not connect for the IPN!');
    
} else { // Send the request to PayPal:
    
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n";
    $header .= "Connection: Close\r\n";
    $header .= "Host: www.sandbox.paypal.com\r\n\r\n";

fputs ($fp, $header . $req);

    // Write the PayPal request to the text file:
    fwrite($file, "Sent:\n");
    fwrite($file, "$header\n");
    fwrite($file, "$req\n");
    
    // Read in the response:
    while (!feof($fp)) {

        $res = fgets ($fp, 1024);

        // Write the PayPal response to the text file:
        fwrite($file, "Received:\n");
        fwrite($file, "$res\n");

        if (strcmp ($res, "VERIFIED") == 0) {
            
            // Check for the right values:
            if ( ($_POST['payment_status'] == 'Completed')
             && ($_POST['receiver_email'] == 'businesstype2@account.com')
             && ($_POST['mc_gross'] == 2.00)
             && ($_POST['mc_currency']  == 'USD')
             && (!empty($_POST['txn_id']))
            ) {
                
                // Need the database connection now:
                require(MYSQL);
                
                // Check for this transaction in the database:
                $txn_id = mysqli_real_escape_string($dbc, $_POST['txn_id']);                
                $q = "SELECT id FROM orders WHERE transaction_id='$txn_id'";
                $r = mysqli_query ($dbc, $q);
                if (mysqli_num_rows($r) == 0) { // Add this new transaction:
                    
                    $uid = (int) $_POST['custom'];
                    $status = mysqli_real_escape_string($dbc, $_POST['payment_status']);
                    $amount = (float) $_POST['mc_gross'];
                    $q = "INSERT INTO orders (user_id, transaction_id, payment_status, payment_amount) VALUES ($uid, '$txn_id', '$status', $amount)";
                    $r = mysqli_query ($dbc, $q);
                    if (mysqli_affected_rows($dbc) == 1) {
                        
                        // Update the users table:
                        $q = "UPDATE users SET date_expires = IF(date_expires > NOW(), ADDDATE(date_expires, INTERVAL 1 YEAR), ADDDATE(NOW(), INTERVAL 1 YEAR)), date_modified=NOW() WHERE id=$uid";
                        $r = mysqli_query ($dbc, $q);
                        if (mysqli_affected_rows($dbc) != 1) {
                            trigger_error('The user\'s expiration date could not be updated!');
                        }
                        
                    } else { // Problem inserting the order!
                        trigger_error('The transaction could not be stored in the orders table!');                        
                    }
                    
                } // The order has already been stored!
                
            } // The right values don't exist in $_POST!

        } elseif (strcmp ($res, "INVALID") == 0) {
            // log for manual investigation
        }

    } // End of the WHILE loop.
    
    // Close the connection:
    fclose ($fp);

} // End of $fp IF-ELSE.

// Inidicate the end of this transaction in the text file:
fwrite($file, "--------------\n");
fclose($file);

?>

 

Here is the log file:

Received:
Array
(
    [mc_gross] => 2.00
    [protection_eligibility] => Eligible
    [payer_id] => LUR52HDDC9QQJ
    [payment_date] => 23:28:28 Nov 04, 2018 PST
    [payment_status] => Completed
    [charset] => windows-1252
    [first_name] => ds
    [option_selection1] => Option 1
    [mc_fee] => 0.36
    [notify_version] => 3.9
    [subscr_id] => I-KHYJ5W4T4WYA
    [custom] => 7
    [payer_status] => verified
    [business] => businesstype2@account.com
    [verify_sign] => AFbtoae17G1snrl8ZQGTOr-6vaYpAOeYDYDiQ1jZwHau1ZceykLAeEqO
    [payer_email] => personaltype@account.com
    [option_name1] => payment options
    [txn_id] => 3MU76381Y5303693J
    [payment_type] => instant
    [btn_id] => 3917566
    [last_name] => dsa
    [receiver_email] => businesstype2@account.com
    [payment_fee] => 0.36
    [receiver_id] => JK68KZ5US2C44
    [txn_type] => subscr_payment
    [item_name] => subscribe1
    [mc_currency] => USD
    [item_number] => 1
    [residence_country] => US
    [test_ipn] => 1
    [transaction_subject] => subscribe1
    [payment_gross] => 2.00
    [ipn_track_id] => 2b0519bf904c2
)

Received:
Array
(
    [txn_type] => subscr_signup
    [subscr_id] => I-KHYJ5W4T4WYA
    [last_name] => dsa
    [option_selection1] => Option 1
    [residence_country] => US
    [mc_currency] => USD
    [item_name] => subscribe1
    [business] => businesstype2@account.com
    [amount3] => 2.00
    [recurring] => 1
    [verify_sign] => ANc79cx0nOjtjRmd1K0rzvpG.GGCAd5QwZfHVcr7UGVgJ-zLIoUB6vug
    [payer_status] => verified
    [test_ipn] => 1
    [payer_email] => personaltype@account.com
    [first_name] => ds
    [receiver_email] => businesstype2@account.com
    [payer_id] => LUR52HDDC9QQJ
    [option_name1] => payment options
    [reattempt] => 1
    [item_number] => 1
    [subscr_date] => 23:28:24 Nov 04, 2018 PST
    [btn_id] => 3917566
    [custom] => 7
    [charset] => windows-1252
    [notify_version] => 3.9
    [period3] => 1 W
    [mc_amount3] => 2.00
    [ipn_track_id] => 2b0519bf904c2
)

Sent:
POST /cgi-bin/webscr HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 827
Connection: Close
Host: www.sandbox.paypal.com


cmd=_notify-validate&mc_gross=2.00&protection_eligibility=Eligible&payer_id=LUR52HDDC9QQJ&payment_date=23%3A28%3A28+Nov+04%2C+2018+PST&payment_status=Completed&charset=windows-1252&first_name=ds&option_selection1=Option+1&mc_fee=0.36&notify_version=3.9&subscr_id=I-KHYJ5W4T4WYA&custom=7&payer_status=verified&business=businesstype2%40account.com&verify_sign=AFbtoae17G1snrl8ZQGTOr-6vaYpAOeYDYDiQ1jZwHau1ZceykLAeEqO&payer_email=personaltype%40account.com&option_name1=payment+options&txn_id=3MU76381Y5303693J&payment_type=instant&btn_id=3917566&last_name=dsa&receiver_email=businesstype2%40account.com&payment_fee=0.36&receiver_id=JK68KZ5US2C44&txn_type=subscr_payment&item_name=subscribe1&mc_currency=USD&item_number=1&residence_country=US&test_ipn=1&transaction_subject=subscribe1&payment_gross=2.00&ipn_track_id=2b0519bf904c2
Sent:
POST /cgi-bin/webscr HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 668
Connection: Close
Host: www.sandbox.paypal.com


cmd=_notify-validate&txn_type=subscr_signup&subscr_id=I-KHYJ5W4T4WYA&last_name=dsa&option_selection1=Option+1&residence_country=US&mc_currency=USD&item_name=subscribe1&business=businesstype2%40account.com&amount3=2.00&recurring=1&verify_sign=ANc79cx0nOjtjRmd1K0rzvpG.GGCAd5QwZfHVcr7UGVgJ-zLIoUB6vug&payer_status=verified&test_ipn=1&payer_email=personaltype%40account.com&first_name=ds&receiver_email=businesstype2%40account.com&payer_id=LUR52HDDC9QQJ&option_name1=payment+options&reattempt=1&item_number=1&subscr_date=23%3A28%3A24+Nov+04%2C+2018+PST&btn_id=3917566&custom=7&charset=windows-1252&notify_version=3.9&period3=1+W&mc_amount3=2.00&ipn_track_id=2b0519bf904c2
Received:
HTTP/1.1 200 OK

Received:
Date: Mon, 05 Nov 2018 07:29:08 GMT

Received:
Server: Apache

Received:
X-Frame-Options: SAMEORIGIN

Received:
Set-Cookie: c9MWDuvPtT9GIMyPc3jwol1VSlO=LO3tJK_vhQ0urAXMiTFbbKU5jsfIrhLsApihIGzPgnV-A7XGCS0ndcdsAZOIBFS9ttES675E7f3itp-vWWm87R8ACF9iwFC3ac8wvO0xtFw038VeOZAARcGEuy48j34EuAx0taCviJPKB91QOfjMxdk5U_NPv3Begcop2P8RSuwC39LpsO1bvpm-4EeqxoJ6KIgCfoB1D7kmhCsOpkgIlJbc26VLB256Z0HxCYPM13RswxXTNT_CHgchEnK3eI6nwKisr_BRgIytBRAoSSY0x8KLTmbTLdMtV649FRz_42-aLYuy7MZNk-kiwawytSquHbUmz8uJS_s1S10LtDCj31kOsvFWYZc5lZA2o692Y96s-4fz5B6FJNr6rWGzy0aA7q9VLXAk75NLVW9tdtA59EiIoVEqQLaOe0g1aPI6BF0rfH1yhA22CXnN0lS; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: cookie_check=yes; expires=Thu, 02-Nov-2028 07:29:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: navlns=0.0; expires=Wed, 04-Nov-2020 07:29:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: Apache=10.72.108.11.1541402948045743; path=/; expires=Wed, 28-Oct-48 07:29:08 GMT

Received:
Vary: Accept-Encoding,User-Agent

Received:
Connection: close

Received:
HTTP_X_PP_AZ_LOCATOR: sandbox.slc

Received:
Paypal-Debug-Id: 2964235d617f

Received:
Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.WEB.1%26silo_version%3D1880%26app%3Dappdispatcher%26TIME%3D1156702043%26HTTP_X_PP_AZ_LOCATOR%3Dsandbox.slc; Expires=Mon, 05 Nov 2018 07:59:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT

Received:
Transfer-Encoding: chunked

Received:
Content-Type: text/html; charset=UTF-8

Received:
Strict-Transport-Security: max-age=63072000

Received:


Received:
8

Received:
VERIFIED

Received:
0

Received:


--------------
Received:
HTTP/1.1 200 OK

Received:
Date: Mon, 05 Nov 2018 07:29:08 GMT

Received:
Server: Apache

Received:
X-Frame-Options: SAMEORIGIN

Received:
Set-Cookie: c9MWDuvPtT9GIMyPc3jwol1VSlO=_9eNJYJ52VW6CeqJZ11MjiesH2txcMDfRML1eFI1xDnK5QjmyplQ-2LiFAwG7GgQ_NRyyNmZEi31xRWVCqHdMZLQA2PhW44bRLUQij-lP268JWCW9QIwmSlnhoCWP2xp3G58fjdj1VPIiGN-QWaNoyM_knolxEFhe8RSjfYJc4Tb1PXHy4QOoT1jyHOLJ3iBb804MloM9AohgxK3Ggfhi9ls2sh_ILHLZmW13-eWW_hqkmN0qGJadk84g7gIn6EfzoA0NwxFn5ioAzrtYAJIA9MR7yPnDN_EyqzFAD-ZwpGDwJ_vNNOH4dNHzs_fFocoH6jA14EGq3ZLAsX1Op_NkyQG5zi3OJdXh6vzMHRegbxPiZjcuViwSmzQFwX7cbh2zhJ73JLqTfXEwzKxmM8fatUJ6VtIAa1TyAPGQBiXqyY8_4FVyzZKw6_tdom; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: cookie_check=yes; expires=Thu, 02-Nov-2028 07:29:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: navlns=0.0; expires=Wed, 04-Nov-2020 07:29:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: Apache=10.72.108.11.1541402948042463; path=/; expires=Wed, 28-Oct-48 07:29:08 GMT

Received:
Vary: Accept-Encoding,User-Agent

Received:
Connection: close

Received:
HTTP_X_PP_AZ_LOCATOR: sandbox.slc

Received:
Paypal-Debug-Id: fd4abe3c73e5

Received:
Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.WEB.1%26silo_version%3D1880%26app%3Dappdispatcher%26TIME%3D1156702043%26HTTP_X_PP_AZ_LOCATOR%3Dsandbox.slc; Expires=Mon, 05 Nov 2018 07:59:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly

Received:
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT

Received:
Transfer-Encoding: chunked

Received:
Content-Type: text/html; charset=UTF-8

Received:
Strict-Transport-Security: max-age=63072000

Received:


Received:
8

Received:
VERIFIED

Received:
0

Received:


--------------


 

Share this post


Link to post
Share on other sites

The log file helps a lot to debug this. It doesn't look like there's anything obviously amiss, which is to say it looks like all checks are passing. I would next debug this by running your MySQL queries by hand with dummy data to make sure they're all syntactically correct. You can also write more debugging info to the log file, things like "Running the SELECT query." and "No records selected." This should help you know what lines get executed and which don't. 

Share this post


Link to post
Share on other sites

Hi Larry:

Thankyou very much for making the time to look into my log file. I did what you suggested and figured out the problem. If I remove the following 'if' condition, everything works fine.

if (strcmp ($res, "VERIFIED") == 0)

Any suggestions as to how I can still use this 'if' condition and keep everything working? Could this be because of the new headers now required by paypal:

    $header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n";
    $header .= "Connection: Close\r\n";
    $header .= "Host: www.sandbox.paypal.com\r\n\r\n";

Thankyou once again for your help and support.

Share this post


Link to post
Share on other sites

It looks like PayPal is now using the lowercase "verified" for the payer_status. You could switch to that or use stricmp() instead. 

Share this post


Link to post
Share on other sites

Thankyou very much Larry for the reply and guidance. I did try the lowercase and stricmp, but it did not work. I did find a solution though and the reason for the problem. I could not have done it without your help and support! THANKS V. MUCH!

Problem:

Now Paypal IPN sends back VERIFIED but with numbers before and after

Solution:

  Change the function :
if (strcmp ($res, "VERIFIED") == 0) {

To

if (strpos($res,'VERIFIED') !== false) {

Share this post


Link to post
Share on other sites

Ah, excellent find and fix! Kudos for figuring that out and thanks for sharing the solution!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×