paypal ipn problem, url malformed (sandbox works, paypal doesn't) - php

I have an IPN file that works perfectly in sandbox. It returns valid or not and executes the code correctly. However if I try real live payments it tells me the url is malformed
This is the error with live payments:
bool(false) string(15) " malformed"
this is the curl code I have:
if($testmode === true)
{
$paypalaction = "https://www.sandbox.paypal.com/cgi-bin/webscr";
}
elseif($paypal == "paypal")
{
$paypalaction = "https://www.paypal.com/cgi-bin/webscr";
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value)
{
// If magic quotes is enabled strip slashes
if (get_magic_quotes_gpc())
{
$_POST[$key] = stripslashes($value);
$value = stripslashes($value);
}
$value = urlencode($value);
// Add the value to the request parameter
$req .= "&$key=$value";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$paypalaction);
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
$res = curl_exec($ch);
var_dump($res);
var_dump(curl_error($ch));
curl_close($ch);

It sounds like you may be posting sandbox transactions to the live URL or the other way around. Make sure that when you try a live transaction, you are doing so with a real, live PayPal account. Double-check your code to ensure that your test for which PayPal URL is working correctly.

The problem wasn't with paypal or sandbox but just me being stupid. i had the following if statement:
if($testmode === true)
{
$paypalaction = "https://www.sandbox.paypal.com/cgi-bin/webscr";
}
elseif($paypal == "paypal")
{
$paypalaction = "https://www.paypal.com/cgi-bin/webscr";
}
But the $paypal variable wasn't used anymore so it never entered the elseif statement. I should have been only an if else:
if($testmode === true)
{
$paypalaction = "https://www.sandbox.paypal.com/cgi-bin/webscr";
}
else
{
$paypalaction = "https://www.paypal.com/cgi-bin/webscr";
}

Related

Testing IPN in Sandbox with MAMP

I’m getting INVALID IPN returns using MAMP for development and testing with the PayPal Sandbox.
I have reviewed a few dozen of the more recent posts that relate to this issue and found no successful advice.
My MAMP setup passed the PayPal test for compliance with TLS 1.2 and HTTP/1.1
I have the May 2019 casert.pem bundle in the same directory as the listener.
All of the transaction variables in the Pay Now button are integers, money_format or urlencoded text.
The transactions are COMPLETED. Sandbox IPN history has been unavailable since I started this test so HTTP code and other data are not available. (Can any one else access IPN history in the sandbox?)
The setup is essentially the same as a live application that has been working on a production site with SSL. So the obvious differences are: MAMP and no SSL (notify_url and return URLs are http and not https). However, I ran similar code in MAMP several months ago without problems.
I get a quick take on the error by holding the listener file open in my browser and refreshing it after I receive the correct ‘return’ PDT data. When live, the code sends emails on errors.
My code—leaving out the application code that runs if IPN verified--is pretty much a copy of the example code.
<?php
$paypal_url = “'https://www.sandbox.paypal.com/cgi-bin/webscr';
$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";
}
$ch = curl_init($paypal_url);
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_CAINFO, dirname(__FILE__) . '/cacert.pem');
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
echo "curl_error($ch)";
exit:
}
if (strcmp ($res, "VERIFIED") == 0) {
echo "VERIFIED";
} elseif (strcmp ($res, “INVALID”) == 0) {
echo “INVALID”;
}
else {
echo “NO IPN”;
}
?>

Paypal IPN always return INVALID - php

I've tried multiple different examples of IPN classes and every time paypal returns with INVALID in the IPN simulator.
End point: https://ipnpb.sandbox.paypal.com/cgi-bin/webscr
I've done some testing, and the data I'm returning to paypal looks the same. e.g.
What paypal is sending:
payment_type=echeck&payment_date=Sun%20Jun%2010%202018%2010%3A17%3A15%20GMT%2B0100%20%28GMT%20Summer%20Time%29&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer#paypalsandbox.com&payer_id=TESTBUYERID01&address_name=John%20Smith&address_country=United%20States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San%20Jose&address_street=123%20any%20street&business=seller#paypalsandbox.com&receiver_email=seller#paypalsandbox.com&receiver_id=seller#paypalsandbox.com&residence_country=US&item_name=something&item_number=AK-1234&quantity=1&shipping=3.04&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&mc_gross_1=12.34&txn_type=web_accept&txn_id=300182286&notify_version=2.1&custom=xyz123&invoice=abc1234&test_ipn=1&verify_sign=undefined
What I'm sending Paypal
cmd=_notify-validate&payment_type=echeck&payment_date=Sun%20Jun%2010%202018%2010%3A17%3A15%20GMT%2B0100%20%28GMT%20Summer%20Time%29&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer#paypalsandbox.com&payer_id=TESTBUYERID01&address_name=John%20Smith&address_country=United%20States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San%20Jose&address_street=123%20any%20street&business=seller#paypalsandbox.com&receiver_email=seller#paypalsandbox.com&receiver_id=seller#paypalsandbox.com&residence_country=US&item_name=something&item_number=AK-1234&quantity=1&shipping=3.04&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&mc_gross_1=12.34&txn_type=web_accept&txn_id=300182286&notify_version=2.1&custom=xyz123&invoice=abc1234&test_ipn=1&verify_sign=undefined
Paypal returned: 'INVALID'
The code I'm currently using is this:
public function verifyIPN()
{
if ( ! count($_POST)) {
throw new Exception("Missing POST Data");
}
$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) {
// 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]);
}
}
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
}
// Build the body of the verification post request, adding the _notify-validate command.
$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 = rawurlencode(stripslashes($value));
} else {
$value = rawurlencode($value);
}
// Added this line to see if it helps
$value = str_replace('%40', '#', $value);
$req .= "&$key=$value";
}
// Post the data back to PayPal, using curl. Throw exceptions if errors occur.
$ch = curl_init($this->getPaypalUri());
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);
// This is often required if the server is missing a global cert bundle, or is using an outdated one.
if ($this->use_local_certs) {
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/cert/cacert.pem");
}
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'User-Agent: PHP-IPN-Verification-Script',
'Connection: Close',
));
$res = curl_exec($ch);
if (!($res)) {
file_put_contents("paypal-errors.txt", "error no: ".$errno($ch)." errorstr: ".curl_error($ch));
$errno = curl_errno($ch);
$errstr = curl_error($ch);
curl_close($ch);
throw new Exception("cURL error: [$errno] $errstr");
}
$info = curl_getinfo($ch);
$http_code = $info['http_code'];
if ($http_code != 200) {
file_put_contents("paypal-errors.txt", "error http status response = ".$http_code);
throw new Exception("PayPal responded with http code $http_code");
}
curl_close($ch);
file_put_contents("paypal-sent.txt", "response: ".file_get_contents('php://input'));
file_put_contents("paypal-result.txt", "response: ".$res);
// Check if PayPal verifies the IPN data, and if so, return true.
if ($res == self::VALID) {
return true;
} else {
return false;
}
}
So at this point, I'm assuming there is something wrong with my environment. My server has TLS 1.2 and 1.3. I've ran out of ideas for troubleshooting. Any help would be greatly appreciated!

How can I prevent paypal duplicate payment using txn_id?

So I am using PHP cURL and PayPal IPN.
I don't want my customer to pay twice by mistake. I have heard that I can do that using txn_id but I don't know how.
Here is my code ...
<?php
// connecting to database
// Prepare the URL to send via cURL
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
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";
}
// Initial cURL
$ch = curl_init();
// Set opt
curl_setopt($ch, CURLOPT_URL,"https://www.paypal.com/cgi-bin/webscr");
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);
// Return result
$result = curl_exec($ch);
// Close cURL connection
curl_close($ch);
// If condition
if($result == "VERIFIED"){
// perform database update
}
?>
How can I prevent the buyer from paying more than once?
The code you provided here is for PayPal IPN, which I think could help little to avoid duplicated payment. Instead, I recommend you adding invoice into your payment data. As PayPal system by default blocks payments with same Invoice ID, I hope this could help you avoid your customers from paying twice by mistake.
If you are integrating PayPal Payment Standard, please check below page for variable list.
https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
If you are integrating PayPal Express Checkout, please check below pages.
https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPayment_API_Operation_NVP/

