PayPal IPN leads to 502 Proxy Error - php

I am using the IPN code sample (for PHP) available on Github. When running this code via the IPN simulator, there is a huge loading time and after a few minutes PayPal finally throws this error.
Proxy Error
The proxy server received an invalid response from an upstream server. The proxy server could not handle the request POST /webapps/developer/applications/ipn_simulator.
Reason: Error reading from remote server
Another user faced the same exact issue here. However, it seems his problem was due to the fact he was not using the good script.
My ipn.php file, when accessed directly in the browser does not throw any error. It looks as follows:
/* INITIALIZATION */
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/aes.php'; // AES lib
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/db.php'; // DB configuration
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/mandrill/src/Mandrill.php'; // Mandrill mailer lib
$UA = $_SERVER['HTTP_USER_AGENT']; // User-Agent
//$proxy = '<my IP>:443';
$ip = getenv('HTTP_CLIENT_IP')?:
getenv('HTTP_X_FORWARDED_FOR')?:
getenv('HTTP_X_FORWARDED')?:
getenv('HTTP_FORWARDED_FOR')?:
getenv('HTTP_FORWARDED')?:
getenv('REMOTE_ADDR'); // IP address
$paypal_business = '< MY PAYPAL ID >'; // My PayPal ID
$license_amount = array( 10 => 0,
25 => 1,
50 => 2); // Type of license wrt to amount paid
$license_types = array( 0 => 1,
1 => 5,
2 => 20); // Number of licenses wrt type of license
$license_names = array( 0 => 'Single',
1 => 'Group',
2 => 'Company'); // Name of license wrt type of license
$donated = false;
/* MAILER powered by Mandrill */
function GmSendMail($bdy, $sbj, $em) {
try {
$mandrill = new Mandrill('< MY API KEY >'); // Mandrill API Key
$message = array(
'html' => $bdy,
'subject' => $sbj,
'from_email' => 'mail#example.com',
'from_name' => 'Company',
'to' => array(
array(
'email' => $em,
'type' => 'to'
)
),
'headers' => array('Reply-To' => 'support#example.com'),
'important' => true,
'track_opens' => true,
'track_clicks' => null,
'auto_text' => null,
'auto_html' => true,
'inline_css' => null,
'url_strip_qs' => null,
'preserve_recipients' => null,
'view_content_link' => null,
'tracking_domain' => null,
'signing_domain' => 'example.com',
'return_path_domain' => null,
'merge' => true,
'tags' => array('license-activation'),
'metadata' => array('website' => 'example.com')
);
$async = false;
$ip_pool = 'Main Pool';
$result = $mandrill->messages->send($message, $async, $ip_pool);
//print_r($result);
} catch(Mandrill_Error $e) {
// Mandrill errors are thrown as exceptions
echo 'An unexpected error occurred: ' . get_class($e) . ' - ' . $e->getMessage();
// A mandrill error occurred: Mandrill_Unknown_Subaccount - No subaccount exists with the id 'customer-123'
throw $e;
}
}
/* PAYPAL IPN: CHECK PAYMENT HAS BEEN COMPLETED */
// CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
// Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
// Set this to 0 once you go live or don't require logging.
define("DEBUG", 1);
// Set to 0 once you're ready to go live
define("USE_SANDBOX", 1);
define("LOG_FILE", "./ipn.log");
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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);
}
$req .= "&$key=$value";
}
// Post IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data
if(USE_SANDBOX == true) {
$paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
$paypal_url = "https://www.paypal.com/cgi-bin/webscr";
}
$ch = curl_init($paypal_url);
if ($ch == FALSE) {
return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
if(DEBUG == true) {
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
}
// CONFIG: Optional proxy configuration
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below. Ensure the file is readable by the webserver.
// This is mandatory for some environments.
$cert = __DIR__ . "/lib/cacert.pem";
curl_setopt($ch, CURLOPT_CAINFO, $cert);
$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
{
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
exit;
} else {
// Log the entire HTTP response if debug is switched on.
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);
// Split response headers and payload
list($headers, $res) = explode("\r\n\r\n", $res, 2);
}
curl_close($ch);
}
// Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
//if (preg_match("!(VERIFIED)\s*\Z!",$res)) {
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_date = $_POST['payment_date'];
$txn_id = $_POST['txn_id'];
$receiver_business = $_POST['business'];
$payer_email = $_POST['payer_email'];
$email_hash = sha1($payer_email, TRUE);
$user_id = $_POST['custom'];
// Retrieve license information
$license = $license_amount[$payment_amount];
$nblic = $license_types[$license] - 1;
$name_license = $license_names[$license];
// Confirmation email variables
$subject_paypal = 'Premium License [' . $name_license . ']';
$body_paypal = '<p>Dear user.</p>';
if ( $payment_status == 'Completed' && $receiver_business == strtolower($paypal_business) ) {
/* DB interactions and checks */
$check = mysql_query("SELECT * FROM `premium` WHERE `id` = '$email_hash'");
/* 1) User already exists: Update DB */
if (mysql_num_rows($check) === 1) {
$row = mysql_fetch_assoc($check);
$transaction_id = $row['pa'];
if ($txn_id == $transaction_id) {
$donated = true;
die('Premium license already activated.');
} else {
$licenses_left = $row['la'];
$current_key = $row['k'];
$newnblic = $licenses_left + $nblic;
// UPDATE DB
mysql_query("UPDATE `premium` SET `lt` = '$license', `la` = '$newnblic', `t` = '$payment_date', `pa` = '$txn_id' WHERE `id` = '$email_hash' ");
// SEND EMAIL CONFIRMATION
GmSendMail($body_paypal, $subject_paypal, $payer_email);
}
/* 2) This is a NEW entry: Insert into DB
*/
} else {
// CREATE ENTRY INTO DB
mysql_query("INSERT INTO `premium` VALUES('$email_hash','$license','$nblic','$payment_date','$txn_id')");
// SEND EMAIL CONFIRMATION
GmSendMail($body_paypal, $subject_paypal, $payer_email);
}
}
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
// Add business logic here which deals with invalid IPN messages
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
}
}
The ipn.log file, when accessed directly from the browser reads:
[2014-02-15 11:53 America/Denver] HTTP request of validation request:POST /cgi-bin/webscr HTTP/1.1
Host: www.sandbox.paypal.com
Accept: */*
Connection: Close
Content-Length: 20
Content-Type: application/x-www-form-urlencoded
for IPN payload: cmd=_notify-validate
[2014-02-15 11:53 America/Denver] HTTP response of validation request: HTTP/1.1 200 OK
Date: Sat, 15 Feb 2014 18:53:08 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Set-Cookie: < Cookie Hash >; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: cookie_check=yes; expires=Tue, 13-Feb-2024 18:53:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: navlns=0.0; expires=Mon, 15-Feb-2016 18:53:08 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: Apache=10.72.109.11.1392490388263396; path=/; expires=Mon, 08-Feb-44 18:53:08 GMT
Connection: close
Set-Cookie: X-PP-SILOVER=name%3DSANDBOX3.WEB.1%26silo_version%3D880%26app%3Dslingshot%26TIME%3D2495086418; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Set-Cookie: Apache=10.72.128.11.1392490388253446; path=/; expires=Mon, 08-Feb-44 18:53:08 GMT
Vary: Accept-Encoding
Strict-Transport-Security: max-age=14400
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
INVALID
[2014-02-15 11:53 America/Denver] Invalid IPN: cmd=_notify-validate
For information, ipn.php is hosted on a subdomain with https enabled, .htaccess is clean and does not block any proxy. PHP 5.4 is used. I'm on a shared server with a dedicated IP.
I tried to uncomment the following lines in ipn.php:
// CONFIG: Optional proxy configuration
curl_setopt($ch, CURLOPT_PROXY, $proxy);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
with one time:
$proxy = 'mydedicatedIP:80';
another time:
$proxy = 'mydedicatedIP:443';
Without any success. Any help welcome !

Related

Unable to Receive PayPal IPN Response in Code Igniter

I have been at this for 3 days now and still can't get it to work.
What I want to do is to get PayPal response from the IPN listener so that I can modify my database accordingly, but no matter what I do, it just won't work. I have already done the following in my PayPal Sandbox account:
Enabled Auto Return
Set Auto Return URL ('paypal/success')
Enabled Payment Data Transfer (PDT)
Enabled IPN message reception
Set IPN URL ('paypal/ipn')
The redirect to Auto Return URL works fine and I receive the payment data in success page, but the IPN won't process for reasons beyond me. A quick look at the IPN history on my PayPal profile shows that the messages are being sent, but I don't receive them at my end.
Here is my current IPN listener: Paypal/ipn
public function ipn() {
//Build the data to post back to Paypal
$postback = 'cmd=_notify-validate';
// go through each of the posted vars and add them to the postback variable
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$postback .= "&$key=$value";
}
// build the header string to post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.sandbox.paypal.com\r\n";//or www.sandbox.paypal.com
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";
// Send to paypal or the sandbox depending on whether you're live or developing
// comment out one of the following lines
$fp = fsockopen ('www.sandbox.paypal.com', 443, $errno, $errstr, 30);//open the connection
//$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
// or use port 443 for an SSL connection
//$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
if ( ! $fp ) {
// HTTP ERROR Failed to connect
$message = 'HTTP ERROR Failed to connect!';
$this->email_me($message);
} else { // if we've connected OK
fputs ($fp, $header . $postback); //post the data back
while ( ! feof($fp) ) {
$response = fgets ($fp, 1024);
if (strcmp (trim($response), "VERIFIED") == 0) { //It's verified
//read the payment details and the account holder
$payment_status = $_POST['payment_status'];
$receiver_email = urldecode($_POST['receiver_email']);
// further checks
if( ($payment_status == 'Completed') && ($receiver_email == $this->business_email) ) {
$message = 'IPN verified successfully!';
$this->email_me($message);
// Insert the transaction data in the database
$this->product_model->insert_transaction_details($_POST);
} else {
$message = 'Payment could not be verified!';
$this->email_me($message);
}
} else {
$message = 'IPN invalid!';
$this->email_me($message);
}
}
}
}
Can someone point me in the right direction please?
Also, is there anyway I can check the IPN response ("VERIFIED" or "INVALID") on chrome debugger or in my PayPal Sandbox dashboard? I can see delivery status in my dashboard but it doesn't say 'Verified' or 'Invalid' anywhere.
I found the solution! I wrote the IPN handler inside a controller that allows access to users who are logged in as admin. Apparently, the IPN method was denying access to PayPal to verify the transaction. I figured this out and wrote the IPN method in a different controller and everything worked perfectly.
I also changed my IPN handler to this code (although the original might still work... i didn't try it):
class Paypal_ipn extends MY_Controller {
public function __construct() {
parent::__construct();
$this->load->model('product_model');
$this->sandbox = $this->config->item('sandbox');
$this->paypal_host = $this->config->item('paypal_host');
$this->paypal_url = $this->config->item('paypal_url');
$this->business_email = $this->config->item('business');
}
public function ipn() {
// STEP 1: Read POST data
// reading posted data from directly from $_POST causes serialization
// issues with array data in POST
// reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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);
}
$req .= "&$key=$value";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init($this->paypal_url);
$headers = array(
'POST /cgi-bin/webscr HTTP/1.1',
'Host: ' . $this->paypal_host,
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
'Content-Length: ' . strlen($req),
'User-Agent: PayPal-IPN-VerificationScript',
'Connection: Close'
);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if( !($res = curl_exec($ch)) ) {
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = urldecode($_POST['receiver_email']);
$payer_email = $_POST['payer_email'];
$school_id = $_POST['custom'];
// further checks
if($payment_status == 'Completed') {
$message = 'IPN verified successfully!';
$this->email_developer($message);
// Insert the transaction data in the database
$this->product_model->insert_transaction_details($_POST);
} else {
$message = 'Payment could not be verified!';
$this->email_developer($message);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
$message = 'IPN Invalid!';
$this->email_developer($message);
}
}
}
For those that might experience my predicament, ensure you also do the following:
If you enabled Cross Site Request Forgery (CSRF), ensure the IPN listener/handler is whitelisted, else IPN message will fail (Error 403 in PayPal IPN history).
To be sure your IPN listener is working well, run it as a URL and see the response. If there is any error, it won't work. For response, trying echoing "Verified" or "Invalid".
Use the PayPal IPN Simulator to test the process. Include a procedure that will submit information to the database upon success.
I hope it helps someone.
use php://input instead of $_POST
reson described here in details : PHP "php://input" vs $_POST
also paypal has documentation for implementing IPN Listener and its in php as well
Paypal tutorial

PayPal IPN response invalid after sending post data back using PHP and cURL

My PayPal IPN code receives the post data sent back from PayPal upon purchase completion but doesn't insert the data into the database. I had the code send me an email if it fails to insert the data into the database. If I take that post data and paste it into the address bar after ipn.php?, it works perfectly. What am I doing wrong? Do I have to post the data back to PayPal?
My IPN Listener
<?php
include 'includes/class.user.php';
$user = new USER();
$emailtext = "";
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
$value = urlencode($value);
$req .= "&$key=$value";
}
$paypalURL = "https://ipnpb.paypal.com/cgi-bin/webscr";
$ch = curl_init($paypalURL);
if ($ch == FALSE) {
return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: Company name removed for security, LLC.'));
$res = curl_exec($ch);
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
//Payment data
$txn_id = $_GET['txn_id'];
$payment_gross = $_GET['mc_gross'];
$currency_code = $_GET['mc_currency'];
$pay_status = $_GET['payment_status'];
$payer_email = $_GET['payer_email'];
$date = date("m-d-y");
$invNum = md5($date);
$orderNum = md5($txn_id);
$fullname = $_GET['address_name'];
$address = $_GET['address_street'];
$city = $_GET['address_city'];
$state = $_GET['address_state'];
$zip = $_GET['address_zip'];
if($user->verify_txnid($txn_id)){
exit();
}else{
$insertPayment = $user->insert_purchase($fullname,$address,$city,$state,$zip,"0000000000",$payer_email,$orderNum,$date,$txn_id,$invNum,$pay_status);
if($insertPayment === TRUE){
$num_cart_items = $_GET['num_cart_items'];
for ($i = 0; $i <= $num_cart_items; $i++) {
$order_item_name = $_GET['item_name' . $i];
$order_item_quantity = $_GET['quantity' . $i];
$order_item_gross_amount = $_GET['mc_gross_' . $i];
$order_item_custom = $_GET['option_selection1_' . $i];
$user->insert_order($txn_id, $order_item_name, $order_item_quantity, $order_item_gross_amount, $order_item_custom);
}
}else{
foreach ($_GET as $key => $value)
{
$emailtext .= $key . " = " .$value ."\n\n";
}
mail('myemail#gmail.com', 'Insert Payment FAILED', $emailtext."\r\n".$req."\r\n".$res);
}
}
header("HTTP/1.1 200 OK");
I have confirmed that the post from PayPal matches the post back to PayPal but I still get an INVALID response and none of the data gets inserted into the database. If I take the raw post data string from PayPal's initial POST and paste it into the address bar, it inserts into the database just fine. I can't figure out for the life of me why it's not inserting the data into the database on its own. My IPN history on PayPal show an html response of 500 and that its retrying.
If IPN message you POST back does not exactly match the one PayPal sent to you, INVALID status will be occurred. You can double check if there have mess code in the IPN message you POST back, such as non-English characters. But if you still cannot find out the root cause, you have to contact PayPal technical support via https://www.paypal-techsupport.com/app/ask/ and provide PayPal transaction ID for the further checking.
Had the exactly same problem on sandbox.
Got it working by switching off "Negative Testing" on the account profile settings and removing the date from the ipn simulator.
That simple code made it work
$ipn_post_data = $_POST;
// Choose url
if (array_key_exists('test_ipn', $ipn_post_data) && 1 === (int) $ipn_post_data['test_ipn']){
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}else{
$url = 'https://www.paypal.com/cgi-bin/webscr';
}
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if ($status == 200 && $response == 'VERIFIED') {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, "Verified\r\n");
} else {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, $response."\r\n");
fwrite($file, $status."\r\n");
}
I would never use that code in production for many reasons.
Hope this can make things easy for u.

sandbox / stage of paypal super slow. Around 20 seconds to respond to my IPN handler. Now does not respond at all

i took the example implementation of the IPN handler from paypals official website
now this worked very well in the beginning. But the answers got slower and slower. For a while they worked fine with 20 seconds delay. Now I am getting no traffic at all for minutes. What is going on? maybe is the sandbox ipn handler of paypal themselves close to be down? they have a bad setup?
I want to mention this post (it kinda shed some light)
note i get not even an initial log. Paypal seems to not call my side at all any longer.
The IPN is callable, i can call it from, say an incognito window, and it will write to the logfile.
for completeness, here my file:
<?php
use ...;
// CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
// Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
// Set this to 0 once you go live or don't require logging.
define("DEBUG", 1);
// Set to 0 once you're ready to go live
define("USE_SANDBOX", 1);
define("LOG_FILE", "./ipn.log");
require_once(__DIR__ . "/../../bootstrap.php");
//define("LOG_FILE", __DIR__ . "/../../../../../japi/logs/ipn.log");
error_log(date('[Y-m-d H:i e] '). "Lukas: Initial call to log". PHP_EOL, 3, LOG_FILE);
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
//$mmessage ="hi lukas <br/>";
//
// include_once JCR_KINT_CLASS;
//$mmessage .= #\Kint::dump($myPost);
//mail("lukas.meier#gmail.com", "IPN from paypal test", $mmessage);
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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);
}
$req .= "&$key=$value";
}
// Post IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data
if(USE_SANDBOX == true) {
$paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
$paypal_url = "https://www.paypal.com/cgi-bin/webscr";
}
$ch = curl_init($paypal_url);
if ($ch == FALSE) {
return FALSE;
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
if(DEBUG == true) {
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
}
// CONFIG: Optional proxy configuration
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below. Ensure the file is readable by the webserver.
// This is mandatory for some environments.
//$cert = __DIR__ . "./cacert.pem";
//curl_setopt($ch, CURLOPT_CAINFO, $cert);
$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
{
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
exit;
} else {
// Log the entire HTTP response if debug is switched on.
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
}
// Inspect IPN validation result and act accordingly
// Split response headers and payload, a better way for strcmp
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your PayPal email
// check that payment_amount/payment_currency are correct
// process payment and mark item as paid.
// assign posted variables to local variables
//$item_name = $_POST['item_name'];
//$item_number = $_POST['item_number'];
//$payment_status = $_POST['payment_status'];
//$payment_amount = $_POST['mc_gross'];
//$payment_currency = $_POST['mc_currency'];
//$txn_id = $_POST['txn_id'];
//$receiver_email = $_POST['receiver_email'];
//$payer_email = $_POST['payer_email'];
$jppa = new JcrPayPalAnswer();
$jppa->createFromPayPalIpnResponseArray($myPost);
if($jppa->is_completed){
//content of file: paykey => array($project_id, serialize($fd), $statisticsId);
$r = parse_ini_file(JCR_PAYKEY_INIFILE);
if($r){
$pkArr = $r[$jppa->pay_key];
$project_id = $pkArr[0];
/** #var FundingDetails $fd */
$fd = unserialize(base64_decode($pkArr[1]));
$statisticsId = $pkArr[2];
$jcrp = new JewcerProject($project_id);
$jewcerFee = $jcrppa->amount_fee_account;
$fundingAmount = $jcrppa->amount_funding_account_brutto;
$x = null;
$js = new JcrStatistic($x, $statisticsId);
//fna [$amount, $paypalfee, $jewcerFeeAmount]
$fnA = JcrPayPalService::getFeesAndAmount($fd->amount, $fd->coverfee, $jcrp->getFundingFee());
$amount = $fnA[0];
$paypalfee = $fnA[3];
$jewcerFeeAmount = $fnA[2];
$fd->wepayFee = $paypalfee;
$fd->jcrFee = $jewcerFeeAmount;
$amount_with_fee = $amount;
if ($fd->coverfee) {
$fd->amount_without_fees = $amount - $paypalfee - $jewcerFeeAmount;
} else {
$fd->amount_without_fees = $fd->amount;
}
$fd->amount = $amount_with_fee;
$jcrf = new JcrFunder($project_id);
$jcrf->setBasicFunderValues($fd);
$jcrf->save();
$js->add_stats_from_fundingdetails($fd, "jfp3");
EmailService::sendDonationSuccessEmails($jcrp, $fd);
unset($r[$jppa->pay_key]);
UtilityService::write_ini_file($r, JCR_PAYKEY_INIFILE);
UtilityService::write_ini_file(array('ok', $jcrf->id ), JCR_PAYPAL_STATUS_FOLDER . $jppa->pay_key);
}else{
error_log(date('[Y-m-d H:i e] '). "JEWCER ERROR 3200: couldn't find entry for paykey in inifile, inifile val: " . var_export($r, true). PHP_EOL, 3, LOG_FILE);
}
// mail("lukas.meier#gmail.com", "IPN COMPLETED", serialize($jppa));
// JcrPayPalKeyPool::$keys[$jppa->pay_key] = $jppa; //eventually verify emails
// JcrPayPalKeyPool::$keys[$jppa->pay_key] = $jppa; //eventually verify emails
}
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
// Add business logic here which deals with invalid IPN messages
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
}
}
I have been using IPN for many years with a wide variety of projects. In my experience, if everything is configured correctly and your IPN script has no problems then it will work very close to real-time without many hiccups.
What you can run into, though, is if your IPN script is failing for any reason, or simply sending PayPal a response code other than 200 when it gets hit, it will re-try, but each time it re-tries it delays the time in which it sends. So it might send one instantly, if that doesn't provide a 200 response it'll send another one in 5 seconds, 10 seconds, 20 seconds, 40 seconds, etc. (that's not the exact increment it uses but it's an example of what it does.)
If your script is not returning a 200 response at some point, or if this is happening a lot with the IPNs that are getting sent to your script, PayPal's system will move your IPNs to a slower queue than the others that are working well.
Eventually, if it still isn't fixed, they'll just disable it altogether on your account.
Check the IPN history in your PayPal account to see what response you're getting back there on the IPNs that are getting sent. Of course, this will allow you to verify that they are indeed getting sent as well.
You'll also want to check the PHP error logs on your server to see if anything is going on when the IPN script gets hit that causes it to fail for any reason. This could be happening with only certain IPN types or when particular characters are included in the data, for example.

PayPal IPN verification

I have an IPN which is sending the messages correctly to my file, I have confirmed that I am receiving them, first here's my code:
echo "test";
// Response from Paypal
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value);// IPN fix
$req .= "&$key=$value";
}
// assign posted variables to local variables
$data['item_name'] = $_POST['item_name'];
$data['item_number'] = $_POST['item_number'];
$data['payment_status'] = $_POST['payment_status'];
$data['payment_amount'] = $_POST['mc_gross'];
$data['payment_currency'] = $_POST['mc_currency'];
$data['txn_id'] = $_POST['txn_id'];
$data['receiver_email'] = $_POST['receiver_email'];
$data['payer_email'] = $_POST['payer_email'];
$data['custom'] = $_POST['custom'];
// post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: www.sanbox.paypal.com\r\n";
$header .= "Accept: */*\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "\r\n";
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
if ($fp === FALSE) {
exit("Could not open socket");
}else{
if (!$fp) {
echo "Error code: 200";
}else{
fputs ($fp, $header . $req);
$res = stream_get_contents($fp, 2048);
echo "test2";
$res = trim($res);
if (strcmp($res, "VERIFIED") == 0){
echo "test3";
// Used for debugging
//#mail("you#youremail.com", "PAYPAL DEBUGGING", "Verified Response<br />data = <pre>".print_r($post, true)."</pre>");
// Validate payment (Check unique txnid & correct price)
$valid_txnid = check_txnid($data['txn_id']);
$valid_price = check_price($data['payment_amount'], $data['item_number']);
// PAYMENT VALIDATED & VERIFIED!
if($valid_txnid && $valid_price){
$orderid = updatePayments($data);
if($orderid){
echo "thanks for your payment!";
$lel = mysqli_query($con,"UPDATE stats SET stats.bankedgold = 10000 WHERE stats.id = $id4");
// Payment has been made & successfully inserted into the Database
}else{
echo "Error code: 100";
// E-mail admin or alert user
}
}else{
echo "Error code: 130";
// Payment made but data has been changed
// E-mail admin or alert user
}
}else if (strcmp ($res, "INVALID") == 0) {
echo "Error code: 170";
// PAYMENT INVALID & INVESTIGATE MANUALY!
// E-mail admin or alert user
// Used for debugging
//#mail("you#youremail.com", "PAYPAL DEBUGGING", "Invalid Response<br />data = <pre>".print_r($post, true)."</pre>");
}
echo strcmp($res,"VERIFIED" == 0);
fclose ($fp);
}
}
}
Now, a var_dump of $res gives:
HTTP/1.1 200 OK
Date: Mon, 02 Mar 2015 11:24:35 GMT
Server: Apache X-Frame-
Options: SAMEORIGIN
Set-Cookie: <REDACTED_FOR_PRIVACY>
Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: navlns=0.0; expires=Wed, 01-Mar-2017 11:24:35 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
Set-Cookie: Apache=10.72.108.11.1425295475397401; path=/; expires=Wed, 22-Feb-45 11:24:35 GMT
Vary: Accept-Encoding,User-Agent Connection: close
Set-Cookie: <REDACTED_FOR_PRIVACY>
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Set-Cookie: Apache=10.72.128.11.1425295475383817; path=/; expires=Wed, 22-Feb-45 11:24:35 GMT
Strict-Transport-Security: max-age=14400
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8 8
VERIFIED 0
But I need it to say just VERIFIED or INVALID so I can determine what to do from there.
A simple fix for me would be to add "strpos" and just see if it contains the words but I want to verify with you experts that it would be a good fix?
PayPal has started using HTTP/1.1 to send its responses. This means, among other things, that you get Transfer-Encoding: chunked in the response.
The chunked encoding requires parsing. Typically it will look like this:
8
VERIFIED
0
Translated, this gives "chunk of length 8 = VERIFIED || chunk of length 0 = end of stream"
If you can't parse this response, you can try to force PayPal to not use chunked encoding by specifying HTTP/1.0 in your validation response. Alternatively, use a better library like cURL to do the parsing for you - PayPal's demo IPN code was written for a very old version of PHP, or at least one with no extensions enabled. It seems like the programmer was ignorant of the http_build_query function to properly encode the $req verification data, which is just sad really. You'd think PayPal could afford competent developers *ahem* but anyway... Personally I "solved" it by just reading the Content-Length header - all the responses that PayPal can send here have different lengths. Sort of cheating, but it worked.
Hope this helps :)
Change this line:
$value = urlencode(stripslashes($value));
To this:
$value = urlencode($value);
You only need to use stripslashes there if you're runnning PHP with Magic Quotes on. As of PHP 5.4, that's (thankfully) no longer possible.
Paypal allows user data to contain backslashes (I have seen it in IPN data in the address_street variable). If you strip the backslashes out from IPN data that had them, Paypal will return an INVALID response.

