The Scenario
I’ve got a PHP script (PHP version 5.2.17) that creates an order record in a MySQL database and then looks at how the user elected to pay for the order (credit card, PayPal, etc) and redirects to the appropriate payment page. As part of the redirection the Order ID is passed as a URL query string so that the relevant order may be retrieved by the payment page.
The payment page grabs the Order ID from the query string and retrieves the relevant order and then goes on to continue processing the order.
The Problem
99% of the time this works fine. But a very small number of times the payment page fails and emails me an error message saying that the order record couldn’t be read and that the Order ID was empty. If I compare the timestamp on the email with the timestamp of records in the Orders table I can see that an order was indeed created at that time and it has an Order ID. The only conclusion that I can come to is that the query string is somehow being altered to remove the Order ID.
The Code
The portion of code that generates the Order ID, saves the record and redirects to the appropriate payment page looks like this:
$orderid = md5(uniqid(rand()));
$schoolid = mysql_real_escape_string($_SESSION['SchoolID']);
$parentname = mysql_real_escape_string($_SESSION['ParentName']);
$email = mysql_real_escape_string($_SESSION['Email']);
$phone = mysql_real_escape_string($_SESSION['Phone']);
$sql = "INSERT INTO orders (OrderID,SchoolID,OrderDate,ParentName,Email,Phone) VALUES " .
"(\\"$orderid\\",\\"$schoolid\\",CURDATE(),\\"$parentname\\",\\"$email\\",\\"$phone\\")";
mysql_query($sql);
if (mysql_error()) {
DisplayMySQLError("Could not create Orders record", $sql, TRUE);
}
switch($paymentmethod) {
case "DPS":
header("Location: payment-dps-send.php?orderid=$orderid");
break;
case "Direct Credit":
header("Location: payment-direct-credit-response.php?orderid=$orderid");
break;
case "School":
header("Location: payment-school-response.php?orderid=$orderid");
break;
}
exit;
And then in each payment page I do the following:
$orderid = isset($_GET['orderid']) ? $_GET['orderid'] : "";
if (!$ordersRow = GetOrdersRecord($orderid)) {
echo "Invalid Order ID";
$msg = "Could not find Orders record for Order ID \\"$orderid\\" in " . $_SERVER['PHP_SELF'];
NotifyWebmaster($msg);
exit;
}
I know there are a few functions here that I haven’t included (ie GetOrdersRecords()) but I don’t think they are at fault.
I get an error saying: Could not find Orders record for Order ID “” in /payment-direct-credit-response.php
The error only seems to occur about 1% of the time, the rest of the time everything works fine. I’ve never been able to cause the error to occur myself, it’s only with live users (who unfortunately I’ve been unable to communicate with). The user usually tries again and everything goes through ok.
Things I’ve Considered
If the payment pages are accessed directly (i.e. not through the order page) then the Order ID won’t be sent in the query string and I will see the behaviour that I’m getting (an error message sent to me saying that there was no Order ID). I can think of a few different ways that the payment pages could be access directly:
[LIST=1][]A user types the URL in directly
[]A search engine or other automated spider is accessing the page
[]A browser add-on is trying to pre-fetch the page to speed up access
[]An anti-virus program is requesting the page so it can check it for malware[/LIST]
I don’t think it’s #1 or #2 because I know that these are real orders because real data is being stored in the database, which tells me that the order page has been accessed first.
I don’t think it’s #3 or #4 because why would they remove the query string before retrieving the page?
I’m now wondering if there is a bug in PHP that sometimes causes it to mis-handle query strings. Or maybe a bad browser that sometimes mangles the query string.
The fact that it works 99% of the time makes me believe that it isn’t my code that is at fault and it’s something outside my control.
I’m using this order setup on 5 different websites, all hosted by the same host, and they all get this error from time to time.
Does anyone have any ideas on what the problem might be?