Skip to content
This repository has been archived by the owner on Jul 20, 2020. It is now read-only.

Fix for plus signs in data and verified array #119

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
46 changes: 24 additions & 22 deletions php/PaypalIPN.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,15 @@ public function getPaypalUri()
* Verification Function
* Sends the incoming post data back to PayPal using the cURL library.
*
* @return bool
* @return array|bool
* @throws Exception
*/
public function verifyIPN()
{
if ( ! count($_POST)) {
if (version_compare(phpversion(), '5.3.0', '<')) {
throw new Exception("Requires PHP version 5.3.0 or greater");
}
if ( ! count($_POST) ) {
throw new Exception("Missing POST Data");
}

Expand All @@ -78,28 +81,28 @@ public function verifyIPN()
foreach ($raw_post_array as $keyval) {
$keyval = explode('=', $keyval);
if (count($keyval) == 2) {
// Since we do not want the plus in the datetime string to be encoded to a space, we manually encode it.
if ($keyval[0] === 'payment_date') {
if (substr_count($keyval[1], '+') === 1) {
$keyval[1] = str_replace('+', '%2B', $keyval[1]);
}
// Since we do not want the plus signs in the date and email strings to be encoded to a space, we use rawurldecode instead of urldecode
if (
// single plus sign found in date
($keyval[0] === 'payment_date' && substr_count($keyval[1], '+') === 1)
// space found that was not encoded using a plus sign
|| strpos(rawurldecode($keyval[1]), ' ') !== false
// un-encoded email address found when receiving data from the simulator
|| ($_POST["test_ipn"] == 1 && filter_var($keyval[1], FILTER_VALIDATE_EMAIL))
) {
// Keep plus signs
$myPost[$keyval[0]] = rawurldecode($keyval[1]);
} else {
// Convert plus signs to spaces
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
}

// Build the body of the verification post request, adding the _notify-validate command.
$req = 'cmd=_notify-validate';
$get_magic_quotes_exists = false;
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);
}
$value = rawurlencode($value);
$req .= "&$key=$value";
}

Expand All @@ -121,7 +124,7 @@ public function verifyIPN()
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
$res = curl_exec($ch);
if ( ! ($res)) {
if ( ! ($res) ) {
$errno = curl_errno($ch);
$errstr = curl_error($ch);
curl_close($ch);
Expand All @@ -136,11 +139,10 @@ public function verifyIPN()

curl_close($ch);

// Check if PayPal verifies the IPN data, and if so, return true.
// Check if PayPal verifies the IPN data, and if so, return data array.
if ($res == self::VALID) {
return true;
} else {
return false;
return $myPost;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this class should have a single responsibility, to verify that the IPN data is valid.
You can then retrieve the post data from $__POST or your frameworks request object. I do not see a reason why this needs to return data.

Copy link
Contributor Author

@BigRedBot BigRedBot Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least from the sandbox, it is in some cases receiving incorrectly encoded data. This can cause the data in $_POST to be incorrect. Since we can compensate for this and rebuild the data, then we should at that point use the validated rebuilt data.

There is pretty much no reason for it not to include the verified array. It already has it, so why not just return it?

Copy link
Contributor Author

@BigRedBot BigRedBot Jan 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other option (that has not been done yet), is to have paypal's servers always make sure that $_POST is always correct no matter where the data is coming from. This would still require the use of the rawurlencode function, to be sure that plus signs are correctly verified.

}
return null;
}
}