Forum Moderators: coopster
I set up a loop ( using php sleep() ) to repeatedly check my DB for completed processing. The problem is that the script doesn't output ANYTHING until it gets completed processing info from the DB - i.e. the user sees a blank page.
I'm looking for a solution that allows me to display a "processing your order..." message before it completes.
Any ideas?
The other option would be to load a new page that just says "processing your order..." then when you get your response load another new page with the results. AJAX is what you want to just add an image and div saying processing.
[edited by: PHP_Chimp at 5:33 pm (utc) on Oct. 31, 2007]
I don't know AJAX, but I just looked it up and will consider it.
I was thinking along the lines of your second suggestion, i.e. having two separate pages, one "processing..." and one with the completed info.
It's probably simple to code, but checking for completed data transfer and/or the completed check-out page is beyond my limited scripting skills.
Can you either give me some code or name relevant php functions that I can look up in the manual?
As you could put
header('Location: http://example.com/processing');
at the end of your payment page.
Then have your loop and checking on the processing page.
Finally when payment is recieved use -
header ('location: http://example.com/we_have_your_money');
$sql = // query DB for data;
for ( $i=0; $i<60; $i++ ) {
if ( // DB result )
{
break;
}
else
{
sleep(1);
}
}
I know how to use header(), but the glitch (if I understand your idea correctly) is that if the checking loop is on the processing page, it doesn't display ANYTHING (i.e. "processing your order...") until it gets a successful DB result, which could be up to a minute. So the user stares at a blank page until then.
Here's a simple 'please wait' page:
<head>
<script type="text/javascript" src="mootools.js"></script>
<script type="text/javascript">
window.addEvent('domready',init);
xhr = null;function init() {
checking = check.periodical(10000); // number of milliseconds, so this checks every 10 seconds
$('stopper').addEvent('click',function() { $clear(checking); }); // this is just to stop it and you probably wouldn't have it
}// End init()var check = function() {
if(xhr == null)
xhr = new XHR({method: 'get',onSuccess: receive});
// Here's where we send the request - id gets passed as a parameter; I'm assuming you need to pass something
// so the checking script can look it up in the database. You could alternatively set it as a SESSION variable and leave
// the second parameter off entirely.
xhr.send('http://www.example.com/checkfinished.php','id=1');
}// End check()function receive() {
$('msg').setHTML(xhr.response.text); // This just echoes the response, and could be skipped.
// This is what you'll be interested in modifying - doing something relevant when this if() is true.
if(xhr.response.text == 'OK') {
$clear(checking);// This turns off the 10 second check.
} // EndIf the wait is over
}// End receive()
</script>
</head>
<body>
<div id="msg">Waiting</div>
<button id="stopper">Stop waiting</button>
</body>
Then the checkfinished script, which would, in reality, check for completion instead of playing with a counter:
<?php
session_start();
if(!isset($_SESSION['count']))
$_SESSION['count'] = 1;
if($_SESSION['count']++ < 3)
echo "Wait some more" . str_repeat('.',$_SESSION['count']);
else {
echo "OK";
$_SESSION['count'] = 0;
}
?>
mootools is set up to be modular. Go to the mootools.net website, click the download link, leave core checked, and put a check by Window.DomReady and XHR (checking those will cause pertinent dependencies to be checked as well). Then download it and name it mootools.js.
I agree with the two-paged approach. Where the receive() function checks for 'OK' you'd redirect with something like
location.href="http://www.example.com/complete.php";
I implemented your scripts as best I could, but have a some glitches. When I go to my processing page, it displays normally for (what I assume is) the time of 1 check cycle (i.e. 10 sec. in your version). Then, whether it's got an 'OK' or not from the result script, it loads the result page IN the processing page (like an i-frame) rather than simply redirecting.
Also, I'm not clear on how to pass the variable from the processing script to the result script. I'm using a php $_SESSION['hash'] for tracking. How do I pass a php var in your javascript line:
xhr.send('http://www.example.com/checkfinished.php','id=1');
I tried:
<?php
echo "xhr.send('http://www.example.com/checkfinished.php','id=".$_SESSION['hash']."');";
but it doesn't seem to work.
Last, how do I code in the 'OK' message in the result script? Basically, the processing script is waiting for a DB result. I added 'echo "OK";' after getting the result, but: a) it doesn't work, and b) when the user redirects to the result page and the script runs again (as I assume it will), doesn't the user see "OK" along with my configured display?
I forgot to even refer to it in the checkfinished example that I posted. For your purpose, you can just retrieve it from $_SESSION['hash'] again.
I was doing an echo on the 'please wait' page to illustrate the status of the transfer. See how the checkfinished script I have has absolutely no HTML in it? That's the entire file - if you were to point your browser directly at it you'd see a very plain:
Wait some more
in your browser, nothing else. Refresh 3 times and you'd finally see
OK
It sounds like your checking script is sending back a whole legitimate HTML document. You need to design a checkfinished script that's more along the lines of what I posted - something sort of like this:
<?php
$qry = mysql_query("SELECT status_field FROM some_table WHERE id={$_SESSION['hash']}");
$status = mysql_fetch_row($qry);
switch($status[0]) {
case 'failed':
echo "Failed";
break;
case 'pending':
echo "pending";
break;
case 'complete':
echo "OK";
break;
}// EndSwitch status
exit;
?>
That's the whole thing, nothing outside the php tags. It's not meant to be a standalone visitable-by-browser html document generator, all it does is produce a text response which gets sent back through the XHR transfer.
Then, the receive function in your more pleasant 'we're waiting' script would do:
function receive() {
/* we don't need this anymore
$('msg').setHTML(xhr.response.text); // This just echoes the response, and could be skipped.
*/
// This is what you'll be interested in modifying - doing something relevant when this if() is true.
if(xhr.response.text == 'Failed') {
location.href="failed_page.php";
} // EndIf go tell them the bad news
else if(xhr.response.text == 'OK') {
location.href="completed_page.php";
} // EndIf go tell them the good news
// returned status is pending so we do nothing - we wait some more
}// End receive()
Is that any clearer? The waiting page is kind of like the guy standing behind the counter smiling at the customer while he's got one eye on the credit card scanner watching for one word, either 'approved' or 'denied', to go flashing across the little LED display. The checkfinished script's whole job is to supply that one word. The AJAX paradigm is a bit alien at first but once you get the hang of it, it's pretty dadgum cool.
if(xhr.response.text == 'OK') {
location.href="good.php";
}
Is there a way to print what the wait page is getting as a variable (if anything) so I can see it?
I use FireFox with the Firebug extension (which shows you everything) and only use IE when I have to to make sure everything will work (which is rare). My IE6 pops up a dialog box when there's an error, showing the line on which the error occurred. Last night it turned out that it was complaining about the line before - only took me a couple of hours to figure that out.
Easiest way to see a variable's contents is to use the alert function:
alert(xhr.response.text);
xhr.send('http://www.example.com/checkfinished.php');
If your browser URL is http://example.com/waiting.php (w/o the www.) the send command for some reason glitches switching over to http://www.example.com...
I resolved it by using:
xhr.send('checkfinished.php');
which seems to work. Anyway, that only stopped the 'Error on Page' message; it was not the problem with the script.
The problem was that on checkfinished.php, I had removed the exit after each echo. Even though no more text was added to my one-word response, some spaces, line-breaks, or other invisible characters WERE added, which caused the conditional to fail. This is despite the fact that my code was simply echo "OK"; with no other output after. When I put back the exits, it worked.
So, now that it's working, a couple questions:
1) When checkfinished gets no DB result, I have it not do anything. Is it better to send back echo ''; -- seems to work both ways.
2) I want the script to timeout after 60 sec. if it hasn't gotten a DB result, then redirect to a page. After the 'checking=' line in function init(), I added:
setTimeout('location.href="page.php"', 60000);
It works, but let me know if there's a reason to do it differently.
1) Personally I think I'd echo back 'pending' if that's what no result means, but it's up to how you want to design it - if you've got it working and are happy with how it works then there you go.
2) That's likely how I'd do it, too (but I am a php guy and just a hack when it comes to javascript :) )
I've been testing the script today and it works great under every condition. I still need to test a few more things, but I don't anticipate any problems.
Thanks a lot for all your help -- I really appreciate it.
For future reference in case I want to use the script in other places and need that 'id' var in the send command, how would I read that and use it in the php of checkfinished?
On the javascript side it depends on where it came from, could be hard-coded, a form value, or passed from php.
If you're using php to assemble the page, for example, you could do something like this:
<script type="text/javascript">
id=<?php echo $id;?>;
</script>
then the send command looks more like:
xhr.send('http://www.example.com/checkfinished.php','id='+id);
If you're sending it back on the url like I did in that first post and above, then in the checkfinished script it's available in the $_GET superglobal:
$id = intval($_GET['id']); // Get id from url, intval() insures it's an integer - a little bit of prankster protection
On the javascript side you can also send the request as a post (like a form submission), making it available in the $_POST superglobal:
$id = intval($_POST['id']);
I also have a checkout page on my site and am similarly trying to implement a "Please Wait" page that displays while processing occurs in the background. Once the processing finishes, I want the user redirected from the "Please Wait" page to the "Results" page.
I found this thread exactly what I needed and have implemented the structure and various code as discussed. It does works but, in my case the java does not execute until AFTER the processing is completed. In other words, once the user clicks "Pay Now" they are shown the "Please Wait" page but the browser displays an hourglass as the mouse cursor and indicates the page has not yet finished loading. It then waits in this condition while processing occurs. Once processing finishes, the "checkfinished.php" script is pinged by the java (in the "Please Wait" page) for the first time and returns the "OK response (since processing is now finished). It then does properly redirect to my "results" page.
I would rather have the "Please Wait" page appear as if it is done loading (returns cursor to user, indicates Done in status bar) while "checkfinished.php" is periodically being pinged to check if processing is finished (as I believe you originally intended in your posts above). Do you have any thoughts on why I might be getting this behavior?
The basic structure I am using to call the "please Wait" page and start processing is shown below:
session_start();
# Set variable to check for completion
$_SESSION['ProcessingStatus'] = 'NotYetDone';
# Display "Please Wait" page (with javascript you outlined above)
include_once ("PleaseWaitPage.php");
<-- Start processing stuff here -->
<-- At this point the java should be pinging checkfinished.php but it is not -->
<-- End processing stuff here -->
# Set variable to indicate finished
$_SESSION['ProcessingStatus'] = 'Finished';
<-- Browser now indicates Done -->
<-- Java in "Wait Page" pings checkfinished.php for first time here -->
Any assistance would be appreciated.
If your background processing is done on your server, you would need to engineer the events such that script 1 finishes processing and sends the output to the browser - this would be up to and including your PleaseWaitPage. PleaseWaitPage could send an ajax request back to the server to begin the secondary processing in script 2 (your three comments after the include). When script 2 is finished it can send a response back to the ajax requestor. The problem there is if the processing takes longer than the request object is willing to wait.
If by background processing you mean your script is waiting for a response from another server, e.g. a payment processor, script 1 can initiate the payment process and terminate, sending the wait message (PleaseWaitPage) to the browser. You would set up a second script to catch the payment response, and a third script (checkfinished in the previous posts) to service PleaseWaitPage's periodic ajax requests.
I am charging a credit card and the processing does go to another server but, I am using a provided function which basically handles all that (using Curl) and reduces it down to a function for me. I basically perform the processing with:
$ProcessResultVariables = ChargeCreditCard($ProcessSubmissionVariables);.
Thus, it does hang and wait until $ProcessResultVariables is resolved. I don't want to mess with the details of the function so as far as I am concerned the processing is on my server (although not technically). Because of this, I do not think your final suggestion above is an option for me.
I am considering your second option but:
1) Wouldn't this structure make the processing dependent upon my user having javascript enabled? If javascript is not enabled, would he wait indefinitely at the PleaseWait page? Or, do you feel everyone has javascript enabled these days and no need to worry?
2) I am using a session variable for checkfinished.php to monitor. I change its value once processing is finished. The next time checkfinished.php is pinged it responds with "OK" to cause the final redirect to the "Results" page. So, the request to start the secondary processing would not necessarily need to wait for a response, correct? It merely needs to start script 2. Would you agree that with this structure I would not need to be concerned with the request objects willingness to wait?
3) I am not a ajax person so could you tell me what the code would look like to start the secondary processing in script 2 (code located in Please Wait page), assuming my approach above seems valid?
execution will not continue to the next line in your script until whatever ChargeCreditCard is doing is finished. You need the results from the function anyway; if you leave there's no place for the results to go when it's finished.
Your payment gateway doesn't provide a process that can notify your server asynchronously when the transaction is complete?
For clarity, what I was proposing was:
Script 1:
1) Sets session variable (for checkfinished.php to monitor, initially indicates processing not finished)
2) Displays Please Wait Page
3) includes javascript you first posted in this thread, which causes periodic execution of checkfinished.php (to monitor session variable for completion)
4) Needs to somehow initiate execution of script 2 (what I do not know how to do)
5) Ends (does not wait for any details from script 2)
Script 2:
1) $ProcessResultVariables = ChargeCreditCard($ProcessSubmissionVariables);.
2) When finished, stores $ProcessResultVariables as session variables (so available for "Results" page)
2) Updates session variable to indicate processing now complete
3 Ends - No HTML
Script 3 (checkfinished.php):
1) Periodically being executed by javascript in Script 1
2) Eventually notices session variable indicates processing complete which causes javascript in Script 1 to redirect to "Results" page.
Results page:
1) Retrieves session variable stored by Script 2
2) Displays details of processing to customer
This structure is what I was trying to propose. Now I am thinking that all along you had intended for the processing to be started with first (and only first) execution of checkfinished.php. Is that correct?
I'm not intimately familiar with the gritty details of how server software works, so if I explain this wrong someone please correct me. I'm assuming you're working in a shared hosting environment, which is the only server technology I've used.
Think of the server as if it were an information booth with one guy at the window. The guy has one job: the exchange of information. He only does one thing at a time and when no one's at the window he does nothing. A browser is just like a person who steps up to the window and asks for or hands the guy information. Browsers are also like very impatient people - if the guy doesn't come back with the info quick enough, the browser steps away from the window (times out) so guess what? The guy goes back to doing nothing (because no one's at the window). If your script tells the booth guy to tell the browser to hold on, that's it - that's the 'one thing at a time'. The browser steps away from the window so the guy goes back to doing nothing.
See how you're painted into a corner? You can't initiate the payment in script 1 because you can either begin the transaction or tell the browser to wait. Once you've told the browser something it steps away from the counter and you can't keep it from doing that.
So the only way to do it within your circumstances is to send pleasewait to the browser. pleasewait immediately (onload) asks the server another question, which is to perform the transaction. The server doesn't respond until the transaction is complete, so it holds the browser at the window. However, like I said previously, the caveat is that the browser (via the ajax request) may get impatient and walk away. That, unfortunately, will likely cause the server to drop the conversation with the payment processor. Even if it doesn't, the booth guy has dropped the phone and gone back to twiddling his thumbs at the window so he won't hear the processor shouting either "OK" or "Denied" into the phone.
From what you've said, ChargeCreditCard() is a synchronous function. That means you can't do anything else, you're stuck waiting for it, otherwise you're no longer in sync. The first 'A' in AJAX stands for asynchronous. Using pleasewait to initiate the processing script is just delaying the synchronous transaction; you don't actually have to use AJAX to do it. For example, you could use an iframe with the source set to script 2, or you could even use an image tag - it just needs to be something that asks the server another question so a script has a chance to respond.
The only other way I could see you emulating an asynchronous transaction is if you created a cron job to initiate the transaction, then dispose of the cron job once it's finished. There, though, you'd lose the convenience of $_SESSION - you'd have to store the results in a database or file with a unique identifier of some sort. I've never actually done a cron job so I can't help you there.
Again, you've looked, and your payment processor doesn't have an asynchronous or unmonitored solution for you to use?
You know, I bet I had this working like sssweb's implementation on my first try. I just was not expecting that the browser would hang around in a "we are not done" state and that the java would execute for the first time once processing was already complete. Other than that, it did work and properly redirect, etc. I just don't like the dependency on javascript.
For the record, I am using a shared hosting environment.
I had completed an incredible amount of testing prior to hooking up the PleaseWait page. The PleaseWait Page is more of a "bell and whistle." The browser never once walked away when executing ChargeCreditCard(). From experience, I am guessing this would be rare.
My payment processor does have an asynchronous method. In fact, the implementation we have been discussing is my second. I hooked up my first implementation asynchronously as you suggest. However, I was never happy with it b/c I wanted to display certain details of the transaction to my user and these details were only available from the asynchronous response. I provided a generic "Thank you" page to my user, and had a separate script that would wait for an asynchronous response from the payment processor (no HTML). However, 1) The "Thank you" page had to hang and wait for a synchronous response anyway (accepted or denied), and 2) due to this structure, I was prevented from including certain details of the transaction obtained from the asynchronous response on the "thank you" page (due to the nature of them being asynchronous). I was also actively attempting to avoid a dependency upon javascript (to avoid execution failures if javascript was not enabled). All these things considered, I was shooting for a higher ground on this second implementation. I suppose I am starting to learn that I either need to do it similar to my first implementation or, decide I am OK with the dependency upon javascript. Based on your comments and now that I think of it, everywhere you go online to make a purchase must have a dependency upon javascript. Do you agree?
You comment on using an iframe or an image tag is very interesting. I do not understand. Are you saying that within the PleaseWait page I could have:
<iframe src ="/script2.php"> #
And then script 2 would perform the processing and redirect when finished? I am wanting to think that it would not redirect since headers would have already been sent by the PleaseWait page. If you are willing to explain I would be all ears.
I wouldn't say that every shopping experience requires javascript, but a great many do. The ideal webmaster would design a site to the absolute lowest (read text-only) technology, and apply each subsequent technology to enhance the experience progressively. The rest of us pretty much figure out what the lowest technology we're willing to support is, and it's up to the visitor to decide whether the web site warrants increasing his/her capabilities.
It wouldn't be that hard to do it both ways, and head the visitor down the javascript path or non-javascript path before you submit the data to the processor. There's nothing wrong with the javascript-enabled visitors having a fancier experience than the non ones. It sounds to me like your first implementation is closer to being ready for enhancement; the javascript on the pleasewait page would be looking for the transaction to complete.
I can't think of much to explain for the iframe. Since you got the src attribute right I suspect you know what to do and visualize how it will look - the visitor will see a page that says something to the effect of 'please wait while your payment is being processed' and the iframe's content will be blank. script2.php makes the call to charge the card, then when it's finished it sends the approved or denied content to the browser, where it drops into the iframe.
I'm not a javascript guru, I'm a php programmer who likes the idea of javascript-enhanced applications but doesn't get along with it particularly well, although I do my best to learn a little more anytime I'm cussing at it. The real javascript gurus might have more elegant detection techniques. I can think of a couple of ways to detect whether or not someone has javascript enabled. On a previous page, include [some of] the js stuff at the top of this thread. You don't need a periodical function, just do one xhr.send, something like xhr.send('gotjs.php'). gotjs.php just sets a session variable and echos 'ok' (just to keep the js object from complaining about a timeout). If that session variable isn't set when you come to the fork in your road you know the visitor has js turned off. A second, extremely easy way would be to have a javascript onload event handler that just immediately redirects to another page, but that wouldn't be 'flicker-free'.
You know it is funny, I originally posted in this thread to resolve 2 problems. In the end I did not resolve either and am pleased with the outcome.
Thanks for your help.