Skip to content

Instantly share code, notes, and snippets.

@anointed
Created September 30, 2012 02:42
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save anointed/3805698 to your computer and use it in GitHub Desktop.
Save anointed/3805698 to your computer and use it in GitHub Desktop.
Paypal multiple IPN's
<?php
/*
* This is a PayPal IPN (Instant Payment Notification) broadcaster
* Since PayPal does not provide any straightforward way to add
* multiple IPN listeners we'll have to create a central IPN
* listener that will broadcast (or filter and dispatch) Instant
* Payment Notifications to different destinations (IPN listeners)
*
* http://codeseekah.com/2012/02/11/how-to-setup-multiple-ipn-receivers-in-paypal/
*
* Destination IPN listeners must not panic and recognize IPNs sent
* by this central broadcast as valid ones in terms of source IP
* and any other fingerprints. Any IP filtering must add this host,
* other adjustments made as necessary.
*
* IPNs are logged into files for debugging and maintenance purposes
*
* this code comes with absolutely no warranty
*/
ini_set( 'max_execution_time', 0 ); /* Do not abort with timeouts */
ini_set( 'display_errors', 'Off' ); /* Do not display any errors to anyone */
$urls = array(); /* The broadcast session queue */
/* List of IPN listener points */
$ipns = array(
'mystore' => 'http://mystore.com/ipn.php',
'myotherstore' => 'http://mybigstore.com/paypal_ipn.php',
'myotherandbetterstore' => 'http://slickstore.com/paypal/ipn.php'
);
/* Fingerprints */
if ( /* My Store IPN Fingerprint */
preg_match( '#^\d+\|[a-f0-9]{32}$#', $_POST['custom'] ) /* Custom hash */
and $_POST['num_cart_items'] == 2 /* alwayst 1 item in cart */
and strpos( $_POST['item_name1'], 'MySite.com Product' ) == 0 /* First item name */
) $urls []= $ipns['mystore']; /* Choose this IPN URL if all conditions have been met */
if ( /* My Other Store IPN Fingerprint */
sizeof( explode('_', $_POST['custom']) ) == 7 /* has 7 custom pieces */
) $urls []= $ipns['myotherstore']; /* Choose this IPN URL if all conditions have been met */
/* My Other And Better Store IPN Fingerprint */
$custom = explode('|', $_POST['custom']);
if (
isset($custom[2]) and $custom[2] == 'FROM_OB_STORE' /* custom prefixes */
) $urls []= $ipns['myotherandbetterstore']; /* Choose this IPN URL if all conditions have been met */
/* ... */
/* Broadcast */
if ( !sizeof($urls) ) $urls = $ipns; /* No URLs have been matched */
$urls = array_unique( $urls ); /* Unique, just in case */
/* Broadcast (excluding IPNs from the list according to filter is possible */
foreach ( $urls as $url ) broadcast( $url );
header( 'HTTP/1.1 200 OK', true, 200 );
exit(); /* Thank you, bye */
/* Perform a simple cURL-powered proxy request to broadcast */
function broadcast( $url ) {
/* Format POST data accordingly */
$data = array();
foreach ($_POST as $key => $value) $data []= urlencode($key).'='.urlencode($value);
$data = implode('&', $data);
/* Log the broadcast */
file_put_contents('_logs/'.time().'.'.reverse_lookup( $url ).'-'.rand(1,100), $data);
$ch = curl_init(); /* Initialize */
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, count($data));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch); /* Execute HTTP request */
curl_close($ch); /* Close */
}
function reverse_lookup( $url ) {
global $ipns;
foreach ( $ipns as $tag => $_url ) {
if ( $url == $_url ) return $tag;
}
return 'unknown';
}
?>
@anointed
Copy link
Author

The script is quite simple:

be ready to receive an IPN from PayPal
construct a list of broadcast targets by looking at the IPN data
broadcast (the script uses cURL as you can see)

You can remove any fingerprint filtering and have this central IPN broadcast to all your URLs regardless, but filtering is a good idea. Nothing beats the custom IPN POST variable, if it’s possible have all your payments go to PayPal with a unique prefix in the custom field and then filter based on that prefix. Refer to the PayPal IPN variables list in order to construct reliable fingerprints.

PayPal Business Accounts can also have up to 8 associated non-primary e-mails, so you can build a fingerprint based on the receiver_email IPN variable instead.

Remember to always verify all IPN requests with PayPal in your satellites or in your broadcast IPN (check PHP PayPal IPN script example).
The conclusion

Thus, multiple PayPal IPN with one PayPal account is absolutely possible and is not too difficult. I’d love to hear what improvements you do to this snippet, so give me a shout anytime.

And for the very attentive of you, and those who read up to the very end, PayPal has a notify_url variable that sets the IPN for the current form; so sometimes there’s no need to go through all the trouble of broadcasting anything really, in other cases it helps.

@anointed
Copy link
Author

Wouldn’t it be safer and easier to just issue a 302/303 redirect instead of re-sending the data using curl?

@Graphicpoet
Copy link

Hey there.

I really need this but I am pretty green at this sort of stuff. So one BIG question. Can I delete this :

if ( /* My Store IPN Fingerprint _/
preg_match( '#^\d+|[a-f0-9]{32}$#', $POST['custom'] ) / Custom hash _/
and $POST['num_cart_items'] == 2 / alwayst 1 item in cart _/
and strpos( $POST['item_name1'], 'MySite.com Product' ) == 0 / First item name /
) $urls []= $ipns['mystore']; /
Choose this IPN URL if all conditions have been met */

if ( /* My Other Store IPN Fingerprint */
    sizeof( explode('_', $_POST['custom']) ) == 7 /* has 7 custom pieces */
) $urls []= $ipns['myotherstore']; /* Choose this IPN URL if all conditions have been met */

AND JUST KEEP THIS ONE BELOW AND DUPLICATE IT FOR EACH IPN? :

/* My Other And Better Store IPN Fingerprint _/
$custom = explode('|', $POST['custom']);
if (
isset($custom[2]) and $custom[2] == 'FROM_OB_STORE' /
custom prefixes /
) $urls []= $ipns['myotherandbetterstore']; /
Choose this IPN URL if all conditions have been met */

/* ... */


/* Broadcast */

Thanks in advance even if you get this....

M. Byrd

@garyvenrooy
Copy link

That worked great! Thank you!

@yanyan001
Copy link

Hi, can i ask the procedure on where to put that code in my wordpress server? My problem is, i have a LIST on my mailchimp wherein after purchasing thru paypal, the details(especially email) of the customer will be added to the list of subscribers on mailchimp. But then i have 2 lists and the problem is, paypal only allows 1 IPN. Can you help me on this?? I badly need your help :(

@gino8080
Copy link

gino8080 commented Jul 1, 2016

Great stuff that works!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment