-
-
Save anointed/3805698 to your computer and use it in GitHub Desktop.
<?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'; | |
} | |
?> |
Wouldn’t it be safer and easier to just issue a 302/303 redirect instead of re-sending the data using curl?
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 _/
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
That worked great! Thank you!
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 :(
Great stuff that works!!!
The script is quite simple:
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.