Shopping carts and Data Integrity
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
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 SolutionMy 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
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
// 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
if (client.code != request.code)
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:
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)
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 WorksThe 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.
ConclusionBy 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.