PayPal IPN returning INVALID even after payment is sent

I am developing a shopping cart application, and in that application, I decided to integrate PayPal IPN. However, it keeps returning INVALID. It successfully transfers money and is deducted from my account and moved to the buyer's account.
if ( ! count($_POST)) {
throw new \Exception("Missing POST Data");
}
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = [];
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]);
}
}
$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);
}
$req .= "&$key=$value";
}
// Post the data back to PayPal, using curl. Throw exceptions if errors occur.
$ch = curl_init(self::VERIFY_URI);
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);
// This is often required if the server is missing a global cert bundle, or is using an outdated one.
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/cert/cacert.pem");
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Connection: Close']);
$res = curl_exec($ch);
$info = curl_getinfo($ch);
$http_code = $info['http_code'];
if ($http_code != 200) {
throw new \Exception("PayPal responded with http code $http_code");
}
if ( ! ($res)) {
$errno = curl_errno($ch);
$errstr = curl_error($ch);
curl_close($ch);
throw new \Exception("cURL error: [$errno] $errstr");
}
curl_close($ch);
// Check if PayPal verifies the IPN data, and if so, return true.
if ($res == "VERIFIED") {
return 'success';
} else {
return print_r($res, true);
}
My inputs and outputs are exactly the same. However, this particular key is different:
INPUT: [payment_date] => 2016-12-09T15:07:18Z
OUTPUT: payment_date=2016-12-09T14%3A12%3A21Z (After the query is made).
I encountered this same error yesterday. Try the following:
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
if (strcmp($res, "VERIFIED") == 0) {
return "Success";
}else if (strcmp($res, "INVALID") == 0) {
//deal with invalid IPN
//mail admin or alert client
}
PayPal IPN variables are already encoded and there is no need to do it twice, replace with this code below
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()){
$varvalue = urlencode(stripslashes($varvalue));
}
else {
$value = urlencode($value);
}

Retrieving JSON Values

I am working with a php payment api https://simplepay4u.com/developer.php
I have followed every step successfully but can't seem to get return values to generate a receipt after payment. I am stock not understanding the what the unotify page should be doing.
Here is my form which is posting properly:
$action = $_POST["action"];
$pid = $_POST["pid"];
$buyer = $_POST["buyer"];
$total = $_POST["total"];
$comments = $_POST["comments"];
$referer = $_POST["referer"];
$customid = $_POST["customid"];
$transaction_id = $_POST["customid"];
$val = #json_encode($_POST);
// For Loging $val can be used
if ($referer == "https://simplepay4u.com"
|| $referer == "https://www.simplepay4u.com"
|| "http://sandbox.simplepay4u.com/process.php") //1st level checking
{
if (!empty($transaction_id)) //2nd level checking
{
/*** Server side verification. Your server is communicating with Simplepay Server Internally**/
//$simplepay_url="https://simplepay4u.com/processverify.php"; // Live URL
$simplepay_url = "http://sandbox.simplepay4u.com/processverify.php";
$curldata["cmd"] = "_notify-validate";
foreach ($_REQUEST as $key => $value) {
if ($key != 'view' && $key != 'layout') {
$value = urlencode($value);
$curldata[$key] = $value;
}
}
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $simplepay_url);
curl_setopt($handle, CURLOPT_POST, 1);
curl_setopt($handle, CURLOPT_POSTFIELDS, $curldata);
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_TIMEOUT, 90);
$result = curl_exec($handle);
curl_close($handle);
var_dump(json_decode($result));
enter code here
if ('VERIFIED' == $result) {
// IMPLEMENTATION CODE & aPPLICATION lOGIC WILL GO HER TO UPDATE THE ACCOUNT
}
}
}
I have set my ureturn page to carry the above script and tried var_dump(json_decode($result)), print_r($result) and echo $result; Just to see something but I get the result.
Can someone please tell me what to do to get my return values after posting my already working form?

Categories