I've been at it for 24 hours now and am about to pull my hair out. I've checked all over the Web and within Stackoverflow and can find nothing that helps. I've even checked previous posts such as Not receiving a response from Paypal IPN Sandbox but the responses aren't working for me. Perhaps I've missed the post that'll help me; if I have, please excuse me for repeating a same question, but this matter is rather urgent and I'm at the end of my rope.
Anyway, the issue is this:
I have a PHP cart going and it sends clients to Paypal. Everything is working perfectly. I've built it according to Paypal's instructions when it comes to sending all codes to them. Everything fine except when it comes to the final step, the return POST
from Paypal back to my server to check everything's OK. Even there I've copied the entire Paypal code as is and just added the functions that'll check everything matches on my part. However, I can't get it to work. I try sending the results to a DB and nothing happens. So I placed a PHP Mail
command right where the last action should be if everything's OK and I don't receive the E-Mail. I've placed this same Mail
command almost everywhere else to try and find out what's going on and have received different clues from different places.
First, when I call to print into the E-Mail the vars Paypal's supposed to send back to my server, I get the E-Mail with all vars except for item_name
and item_number
, which I get blank; this is strange, because up to that point Paypal Sandbox is showing me every single product I'm supposedly purchasing. I tried giving these vars on the PHP code a fixed value in light of the fact that Paypal is not posting a value to them, at least at that point. I wanted to see if the lack of these two values was the reason for the error; the error persisted, however, even with the set values.
Second, there's a while
loop in the Paypal code (the loop that a response in the link given above says one should replace with $res=stream_get_contents($fp, 1024);
, this, however, didn't work for me either), so I placed the Mail
command in there to see what I got back. I asked to print the $res
var and the payment_status
and got, as expected, several E-Mails, the collected results of which are as follows:
payment_status = Pending
$res = HTTP/1.0 302 Found
Location: https://www.sandbox.paypal.com
Server: BigIP
Connection: close
Content-Length: 0
So I'm guessing the error is occurring at this point. Something is happening (or not happening) which is not only not returning the Completed
status for payment_status
that the code looks for in order to continue checking and etc., but it's not even returning an INVALID
response. So I'm getting no response and nothing to show me whether everything's OK or not. And, what's strange, and even worse, is the fact that Sandbox is still fake-charging me for every single one of these payment attempts, even though it's clear that there's an error somewhere and the process is not being fulfilled. (This, of course, brings out another dilemma: once all this is solved, if there is a server error at some point for X reason, will Paypal still charge my client and I won't even know somebody has bought something on my Website?)
Well, I suppose that's as much as I can explain on this matter. At this point I can't see where I could have gone wrong. The code should make this a pretty straightforward thing. So really I don't see how I could have messed up the copy & paste. I suppose that at least should have no errors, unless they come with the original code. Moreover, because everything else to that point is working 100% fine, the only place where this error could be is within this PHP file Paypal has to call once the payment process is completed by the user.
ANY idea on the matter will be EXTREMELY appreciated!
Here goes the code.
PHP CODE:
include('../inc/db_fns.inc'); //<--All my DB work is in here.
include('../inc/shipping.inc');
$paypal_email = "seller_1360198925_biz@hotmail.com";
$paypal_currency = 'USD';
//Here begin the functions I'm using to check everything and pass data to DB.
function no_paypal_trans_id($trans_id) {
$connection = db_connect();
$query = sprintf("SELECT id FROM orders WHERE paypal_trans_id = '%s'", mysql_real_escape_string($trans_id));
$result = mysql_query($query);
$num_results = mysql_num_rows($result);
if($num_results == 0) {
return true;
}
return false;
}
function payment_amount_correct($shipping, $params) {
$amount = 0.00;
for ($i=1; $i <= $params['num_cart_items']; $i++) {
$query = sprintf("SELECT precio FROM products WHERE id='%s'", mysql_real_escape_string($params["item_number{$i}"]));
$result = mysql_query($query);
if($result) {
$item_price = mysql_result($result, 0, 'precio');
$amount += $item_price * $params["quantity{$i}"];
}
}
if(($amount+$shipping) == $params['mc_gross']) {
return true;
} else {
return false;
}
}
function create_order($params) {
db_connect();
$query = sprintf("INSERT INTO orders set orders.firstname = '%s', orders.lastname = '%s', orders.email = '%s', orders.country = '%s', orders.address = '%s', orders.city = '%s', orders.zip_code = '%s', orders.state = '%s', orders.status = '%s', orders.amount = '%s', orders.paypal_trans_id = '%s', created_at = NOW()", mysql_real_escape_string($params['first_name']), mysql_real_escape_string($params['last_name']), mysql_real_escape_string($params['payer_email']), mysql_real_escape_string($params['address_country']), mysql_real_escape_string($params['address_street']), mysql_real_escape_string($params['address_city']), mysql_real_escape_string($params['address_zip']), mysql_real_escape_string($params['address_state']), mysql_real_escape_string($params['payment_status']), mysql_real_escape_string($params['mc_gross']), mysql_real_escape_string($params['txn_id']));
$result = mysql_query($query);
if(!$result) {
return false;
}
$order_id = mysql_insert_id();
for ($i=1; $i <= $params['num_cart_items'] ; $i++) {
$product = find_product($params["item_number{$i}"]);
$query = sprintf("INSERT INTO items set order_id = '%s', product_id = '%s', title = '%s', price = '%s', qty = '%s'", mysql_real_escape_string($order_id), mysql_real_escape_string($product['id']), mysql_real_escape_string($product['title']), mysql_real_escape_string($product['price']), mysql_real_escape_string($params["quantity{$i}"]));
$result = mysql_query($query);
if(!$result) {
return false;
}
}
return true;
}
//Here begins the Paypal code as is
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// post back to PayPal system to validate
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30); //<--Here I also tried out the other response to the question linked above and it actually returned a 'Invalid host' into he $res var-
// assign posted variables to local variables
$item_name = $_POST['item_name']; //<--These are the two vars for which
$item_number = $_POST['item_number'];// Paypal Sandbox posts no value.
$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 (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
if ($_POST['payment_status'] == 'Completed' && no_paypal_trans_id($_POST['txn_id']) && $paypal_email == $_POST['receiver_email'] && $paypal_currency == $_POST['mc_currency'] && payment_amount_correct($shipping, $_POST)) {
// process payment
create_order($_POST);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
}
fclose ($fp);
}
EDIT 1: Gave it another go: No luck!
I've just tried the original, untouched Paypal code. I've just copied and pasted it into this PHP and then run the Sandbox buy. Same outcome. No response, no data inserted into tables, no way of me knowing somebody has actually bought something off my cart, but Paypal still fake-charges the buy. This supports my suspicion that the error comes in the original code and has nothing to do with the functions I've added. Hope somebody can make sense of this soon. Thousand thanks in advance to that person!
EDIT 2: Following 1st response.
A kind user who has now deleted his/her response was kind enough to give it a first try. He/she recommended that the Paypal documentation mentions https://www.sandbox.paypal.com/cgi-bin/webscr
where I have http://www.sandbox.paypal.com
. Perhaps I have an older version of the code, so I tried out his/her recommendation. Now I comment what happened:
First of all, thanks a lot for your response. Unfortunately, no luck with it. Having substituted one address with the other, now the process doesn't get to the while
loop, it stops at the fsockopen
. I placed a Mail
command to print out the $errno
and $errstr
contained in that command. With https://www.sandbox.paypal.com/cgi-bin/webscr
substituting http://www.sandbox.paypal.com
I get Unable to find the socket transport "https" - did you forget to enable it when you configured PHP? So I stripped the "https://" and tried again. Now I get php_network_getaddresses: getaddrinfo failed: Name or service not known. Any ideas?
EDIT 3: Following my conversation with @hexacyanide in the comments section:
$req = cmd=_notify-validate&test_ipn=1&payment_type=instant&payment_date=20%3A55%3A22+Feb+08%2C+2013+PST&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&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%2C+any+street&receiver_email=seller%40paypalsandbox.com&receiver_id=TESTSELLERID1&residence_country=US&item_name1=something&item_number1=AK-1234&quantity1=1&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross_1=9.34&mc_handling=2.06&mc_handling1=1.67&mc_shipping=3.02&mc_shipping1=1.02&txn_type=cart&txn_id=2229455¬ify_version=2.4&custom=xyz123&invoice=abc1234&charset=windows-1252&verify_sign=AsdNkKD2ktCz.aUB.9WYWy-g8MHoAa-TsvSjUgGstseJVdUhQTq3aCwW
Sorry, that was $req
, not $res
var. The $res
var is returned by the while
loop. I placed a mail
command there and I get several E-Mails with the $res
values. These are:
X-Frame-Options: SAMEORIGIN
HTTP/1.0 200 OK
Strict-Transport-Security: max-age=14400
INVALID
Content-Type: text/html; charset=UTF-8
Date: Sat, 09 Feb 2013 05:44:33 GMT
Connection: close
Content-Length: 7
EDIT 4: Absolutely dumbfounded: Not even simple Paypal IPN code straight from the Paypal Developer website and tested with the Paypal Developer website IPN Tester works...
- I've used the Paypal IPN script generator found here.
- I chose to generate a script that would mail back to me a VALID or INVALID response.
- I copied it in its entirety and pasted it into a PHP file I saved in my server.
- I checked and re-checked that no part of the code was left behind.
- I targeted this PHP file on my server from the IPN Simulator found here.
- Simulator shows a Check and says "IPN successfully sent."
- I go to check my mail to see what response I got, whether it was VALID or INVALID... I got no response at all. So the IPN reaches the PHP buy at some point the process is broken so that it doesn't even get to the VALID/INVALID functions. However, this is the very code Paypal gives you and is tested with the very sim it has designed for it. So what's going on?
DAY 2 OF THIS ODYSSEY
O.K. Needless to say: no luck yet. But I've now decided to exhaustively track everything that's going into the PHP code and out to Paypal. The idea is that a response of VALID
occurs when this code sends back to Paypal those initial values Paypal inserted into it and in the very same order (this according to Paypal documentation).
The first issue is that I was receiving no response at all from the code. So the supposition was that the code was failing at the very start. It happened that the first issue was that the Sandbox links do no work. (I got to this point with the aid of @hexacyanide, who was very kind to follow along yesterday with my progress or lack thereof. Whenever I get enough reputation to positively mark his/her response, I will.) So I stripped off the sandbox.
and, voilà, it was returning a response, but now the response was INVALID
.
From that time to now I've understood that IPN is just for seller-side transaction checking and data logging. Paypal doesn't care about the outcome of the IPN. So my cart IS working 100%. A user can buy something and Paypal will charge the user for it. The IPN will only check for me if the buy info is in fact coming from Paypal and all is well there and will then (in my script) send all this info into a DB so I can fulfill my side of the service. So, even though the transaction happens successfully without IPN, IPN would be an invaluable mechanism to facilitate the overall selling process. However, all this last info settles my suspicion that all my cart's code is O.K., and that the issue is ONLY here, with this code, or the way Paypal is handling it. (The fact that Paypal's "virgin" code passed through Paypal's own IPN Simulator returns no response, but the same error at the initial state of not being able to connect to the the Paypal server makes me inclined to think that the issue has to do with something wrong on Paypal's side.)
So, the log of everything going into and out of this PHP. I located Mail
commands at every single significant point in the code in order to create checkpoints. Inside the foreach()
command I included an incremental variable in order to have and ordered list of the POSTED data coming in. And this is what I got:
This is the data being POSTED into the code by Paypal (in the order it's being POSTED)
test_ipn=1
payment_type=instant
payment_date=08%3A42%3A23+Feb+09%2C+2013+PST
payment_status=Completed
payer_status=verified
first_name=John
last_name=Smith
payer_email=buyer%40paypalsandbox.com
payer_id=TESTBUYERID01
business=seller%40paypalsandbox.com
receiver_email=seller%40paypalsandbox.com
receiver_id=TESTSELLERID1
residence_country=US
item_name1=something
item_number1=AK-1234
quantity1=1
tax=2.02
mc_currency=USD
mc_fee=0.44
mc_gross=15.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=23291642
notify_version=2.4
custom=xyz123
invoice=abc1234
charset=windows-1252
At a glance, I seem to be getting all the data I should from Paypal. And this is the order in which PP sends it. So if I were to send this data in this order back and PP actually does check it for this same order, then I should, in theory, get a VALID
response.
So what the code does is add "cmd=_notify-validate", which is the only addition (the PP documentation makes it clear that this is necessary addition is necessary), and passes all this data into a var called $req
in this way: A=X&B=Y&C=Z...
.
Here is the message from the next checkpoint, added just after the foreach()
command closes.
$req = cmd=_notify-validate&test_ipn=1&payment_type=instant&payment_date=08%3A42%3A23+Feb+09%2C+2013+PST&payment_status=Completed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&payer_id=TESTBUYERID01&business=seller%40paypalsandbox.com&receiver_email=seller%40paypalsandbox.com&receiver_id=TESTSELLERID1&residence_country=US&item_name1=something&item_number1=AK-1234&quantity1=1&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=15.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=23291642¬ify_version=2.4&custom=xyz123&invoice=abc1234&charset=windows-1252&verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb
Note the verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb
at the end. I can't account for that piece of data because it was not spit out in the form of an E-Mail during the foreach()
loop. So where does it come from? Could this be the culprit piece of data that returns the INVALID
response? Open question. But at this point, all this data is passed by the code at the "DATA COMING IN" checkpoint; so if this data is what's coming in and my code sends it back out, then everything should be fine.
So the next checkpoint will be "DATA COMING OUT." I located this checkpoint right before the fputs ($fp, $header . $req);
code which writes $req
back to PP. And this is what is coming out of the code:
$req = cmd=_notify-validate&test_ipn=1&payment_type=instant&payment_date=08%3A42%3A23+Feb+09%2C+2013+PST&payment_status=Completed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&payer_id=TESTBUYERID01&business=seller%40paypalsandbox.com&receiver_email=seller%40paypalsandbox.com&receiver_id=TESTSELLERID1&residence_country=US&item_name1=something&item_number1=AK-1234&quantity1=1&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=15.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=23291642¬ify_version=2.4&custom=xyz123&invoice=abc1234&charset=windows-1252&verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31ACyRxUQ6LVDwUz.i78mjQLsN9aKb
HUM! Am I missing something? Are my eyes skipping over the tiniest hiccough that makes this code trip? The output seems to be exactly the same as the input. Let's assume that PP worked up their code fine, that the POSTED data is turned into a string in a way that PP expects and, therefore, checks for... assuming all this, what could be wrong?
Let's see... again: open questions:
- Could it be an issue with the header? According to documentation, this is the header one should use... so unless they're off on this, then it should work fine.
- Could it be an issue with the URL given? O.K., this IS in fact an issue, or rather WAS at some point. There are at least 6 or 7 different URLs running around the Internet which are supposed to work and not work. Some have worked for some people and not for others, some seem never to work, etc. Not even PP themselves are sure what URL is supposed to be used, as in their own documentation it varies. What is clear is that using the
sandbox.
URLs will return nothing--one is to suppose that these are broken. - Could it be an issue with the charset? Could there be an incompatibility? This just occurred to me as I was seeing the data come in and out with Hex codes. The thing is that this should make it compatible, right? However, yesterday, as I was checking what was going on in the
while()
loop, and catching the info sent back from PP into the$res
var I wasn't getting Hex codes, as you can see further up in this testament.
The next checkpoint is the classic one at if()
VALID and if()
INVALID. And, of course, I get the one that tells me it all came out INVALID, once again.
Hopefully this detailed info I'm giving will make someone's light bulb shine brightly! As for me, I'm still dumbfounded.
Thanks in advance!
THANKS A LOT TO BOTH OF YOU FOR YOUR RESPONSES!
I haven't tried the second one out, though it sounds pretty sensible. Sounds like that could have been the issue with the original code I got from PayPal. If I have any time to try it out and see if the code works with it, I'll post my feedback here. Hopefully someone will find all this useful.
After a week banging my head against the walls and a little time off to cool the issue in my brain, I have finally, a couple of days ago, found another way to solve the issue somewhere in the Web which is pretty similar to the second part of Hexacyanide's response. I'm posting it below for anyone who's following this string. However, I must say, the PayPal code is pretty buggy. At least the one I received. I have since had several other issues I've had to solve via workarounds. An the worst part is that PayPal has no support AT ALL. I even called three times: the first, the person hung up on me; then, they told me it could be a virus issue (typical response when they don't really know what's going on).
Anyway, I wish I could rate both your responses but I need more than +15 points for that, so I'll praise them verbally here. I tried out Hexacynide's response code as is but it didn't work or me, so I can't mark it as checked. If I have time to try out the other in the future, and it works, I'll mark it as correct.
Until then, here is my code update using CURL.
$ch = curl_init('https://www.paypal.com/pe/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);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
NOTE: Here it's targeted to the real thing because it's now up and running, however, using it with sandbox.
works as well.