Reputation: 90
I am using this listener I downloaded here Paypal-IPN-listener. The problem is I always get an "INVALID" response. I've checked my host provider and they confirmed that the server has OpenSSL v1.0.1 which supports TLS1.2. Any help would be much appreciated.
The response I get:
HTTP/1.1 200 OK Date: Fri, 10 Jun 2016 03:32:59 GMT Server: Apache X-Frame-Options: SAMEORIGIN Set-Cookie: c9MWDuvPtT9GIMyPc3jwol1VSlO=M5RZRbR9O0p3RyZcCd57VOjVO7vQahSBxti78obb5GWSaRge7FbInlJm9xeF6VIGA8jPMUyenYAZxlLOd4-YqTmucvLu0i7lWMg5RM94Q0SFZOVZ1_ysAo5Y_G3ISqMX_oc5Ts8Xqy5z9q8YKYW-RCBOKKo6ZpkXYUKF2RyjbOQIV3jeUWUSBBtkl4SxeDFi4MUm49opYTa6CvKAqQZ4vr6cWpOQghPpmEsz0s175Z8k6UtTfB99JV14faev1SBZUafmOkyNWCT3XSUF_dRWx-RAQ9L_T8MPetd5beRMm5fXBY1rvehFXw33aUvhTxckwGBJrdrKW2bOQa0Ry6YaRdp1uvdMC6uCuesozTjKXBFgSf0g0cDdHXoxV0rgVPTMlTRoVoV9xNJJ1CNKZzxeCvaFBZNSQdSTT888w0; domain=.paypal.com; path=/; Secure; HttpOnly Set-Cookie: cookie_check=yes; expires=Mon, 08-Jun-2026 03:32:59 GMT; domain=.paypal.com; path=/; Secure; HttpOnly Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly Set-Cookie: navlns=0.0; expires=Sun, 10-Jun-2018 03:32:59 GMT; domain=.paypal.com; path=/; Secure; HttpOnly Set-Cookie: Apache=10.72.108.11.1465529579669687; path=/; expires=Sun, 03-Jun-46 03:32:59 GMT Vary: Accept-Encoding,User-Agent X-Cnection: close HTTP_X_PP_AZ_LOCATOR: sandbox.slc Paypal-Debug-Id: 5d6d91989f423 Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.WEB.1%26silo_version%3D1880%26app%3Dappdispatcher%26TIME%3D3946076759%26HTTP_X_PP_AZ_LOCATOR%3Dsandbox.slc; Expires=Fri, 10 Jun 2016 04:03:00 GMT; domain=.paypal.com; path=/; Secure; HttpOnly Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT Strict-Transport-Security: max-age=14400 Transfer-Encoding: chunked Content-Type: text/html; charset=UTF-8
INVALID -------------------------------------------------------------------------------- payment_type instant payment_date Fri Jun 10 2016 11:02:14 GMT 0800 (PHT) payment_status Completed address_status confirmed payer_status verified first_name John last_name Smith payer_email [email protected] payer_id
TESTBUYERID01 address_name John Smith address_country
United States address_country_code US address_zip
95131 address_state CA address_city San Jose address_street 123 any street business
[email protected] receiver_email
[email protected] receiver_id
[email protected] residence_country US item_name1
something item_number1 AK-1234 tax
2.02 mc_currency USD mc_fee 0.44 mc_gross 12.34 mc_gross_1 12.34 mc_handling 2.06 mc_handling1 1.67 mc_shipping 3.02 mc_shipping1 1.02 txn_type
cart txn_id 331854069 notify_version 2.1 custom xyz123 invoice abc1234 test_ipn 1 verify_sign
AFcWxV21C7fd0v3bYYYRCpSSRl31AfWDVp14NjdjOS60QpDQiiFYCMSm
ipn.php:
<?php
include('listener/ipnlistener.php');
$listener = new IpnListener();
$listener->use_sandbox = true;
try {
$listener->requirePostMethod();
$verified = $listener->processIpn();
} catch (Exception $e) {
$file = fopen("log.txt","w");
fwrite($file,$e->getMessage());
fclose($file);
exit(0);
}
if ($verified) {
$file = fopen("log.txt","w");
fwrite($file,$listener->getTextReport());
fclose($file);
} else {
$file = fopen("log.txt","w");
fwrite($file,$listener->getTextReport());
fclose($file);
}
?>
ipnlistener.php
<?php
/**
* PayPal IPN Listener
*
* A class to listen for and handle Instant Payment Notifications (IPN) from
* the PayPal server.
*
* https://github.com/Quixotix/PHP-PayPal-IPN
*
* @package PHP-PayPal-IPN
* @author Micah Carrick
* @copyright (c) 2012 - Micah Carrick
* @version 2.1.0
*/
class IpnListener {
/**
* If true, the recommended cURL PHP library is used to send the post back
* to PayPal. If flase then fsockopen() is used. Default true.
*
* @var boolean
*/
public $use_curl = true;
/**
* If true, explicitly sets cURL to use SSL version 3. Use this if cURL
* is compiled with GnuTLS SSL.
*
* @var boolean
*/
public $force_ssl_v3 = true;
/**
* If true, cURL will use the CURLOPT_FOLLOWLOCATION to follow any
* "Location: ..." headers in the response.
*
* @var boolean
*/
public $follow_location = false;
/**
* If true, an SSL secure connection (port 443) is used for the post back
* as recommended by PayPal. If false, a standard HTTP (port 80) connection
* is used. Default true.
*
* @var boolean
*/
public $use_ssl = true;
/**
* If true, the paypal sandbox URI www.sandbox.paypal.com is used for the
* post back. If false, the live URI www.paypal.com is used. Default false.
*
* @var boolean
*/
public $use_sandbox = true;
/**
* The amount of time, in seconds, to wait for the PayPal server to respond
* before timing out. Default 30 seconds.
*
* @var int
*/
public $timeout = 30;
private $post_data = array();
private $post_uri = '';
private $response_status = '';
private $response = '';
const PAYPAL_HOST = 'www.paypal.com';
const SANDBOX_HOST = 'www.sandbox.paypal.com';
/**
* Post Back Using cURL
*
* Sends the post back to PayPal using the cURL library. Called by
* the processIpn() method if the use_curl property is true. Throws an
* exception if the post fails. Populates the response, response_status,
* and post_uri properties on success.
*
* @param string The post data as a URL encoded string
*/
protected function curlPost($encoded_data) {
if ($this->use_ssl) {
$uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';
$this->post_uri = $uri;
} else {
$uri = 'http://'.$this->getPaypalHost().'/cgi-bin/webscr';
$this->post_uri = $uri;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO,
dirname(__FILE__)."/cert/cacert.pem");
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->follow_location);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
if ($this->force_ssl_v3) {
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
}
$this->response = curl_exec($ch);
$this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
if ($this->response === false || $this->response_status == '0') {
$errno = curl_errno($ch);
$errstr = curl_error($ch);
throw new Exception("cURL error: [$errno] $errstr");
}
}
/**
* Post Back Using fsockopen()
*
* Sends the post back to PayPal using the fsockopen() function. Called by
* the processIpn() method if the use_curl property is false. Throws an
* exception if the post fails. Populates the response, response_status,
* and post_uri properties on success.
*
* @param string The post data as a URL encoded string
*/
protected function fsockPost($encoded_data) {
if ($this->use_ssl) {
$uri = 'ssl://'.$this->getPaypalHost();
$port = '443';
$this->post_uri = $uri.'/cgi-bin/webscr';
} else {
$uri = $this->getPaypalHost(); // no "http://" in call to fsockopen()
$port = '80';
$this->post_uri = 'http://'.$uri.'/cgi-bin/webscr';
}
$fp = fsockopen($uri, $port, $errno, $errstr, $this->timeout);
if (!$fp) {
// fsockopen error
throw new Exception("fsockopen error: [$errno] $errstr");
}
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: ".$this->getPaypalHost()."\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: ".strlen($encoded_data)."\r\n";
$header .= "Connection: Close\r\n\r\n";
fputs($fp, $header.$encoded_data."\r\n\r\n");
while(!feof($fp)) {
if (empty($this->response)) {
// extract HTTP status from first line
$this->response .= $status = fgets($fp, 1024);
$this->response_status = trim(substr($status, 9, 4));
} else {
$this->response .= fgets($fp, 1024);
}
}
fclose($fp);
}
private function getPaypalHost() {
if ($this->use_sandbox) return self::SANDBOX_HOST;
else return self::PAYPAL_HOST;
}
/**
* Get POST URI
*
* Returns the URI that was used to send the post back to PayPal. This can
* be useful for troubleshooting connection problems. The default URI
* would be "ssl://www.sandbox.paypal.com:443/cgi-bin/webscr"
*
* @return string
*/
public function getPostUri() {
return $this->post_uri;
}
/**
* Get Response
*
* Returns the entire response from PayPal as a string including all the
* HTTP headers.
*
* @return string
*/
public function getResponse() {
return $this->response;
}
/**
* Get Response Status
*
* Returns the HTTP response status code from PayPal. This should be "200"
* if the post back was successful.
*
* @return string
*/
public function getResponseStatus() {
return $this->response_status;
}
/**
* Get Text Report
*
* Returns a report of the IPN transaction in plain text format. This is
* useful in emails to order processors and system administrators. Override
* this method in your own class to customize the report.
*
* @return string
*/
public function getTextReport() {
$r = '';
// date and POST url
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n[".date('m/d/Y g:i A').'] - '.$this->getPostUri();
if ($this->use_curl) $r .= " (curl)\n";
else $r .= " (fsockopen)\n";
// HTTP Response
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n{$this->getResponse()}\n";
// POST vars
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n";
foreach ($this->post_data as $key => $value) {
$r .= str_pad($key, 25)."$value\n";
}
$r .= "\n\n";
return $r;
}
/**
* Process IPN
*
* Handles the IPN post back to PayPal and parsing the response. Call this
* method from your IPN listener script. Returns true if the response came
* back as "VERIFIED", false if the response came back "INVALID", and
* throws an exception if there is an error.
*
* @param array
*
* @return boolean
*/
public function processIpn($post_data=null) {
$encoded_data = 'cmd=_notify-validate';
if ($post_data === null) {
// use raw POST data
if (!empty($_POST)) {
$this->post_data = $_POST;
$encoded_data .= '&'.file_get_contents('php://input');
} else {
throw new Exception("No POST data found.");
}
} else {
// use provided data array
$this->post_data = $post_data;
foreach ($this->post_data as $key => $value) {
$encoded_data .= "&$key=".urlencode($value);
}
}
if ($this->use_curl) $this->curlPost($encoded_data);
else $this->fsockPost($encoded_data);
if (strpos($this->response_status, '200') === false) {
throw new Exception("Invalid response status: ".$this->response_status);
}
if (strpos($this->response, "VERIFIED") !== false) {
return true;
} elseif (strpos($this->response, "INVALID") !== false) {
return false;
} else {
throw new Exception("Unexpected response from PayPal.");
}
}
/**
* Require Post Method
*
* Throws an exception and sets a HTTP 405 response header if the request
* method was not POST.
*/
public function requirePostMethod() {
// require POST requests
if ($_SERVER['REQUEST_METHOD'] && $_SERVER['REQUEST_METHOD'] != 'POST') {
header('Allow: POST', true, 405);
throw new Exception("Invalid HTTP request method.");
}
}
}
Upvotes: 1
Views: 1101
Reputation: 1265
Debugging steps
Only SSL connection is permitted for Sandbox.
The HTTP protocol can still be used for the live site at the time of writing.
Check if you server supports TLS 1.2
PayPal is updating its services to require TLS v1.2 for all HTTPS/TLS
if you use fsockopen()m makes sure the function has been enabled in your PHP configuration
if you use curl() functions makes sure, they are enabled in your PHP configuration
Check if curl_setopt($ch, CURLOPT_SSLVERSION, ) is set to the correct version, i.e. 6 at the time of writing
If all the above has been checked and you still can't get it to work, you can trying putting NULL in the payment date field of the IPN simulator, to see if it returns a VERIFIED result.
The plus sign used for timezone in payment date can cause problem when urlencode() is used to parse the URL.
Upvotes: 1