Using PayPal's IPN with Adaptive/Parallel Payments
Introduction
Often while working with PayPal's API, you would want some sort of action to take place once a payment is complete. This is where PayPal's Instant Payment Notification service comes into play. Once a payment is complete, PayPal will send your server a message, your server will acknowledge the message, and the payment will be confirmed. Sounds easy, right?
Not really
Due to the fact we are leaving an open point on our server for payment verification, PayPal has decided it has to be quite strict to ensure people don't go about sending bogus calls to your handler in an attempt to fake payments. Once your IPN handler receives the message, your server must send the exact message back with one tiny addition. PayPal will then verify your message is also legitimate before sending you back either VERIFIED
or INVALID
.
Prefer Pictures?
Let's jump into the implementation. PayPal gives us a listener we can use with php, and it works fine for most cases, but will not work for Adaptive Payments and perhaps others. Don't believe me? Go try it. Chances are you've found this article because you're looking for the solution, so I don't need to prove it isn't working.
The important bit of the handler is as follows.
foreach ($_POST 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";
}
You can see this simply takes each of the $_POST
variables and adds them into an encoded string. This works for most simple payment methods on PayPal, but as soon as we jump to Adaptive or Parallel Payments, PayPal will return INVALID
on all messages sent for verification.
The Problem
Check out this table. You'll notice there are some variables such as transaction[n].id
that seem to represent an array of objects. In the code snippet above, urlencode is called on each $value
. The problem is that urlencode will only accept strings (source) and transaction[n]
is an array. Since the array won't get encoded, we end up with a malformed message that PayPal thinks is fraudulent and will return INVALID
after we send it.
The Solution
Forget about trying to process the $_POST
variables with a loop, actually- we'll be ignoring $_POST
all together for verification purposes. Instead, we'll be taking exactly what our server receives, put the cmd=_notify-validate before the message, and use that to validate our IPN message. It's actually straight-forward.
$req = 'cmd=_notify-validate&'.file_get_contents("php://input");
After that, it's just a matter of replacing the request in PayPal's example handler. Using this method will generate a VERIFIED message on all payment types.
Finishing Up
Now that we have a VERIFIED
IPN message, we can forget about our $req
string and start verification on our end. Checking the payment_status
is essential and can be easily accessed through the $_POST
array ($_POST['status']
).
PayPal can be really troublesome.