You may find yourself needing to send back a verifiable and dummy IPN response to PayPal after switching your IPN URL.  If so, this tutorial may be helpful.

Note, IPN stands for Instant Payment Notification.

I recently moved some forum communities from vBulletin to Xenforo.  The PayPal IPN addresses for vBulletin and Xenforo are completely different. 

Case in point:

vBulletin:
http://www.domain.com/forum/payment_gateway.php?method=paypal

Xenforo:
https://www.domain.com/community/payment_callback.php?_xfProvider=paypal

Problems with old IPN URL’s

One of the problems with PayPal’s IPN system is that you’re only able to have one IPN address at any given time.  When switching software you may be required to change your IPN to suit your new software or payment and recurring billing systems. 

The biggest problem changing IPN’s with PayPal is the fact that any profiles created PREVIOUS to your updated IPN will continue to respond back on your old IPN.  Now, in a perfect world you would have both systems running and integrated to handle this change.  However, when swapping out community software this becomes extremely difficult, time consuming, and cumbersome. 

Why not support both IPN’s?

If you have a development team, or tens of thousands of dollars of subscriptions per month this time would be justifiable, but, for a simple website or low revenue system this cost is not justifiable.

PayPal disabling defunct IPN addresses

Given your old subscriber profiles will be using an IPN URL that is now different than your current system, and perhaps offline, this will result in PayPal pinging on an old, and now, defunct IPN address.  This leads to failures on PayPal’s end and follow up automated email messages from PayPal that look like:

PayPal deactivation email

Hello, John Doe

Please check your server that handles PayPal Instant Payment Notifications (IPN). IPNs sent to the following URL(s) are failing:

http://www.domain.com/forum/payment_gateway.php?method=paypal

If you do not recognize this URL, you may be using a service provider that is using IPN on your behalf. Please contact your service provider with the above information. If this problem continues, IPNs may be disabled for your account.

Thank you for your prompt attention to this issue.

The largest issue is that old IPN pings fail on PayPal’s end, resulting in the email above and a possible disabling of future IPN’s. 

Quick fix instead of code?

A quick solution to prevent IPN disabling is to visit PayPal, view Notifications, and click “Edit Settings” and “Resave”.  I have found this to be effective to prevent PayPal disabling your IPN.  However, this is time consuming and feels like plugging holes with your finger.  A waste of time.

Failure to continually “resave” your current IPN address (due to old IPN failures on a defunct IPN address) will result in the automatic disabling of your current IPN address, thus disabling all PayPal notifications to and from your website. 

This is devastating consequence if you have updated your IPN to your current software.

A middle-ground solution to capture old IPN addresses

To overcome this issue I’ve created a “dummy” IPN system on my old IPN address for two purposes:

  1. Respond back to PayPal on my old IPN address to prevent deactivation or disabling of my account
  2.  To collect old subscriber and recurring billing information to a text file so I can manually review old recurring profiles
    • I can then manually remove subscriptions, or ensure anyone that’s paid continues to have their subscription privileges on my websites

What you’ll need:

  • PayPal developer account to test IPN messages to the sandbox
  • Your old PayPal IPN URL Address that’s failing
  • Access to your server to create files and test

Why would you want to fake a valid response to PayPal’s IPN?

  • To prevent your IPN from being disabled
  • To record previous old IPN recurring billing information

Setting up your dummy IPN

In order to ping back on an old and now defunct PayPal IPN address simply create a file at the old IPN end-point.

For me, this was: http://www.domain.com/forum/payment_gateway.php?method=paypal

I create a file at:  /forum/payment_gateway.php

The contents of this file needs to accept the PayPal Instant Payment Notification (IPN), and log the contents to a text file or database (I chose a simple text file).

Dummy IPN Code

<?php
 
// STEP 1: Read POST data
 
// reading posted data from 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";
}
 
// STEP 2: Post IPN data back to paypal to validate
 
// LIVE IPN ADDRESS
$ch = curl_init('https://ipnpb.paypal.com/cgi-bin/webscr');

// SANDBOX IPN ADDRESS
// $ch = curl_init('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr');

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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
 
// In wamp like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path 
// of the certificate as shown below.
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if( !($res = curl_exec($ch)) ) {
    // error_log("Got " . curl_error($ch) . " when processing IPN data");
    curl_close($ch);
    exit;
}
curl_close($ch);
 
 
// STEP 3: Inspect IPN validation result and act accordingly
 
if (strcmp ($res, "VERIFIED") == 0) {
    // check whether the payment_status is Completed
    // check that txn_id has not been previously processed
    // check that receiver_email is your Primary PayPal email
    // check that payment_amount/payment_currency are correct
    // process payment
 
    // assign posted variables to local variables
    $first_name = $_POST['first_name'];
    $last_name = $_POST['last_name'];
    $item_number = $_POST['item_number'];
    $payment_date = $_POST['payment_date'];
    $payer_status = $_POST['payer_status'];
    $payment_status = $_POST['payment_status'];
    $mc_fee = $_POST['mc_fee'];
    $payment_amount = $_POST['mc_gross'];
    $payment_currency = $_POST['mc_currency'];
    $txn_type = $_POST['txn_type'];
    $txn_id = $_POST['txn_id'];
    $receiver_email = $_POST['receiver_email'];
    $payer_email = $_POST['payer_email'];

    // <---- HERE you can do your INSERT to the database

    // <---- I append to a text file to track IPN hits

    $content = "";
    $content .= $first_name . ",";
    $content .= $last_name . ",";
    $content .= $item_number . ",";
    $content .= $payment_date . ",";
    $content .= $payer_status . ",";
    $content .= $payment_status . ",";
    $content .= $mc_fee . ",";
    $content .= $payment_amount . ",";
    $content .= $payment_currency . ",";
    $content .= $txn_type . ",";
    $content .= $txn_id . ",";
    $content .= $receiver_email . ",";
    $content .= $payer_email . "\n";
    $this_directory = dirname(__FILE__);
    $fp = fopen($this_directory . "/ipnhits.txt", "a");
    fwrite($fp, $content); 
    fclose($fp);

} else if (strcmp ($res, "INVALID") == 0) {
    // log for manual investigation
    $content = "";
    $content .= date("Y-m-d H:i:s");
    $content .= "\n";
    $this_directory = dirname(__FILE__);
    $fp = fopen($this_directory . "/ipnfails.txt", "a");
    fwrite($fp, $content); 
    fclose($fp);

}

Testing Sandbox & Going Live

If you would like to test that everything is working change the “live IPN end point” with the “sandbox IPN end” point.

You will need to sign up for PayPal’s developer account and access their “IPN Simulator“.

More information for PayPal sandbox testing for IPN is available here.

There are two basic requirements when testing in the sandbox.

  1. You need to change the code in your listener to post your response to the sandbox URL at https://ipnpb.sandbox.paypal.com/cgi-bin/webscr, instead of the live URL https://ipnpb.paypal.com/cgi-bin/webscr
  2. Your IPN listener must be implemented and running on your web server.

In Conclusion

Once setup properly, and once you’ve finished with the sandbox you will need to update your response URL to:

https://ipnpb.paypal.com/cgi-bin/webscr

Once working you should start to see your “ipnhits.txt” file fill up with your old subscriber IPN notifications, as well as ensure your current IPN address isn’t deactivated.  

In conclusion, you can collect old data while ensuring your new PayPal IPN is removed, as well as avoid future automated emails from PayPal regarding your new defunct IPN address.