LanguagesJavaScriptShopping carts and Data Integrity

Shopping carts and Data Integrity

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.



Any E-Commerce developer knows that today’s Web technologies are not perfectly suited for Web-based E-Commerce. HTTP — the Hypertext Transfer Protocol that defines how the Web works — has two fundamental features that get in the way of E-Commerce. First, HTTP is stateless: the Web server forgets who you are and what you did as soon as it serves you a page. Second, the Web server drops its connection to the browser as soon as a page is finished loading. Information on the server may change, and the user is none the wiser unless they load a page again.

Let’s explore how to deal with these limitations by discussing the most fundamental E-Commerce construction: the shopping cart. You go to a commerce site, and you can put an item into your cart. You can then continue shopping and adding items to your cart, and it remembers what you put into it. Therefore, the Web server must maintain state, which it typically does by putting a code number into the cookie on your computer, which tells the server which shopping cart is yours.

Handling the second problem is more difficult. The page you see in the browser may reflect out-of-date information. Even worse, reloading a page may instruct the server to repeat an action you only want taken once, like making a purchase. This article covers a technique you can use to prevent such problems from occurring.

Examples of the Problem

  • You change your mind about an item in your cart, so you delete it. Then you click the “back” button and it reappears. Then you make your purchase. Did you buy the deleted item or not?
  • You make a purchase, and then you want to review the pages you saw, so you click the “back” button a few times, followed by the “forward” button. When you reload the “purchase complete” page, do you inadvertently make a second purchase?
  • You bookmark your shopping cart, and come back another day. However, your session with the server has timed out, and your shopping cart has been deleted. What do you see when you follow the bookmark? What happens if you purchase the items displayed on the bookmarked page?
  • You’re in the habit of double-clicking buttons, or maybe you’re just impatient, and you click the “buy” button more than once before the “purchase complete” page loads. Did you make an additional purchase?
  • You notice that information about your purchase appears in the URL; maybe the amount for shipping or sales tax is up there. You figure you can save money, so you change your shipping from $9.95 to $.95. Sure enough, the erroneous shipping charge appears on your “purchase complete” page.

    What’s an E-Commerce developer to do?

    The Tools

    I am going to use Server-Side JavaScript (SSJS) to script the solution to these problems. Server-Side JavaScript is used in Netscape and other Web servers; it is well-suited to this problem because of the way it maintains client state. SSJS provides a “client” object, which is created when a user accesses the SSJS application, and remains associated with that user until the object is destroyed or times out. The client object can have any number of properties, which you can define as you like. If you use a “server cookie” to maintain the client object, then all these client properties are stored on the Web server, and a code number is stored in the browser’s cookie.

    I will also use the URL to pass all information from the browser to the server. I can pass information in the link itself (for example, by writing a href=”example.html?code=12345″), or by choosing a method of “GET” for all the forms I want my users to submit.

    Third, I will be using an “interstitial page” in my solution. Links or forms tell the Web server to load the interstitial page, and this page executes a server redirect to another page, which is the one that gets sent to the browser. Therefore, the user never sees the interstitial page; if it catches one of the problems that we’re concerned with, it can redirect to an error page, instead of executing the erroneous query we want to avoid. Otherwise, it redirects to the page the user expects.

    The Solution

    My solution uses a sequence of randomized numbers, which I call “codes.” There is a formula that the server uses to calculate the next code in the sequence, and this formula is itself based on a random number that is different for each client session. Whenever the user requests a shopping cart page, the code is sent to the interstitial page, which compares it against the code stored in the client object. If the codes match, the page updates the value of the code and redirects to the shopping cart page. If the codes don’t match, then we know that one of the problem situations I listed above has happened, and the interstitial page redirects to the appropriate error page.

    The first step in the solution is to initialize the code when the client object is created:


    // the system time expressed in the number of
    seconds since 1/1/1970
    now = today.getTime ();

    // two different random numbers between 0 and 1
    seed1 = Math.random ();
    client.seed = Math.random ();

    // a large random integer that cannot be predicted by
    a hacker
    client.code = Math.floor (now * seed1);

    We now define two functions that we will use on many pages:

    function inc (oldcode)
    // finds the next code in the sequence
    {
    // take the old value of the code and make it look
    very different.
    // You pick your own large integer here.
    var newcode = (oldcode * oldcode) % 284339220943674;

    // return a randomized integer based on the old code
    // and the stored seed value.
    return Math.floor (newcode * client.seed);
    }

    function badload ()
    // determines if the codes match;
    // if they do, find the next code in the sequence
    {
    // client.code is the code stored in the client object;
    // request.code is the code passed to the page in
    the URL

    if (client.code != request.code)
    redirect (‘error_page.html’);

    client.code = inc (client.code);
    }

    Now, let’s say our shopping cart page is called shoppingcart.html. At the top of shoppingcart.html, we place the following code:

    badload ();
    We may want to load this page without executing a query on the server, for example, to show the user a new, empty cart. You would write a link that loaded an empty shopping cart like this:

    write (‘Load empty shopping cart‘);
    Here, we’re not yet using the interstitial page. Instead, we are simply passing the current value of the code to the shopping cart page. badload () matches the two codes, and allows the page to load.

    Let’s add an item to our shopping cart. Now we must link to our interstitial page, which I am calling “hidden.html”:


    write (‘Add item #123 to cart‘);
    Notice that we are now passing the old value of the code to hidden.html; this is the code that we sent to the shopping cart when we loaded it the first time.

    So, what does hidden.html do with this code? First, we check to see if the code numbers match. We take the old code value, find the next code in the sequence, and see if it matches the new code value:


    if (inc (request.code) != client.code)
    redirect (‘error_page.html’);
    Now we must create the new URL for shoppingcart.html. request.page contains the name of the page you want the user to see, without the “.html” at the end; request.item contains the item number we’re putting in our shopping cart; client.code contains the new value of the code we’re passing to shoppingcart.html.

    redirect (request.page + ‘.html?item=’ +
    request.item + ‘&code=’ + client.code);
    Now, when shoppingcart.html loads, the old code has been replaced by the new code, so badload () doesn’t find a problem.

    How It Works

    The only way to add an item to your shopping cart is by sending the correct code to the interstitial page. Once you’ve added the item, the correct code changes. Therefore, if you click “back” and “forward,” the code you send is no longer the correct code, and you are sent to the error page. Likewise, if you click “back” and then “submit,” the code number will also be wrong.

    Additionally, if the user attempts to modify the URL, the code will also have the wrong value. Guessing the right value of the code is virtually impossible, as well, because the operation used to calculate one code from another is based on a random number that is different every time. Even double-clicking a button doesn’t cause a problem; the code will be correct for the first click, but not for the second.

    Conclusion

    By using a server-side scripting language with a robust client object, interstitial pages with server-side redirects, and sophisticated URL manipulation techniques, you can prevent a large class of “shopping cart” type Web page errors. You gain two great benefits from taking the time to implement techniques like these. The most obvious benefit is that your back-end database receives fewer invalid queries. However, there is a much more important benefit: you are able to provide the user with a better experience. And that leads us to the moral of this article. It’s worth it to spend a little more time programming if you make the user experience better. E-Commerce is not about databases and distributed systems; E-Commerce is about the customer. Keep the customer happy, and they will beat a path to your door.


    Jason Bloomberg is Head of Product Development and Web Technology at TransNexus, an E-Commerce startup in the Internet/telephony convergence industry. He is also host of the JavaScript discussion groups at EarthWeb. He welcomes comments at wizard@rhodes.com.

     

  • Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Latest Posts

    Related Stories