Paypal SandBox IPN always returns INVALID

As mentioned in one of the comments in an answer below, I tried following this tutorial. So now I have the following:
The ipn.php file:
<?php
$ipn_post_data = $_POST;
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_CAINFO => 'cacert.pem',
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if($status == 200 && $response == 'VERIFIED')
{
$subject = "valid";
$message = "good";
}
else
{
$subject = "invalid";
$message = "bad";
}
$to = "oshirowanen#mail.com";
$from = "me#desktop.com";
$header = 'MIME-Version: 1.0' . "\r\n";
$header .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
$header .= 'To: Oshirowanen <oshirowanen#mail.com>' . "\r\n";
$header .= 'From: Me <me#desktop.com>' . "\r\n";
mail($to,$subject,$message,$header);
?>
The received email:
Subject "invalid"
Message "bad"
Edit:
Now that I can see the array you've outputted, try replacing this to get rid of the PHP array error:
foreach ($_POST as $key => $value) {
if (!is_array($value)) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
else if (is_array($value)) {
$paymentArray = explode(' ', $value[0]);
$paymentCurrency = urlencode(stripslashes($paymentArray[0]));
$paymentGross = urlencode(stripslashes($paymentArray[1]));
$req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
}
}
Here is the edited code in full:
// read the post from PayPal system and add 'cmd'
$req = 'cmd=' . urlencode('_notify-validate');
foreach ($_POST as $key => $value) {
if (!is_array($value)) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
else if (is_array($value)) {
$paymentArray = explode(' ', $value[0]);
$paymentCurrency = urlencode(stripslashes($paymentArray[0]);
$paymentGross = urlencode(stripslashes($paymentArray[1]);
$req .= '&mc_currency=' . $paymentCurrency . '&mc_gross=' . $paymentGross;
}
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Host: www.paypal.com'));
$res = curl_exec($ch);
curl_close($ch);
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];
if (strcmp ($res, "VERIFIED") == 0) {
// check the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
}
else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
Check this out!
Edit: Check out the PayPal troubleshooting tips:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNTesting
The problem is that you don't check the HTTP response code, so you are intepreting the "Invalid Host header" as the PayPal response, whilst it's the web server response (for the status code 400).
If you look at the PayPal documentation, there is a PHP example which is very similar to your code, since it uses the "fsockopen", "fputs" and "fgets" functions to communicate with the PayPal server.
But if you look carefully at the remark after the "fsockopen" call, you can read:
// Process validation from PayPal
// TODO: This sample does not test the HTTP response code. All
// HTTP response codes must be handled or you should use an HTTP
// library, such as cUrl
And this is exacty your problem: you don't check that the HTTP response code is 200 (OK), before parsing the response body.
Also, using the "strtolower" function is not correct, since the real response from the PayPal server is always uppercase, as shown in the above cited example.
Even if the PayPal example uses the "fsockopen" approach, I think it should be much better to use the PHP cURL library to implement your IPN listener.
Have also a look at the following answers:
PHP cURL PayPal Sandbox
cURL or fsockopen for paypal ipn
However, if you really want to use the "fsockopen" function, you should always specify the "Host" header field in the POST request, as shown in the following snippet of code (taken from the PHP manual):
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
UPDATE
Here is a simple function for recursive stripslashes/urlencoding:
<html>
<body>
<pre>
<?
$post = Array (
"transaction" => Array("USD 20.00"),
"payment_request_date" => "Sun Aug '05 08:49:20 PDT 2012",
"return_url" => "http://000.000.000.000/success.php"
);
echo "before myUrlencode...\n";
print_r($post);
function myUrlencode($post) {
foreach ($post as $key => $val) {
if (is_array($val)) {
$post[$key] = myUrlencode($val);
} else {
$post[$key] = urlencode(stripslashes($val));
}
}
return($post);
}
echo "\nafter myUrlencode...\n";
print_r(myUrlencode($post));
?>
</pre>
</body>
</html>
Got it working using the basic sample code 4b,
Cleared $ipnNotificationUrl = ""; from the basic sample code as I had a value in there which I added myself,
Created a seller account instead of a business pro account in sandbox,
Set the seller account to enable the ipn url,
Used the following PHP 5.2 sample code for the ipn listener
Added the 2 lines into the listener, as described here, the 2 lines can be seen below:
Downloaded the cacert.pem certificate to my server from here and put it in the same directory as the ipn listener:
The 2 lines mentioned in point 6:
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_CAINFO => 'cacert.pem',
I have no idea why the sandbox business pro account does not let me set an ipn url, but the seller account does.
These links may resolve your problem,
Paypal: Invalid IPN problem
http://www.webmasterworld.com/ecommerce/4292847.htm
Paypal sandbox IPN return INVALID
I am not sure what is exactly wrong right now with your code, but I was strugling wuth the same while ago and my fixes was to add HOST in the header and host have to be www.paypal.com. I used fsockopen method and work fine now.
In Curl I had a problem before with ssl. And solution was to put those lines:
curl_setopt($curl, CURLOPT_COOKIEJAR, dirname(__FILE__) . "/cookies.txt");
curl_setopt($curl, CURLOPT_COOKIEFILE, dirname(__FILE__) . "/cookies.txt");
where of course file cookies.txt have to exists.
and more over I had to run one connection to page to get session data and later send post data.
Below is a header what is working fine for me with fsockopen method
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
It's a problem with the + character, it often get wrongly fetched so I made that workaround, and it worked for me.
payment_data = Sat Jun 04 2016 15:11:16 GMT+0200 (CEST)
foreach ($_POST as $key => $value) {
if($key !== "payment_date"){
$req .= '&' . $key . '=' . rawurlencode(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
}else{
$req .= '&' . $key . '=' . rawurlencode(str_replace(array('GMT '),array('GMT+'),$value));
}}
Here's how to avoid these errors...
foreach ($_POST as $key => $value) {
if ($key=='transaction')
foreach ($value as $key2=>$value2) {
$value['transaction'][$key2] = urlencode(stripslashes($value2));
}
else {
$value = urlencode(stripslashes($value));
}
$req .= "&$key=$value";
}
Hours of hair pulling until I saw Izudin's answer. He's right..The + in the date wasn't being transferred. Just to test, I removed it from the pre-populated field in the simulator and got a Verified at last.
I finally found an updated (August 5, 2016) working answer to this query. You can use this code as your final IPN for Sandbox or Live. With the following consideration:
Be sure to place your IPN listener to ->My selling tools -> instant payment notifications Section.
Do not use IPN Simulator in sandbox, it will always return INVALID.
Create and Use an actual Sandbox Button, but DO NOT put your IPN listener to RETURN PAGE that says "Take customers to this URL when they finish checkout".
That's all of it. I hope this will help.
And here is the working code:
<?php
$post_data = file_get_contents('php://input');
$post_array = explode('&', $post_data);
$dataFromPayPal = array();
foreach ($post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$dataFromPayPal[$keyval[0]] = urldecode($keyval[1]);
}
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($dataFromPayPal 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";
}
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
//use https://www.sandbox.paypal.com/cgi-bin/webscr in case you are testing this on a PayPal Sanbox environment
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
curl_close($ch);
exit;
}
curl_close($ch);
if (strcmp ($res, "INVALID") == 0) {
echo "INVALID";
}
else if (strcmp ($res, "VERIFIED") == 0) {
echo "VALID";
}
?>

Categories