Shopping Carts: Code That Exists To Make You Money

I'm a small software vendor who spends a lot of time doing A/B testing of website elements, including those in the conversion pathway, with the goal of maximizing my sales. Recently, I had the occasion to reimplement my shopping cart. I'd like to share what I know about shopping carts: the technical details in implementing them, how users interact with them, and how crafting that interaction to be maximally satisfactory can make you money.

Disclaimer: These are my own experiences, and the design of the shopping cart in this article is intensely specific to my own business challenges. It is not intended to be a copy/paste solution for anyone selling anything on the Internet. If I were an animal, I would be a three-toed sloth. I'm not presuming to tell tigers how to hunt, I'm telling other tree dwellers how to make an absolute science out of hanging from branches. (I am indebted for this metaphor to a commentor on Hacker news who has the best take ever on following advice.)


Divider

The Problem Statement

Most websites which sell things sell many things. The number of possible combinations of things is combinatorially explosive, which quickly overwhelms the ability of the user to select what they want via the Internet's default method: click a link and get taken right to what you want. This happens really quickly: there were once exactly two choices to make for my software: "Do you want a CD or not?" and "Do you want to pay with Paypal or Google Checkout?" That is a 2x2 matrix, 4 possible options. I put up 4 possible "Buy Now" buttons, which dumped people directly to the order form. Conversions were dreadful.

Shopping carts attempt to solve this problem of choice paralysis. They are a dirty, disgusting, user-unfriendly hack. The metaphor of the shopping cart was developed to ease users into e-commerce by comparing it to commercial interactions they were familiar with. This is a leaky abstraction, because what the designer (i.e. you) actually wants is to present sets of options efficiently. User expectations are high for software, including for web sites. Call it the Principle of Instant Satisfaction (POIS): I want the good stuff right the heck now, exactly when I request it. Not for nothing is Amazon's enormously successful cart built around a technology called "One-Click".

Most shopping carts aren't one-click. They're not even close: they bury the user in page upon page of buttons, forms, and unintuitive interfaces. It is no wonder the shopping cart abandonment is so high. (From personal experience, I saw 80%+ on my site -- and that is the number after a year of improvement!)

Thus the problem: rethink the shopping cart and make more money.

Back to the Table of Contents


Divider

Rethinking the Shopping Cart: A Journey, Not A Goal

In the current conception of shopping carts, there are essentially three phases:

  1. persuade the user to purchase from you
  2. get them to add something to the cart
  3. get them to check out.

Notice how linear this is. The Internet is the enemy of linearity: hypertext and URL bar means the entire world is just a click away. Thus the shopping cart has to impose an unnatural and artificial linearity to generate sales. But sometimes the users are not quite ready for linear.

Take, for example, the phenomenon of "just trying it on", one major source of cart abandonment. Users interact with your content -- sales copy, reviews, what have you -- in an intensely nonlinear fashion.

  • They bounce around pages
  • They skim
  • They go up and read more closely
  • They bookmark and come back later.

In this non-linear search for shiny things, they may well put an item in your cart. But they're not doing that to make a commitment. They're doing that as part of their process. Most shopping carts try to be really pushy after that point: better carts will make it obvious that they can now checkout, worse carts will immediately try to capture billing details. Its sort of like going up to a young lady in a social situation, talking for a little, hearing her name, and then immediately asking her out next Friday. It might work some of the time, but she is probably not ready for that offer yet -- she hasn't made a commitment, she has just signaled that she is willing to continue the conversation with you.

So your shopping cart needs to be willing to continue the conversation with your customers. This is difficult when using high-friction, pageload required shopping cart designs. Enter the brave new world of Javascript interfaces, typified by (but not limited to) AJAX. One important interface these made possible was the Lightbox.

Back to the Table of Contents


Divider

Lightbox: a Revolutionary User Interface Paradigm

B2C (business-to-consumer) software lives and dies on screenshots. Unfortunately, screenshots tend to be physically large and thus limit the amount of space one can devote to other uses, such as sales copy. You generally make a thumbnail of the screenshot and then arrange things such that clicking the thumbnail shows the user the larger version in some fashion.

The specifics of "in some fashion" are enormously important, particularly when selling to non-technical customers. Just linking the screenshot to a JPG set to open in the same window, for example, is like sending non-technical users to the roach motel. They check in, they don't check out. Many users simply do not know the back button exists.

I noticed this on my website some years ago and looked for solutions. Screenshot galleries are marginally more user friendly, as are flash sideshows, etc. Then I started using a piece of Javascript called Lightbox, and I was blown away. Bounce rates on core pages decreased by as much as 20%.

Lightbox gives your users the ability to do non-linear interaction with content, which they deeply desire, while still allowing you to maintain your hold on their attention. The name now generically describes the effect, of which there are many excellent implementations.

  • The subtly dimmed background reminds you of what you were doing.
  • The window is easy to dismiss: click anywhere and it is gone. (Compare it to a modal dialog, which requires you to hit a small target to dismiss.)
  • The box automatically finds pristine, uncluttered space on any design.

Lightboxes are excellent tools for integrating shopping carts, signup dialogs, and the like onto sales pages. You can also put additional information which you think some users may find useful without sacrificing the experience for other users, similar to a footnote in writing, but less distracting. When I first integrated a lightbox-esque shopping cart, it increased sales by about 20%.

Back to the Table of Contents


Divider

E-junkie Cart: Good, But Improvable

Two years ago the folks at e-junkie, my payment processor, unveiled a new shopping cart. It used a lightbox-esque effect, plus AJAX to communicate with the server, and could be embedded in almost any web page with some simple cut/paste of Javascript code.

Take five seconds and play with it: the following is ripped directly from my purchasing page. Any button/link adds to the cart.

Get it through instant download for only $29.95*. Purchase a single copy with your credit card.
Get it through instant download and get a CD for only $34.95! Buy a single copy and CD with your credit card.

I fell in love with it at first sight, and it went on to make them a lot of money. It is easy to see why:

  1. The cart never got in your way, even in reading thousands of words of dense, technical text.
  2. The cart is ready to step back, with one click. If you want to keep reading sales copy, go right ahead. No page load, no friction.
  3. The design is sleek and modern, even on a website which largely isn't.

So I implemented it on my website, which took substantially less than an hour (mostly to rewrite my content to take advantage of saved space.) Sales soared.

  1. Less space spent on UI to accomplish sales means more space to actually sell with.
  2. Two binary questions asked sequentially are easier to process than a matrix.
  3. The cart greatly increased uptake of software with a CD.

It was this last point which eventually pointed to a problem: prior to implementing the cart, roughly 25% of my users purchased CDs. (It adds $5 to the cost of their order and I actually lose money on them: the option exists only to drive sales among people who would not buy without getting something physical.) After the cart made purchasing CDs much easier, 50% of my users purchased CDs. This lasted for about a month. This lead me to conclude that, when given easy access to the option, roughly 1/2 of the people inclined to purchase my software will purchase CDs.

Then the cart changed. E-junkie started concentrating less on virtual goods sellers (software makers and bands) as they became more popular. Suddenly, their cart had to accomodate tangible goods as first class citizens, and it eventually grew features like calculating sales tax and shipping costs. These are wonderful features for many users, and useless to me. The code base had to support both of us. Now try putting a CD in your cart and click the checkout button. (You won't be charged.)


Get it through instant download for only $29.95*. Purchase a single copy with your credit card.
Get it through instant download and get a CD for only $34.95! Buy a single copy and CD with your credit card.

Surprised by that, weren't you? I was, too, the first time it happened to me. I now understand the technical reasons for it, but it strikes me as a suboptimal user experience. (It actually used to be worse -- no auto-update of the cart -- but e-junkie implemented some significant improvements.)

One month after the zip code field appeared for the first time, the proportion of my users buying CDs had declined in half again. The new proportion, 25%, has been the same for 2 years now, even as absolute sales have tripled. Did the others move to the download, or did they leave my site entirely? I'll never know.

(There are technical reasons preventing e-junkie from nixing that box for me. We tried and I give them a lot of credit for it.)

Another thing you may have noticed, the first time you loaded the cart, was 2-3 seconds of load time. That is fast compared to a page load but it is not instant, and on the Internet if it isn't instant, its slow.

Back to the Table of Contents


Divider

Designing a new cart

On February 7 2009, two years and change from when I first started using the e-junkie cart, I blogged that I was going to write a new cart. I originally planned on making heavy use of existing OSS code (Prototype, for example) and getting it done in about 2 hours.

My goals were simple:

  1. Loading the cart must be instant.
  2. Modifying the cart must be instant.
  3. Checkout should be one click away for 99%+ of users.
  4. The cart should not say it is a shopping cart.

This last point is subtle: the reason I am using a lightweight, no friction, low-commitment cart is to encourage users to go back and forth between it and the sales copy. If they're not ready to say yes yet, that is fine. Accordingly, I don't want them to perceive of interacting with the cart as commercial in nature, I want them to see it as information gathering, just like the information gathering they're doing elsewhere on the page. That means nixing the words "shopping cart". Besides, "checkout" is well-understood enough these days to not need a "cart" context to tell you what it means.

Back to the Table of Contents


Divider

Implementing A Cart: Open Source Rocks

The cart requires four major technical components:

  • A lightbox library.
  • Some way to get pricing logic.
  • Display logic (i.e. if you increase the quantity, then redraw the price, etc)
  • Some way to communicate the customer's choices, and the customer, to the payment processor.

After looking at a few lightbox options, I settled on iBox because of its advertised ability to load content from a div. I assumed this would be essentially instantaneous, avoiding the several second AJAX-induced delay I was used to with e-junkie's cart. Plus, the default styles are gorgeous and that is important since I have the design ability of a blind, drunken rabbit.

iBox was a good decision in some ways and a bad one in others. For one, the default (easy to implement) way of tagging a link as iBoxy is to assign it rel="iBox". Links so tagged have their onclick attributes overwritten and replaced with the necessary logic to open the iBox. Unfortunately, since I have two links opening the same cart but want different behavior based on which one is pressed (switching the product the user is presented with), I needed the onclick myself to fire the logic to update the cart.

iBox has a feature to force it to show via Javascript. Unfortunately, for whatever reason, this doesn't interact well with showing content of hidden divs , which was the only case I cared about. I eventually hacked my way around this limitation by keeping one hidden copy of the cart on my page and copy/pasting its HTML directly into the iBox, resulting in two identical carts, one visible and one a permanently hidden scratch-pad. Its conceptually ugly, but it works well.

Synching the visible copy and scratch-pad would be quite a bit of challenge if not for Prototype, my Javascript library of choice. (I don't have any particular reason to recommend it other than "it came with the Rails books which I learned web development from". Other folks may wish to try JQuery or, if you're really up for fighting for your supper, implementing a cart without one of these libraries.) The td that I know will hold the item name has the same class name in both places -- updating it is a matter of

$$('.pjm_cart_item_name').each(
  function(element) {element.innerHTML = productName}
);

Incidentally, there is a bug in iBox which causes the overlay to not scroll with the view port on certain browsers. I fixed it, see code snippet here if interested.

Back to the Table of Contents


Divider

A Premeditated Crime Against MVC

The pricing logic for my program is not very complicated: $X for the program, $Y for a CD, two bulk purchase discounts. Accordingly, and with most of my development budget chewed up by actually getting the cart to display, I hard-coded it into the View layer of my very MVC site.

Don't do that. Your CS professor won't like it. Your maintenance programmers will curse your name. Rails coders will laugh at you from behind their Powerbooks. But it does, indeed, get the job done.

A slightly more orthodox way to do it: put the logic in the server. Have the Javascript actually use AJAX calls to talk to the server when the cart changes and find out the new price. You can use whatever display logic is necessary in the Javascript, as long as it is dumb as can be.

This approach has a lot to recommend it from maintainability, security, and pure code hygeine perspectives. However, I was over my time budget and didn't want to compromise the number one goal: the user experience. You know that little loading-please-wait animation you get every time you do an AJAX call? Not so fun, for most people.

Back to the Table of Contents


Divider

Getting the Cart Data To The Payment Processor

As mentioned previously, I use e-junkie as a payment processor. They wrap Paypal and Google Checkout so that I don't have to. I'm overwhelmingly satisfied with them and had no desire to change payment processors to get a new cart, so I integrated this new cart with their service. This required a very, very offlabel use of their Buy It Now buttons.

Usually, we think of "Buy It Now" as the antonym to "shopping cart". However, all a checkout does is Buy It Now for a basket of items you happen to know about, right? And it turns out that e-junkie will let you use their Buy It Now buttons with any quantity you want. Since no customer has ever ordered both a license of the software and a separate license complete with a CD, I felt very little resistance to removing that as a possibility. This lets me get away with another dirty hack: using Javascript to rewrite the URL for the checkout buttons lets me change item, quantity, and even pricing, which is easier than actually figuring out how to post a real shopping cart to their API. This trick will also work for those of you using Paypal and, I think, Checkout even if you don't use e-junkie.

You can look at some sample code to do this if you're interested. (Lightbox, again. I love it.)

Thus, if I want to update the cart links to reflect that the user wants 2 copies,

pjm_updateCart("quantity", 2)

and it is done.

Back to the Table of Contents


Divider

Hacking Around Limitations In 3rd Party Code

Observant readers who perused that code might wonder "What is that paypalOnly variable doing there?"

Well, as it turns out, e-junkie's internal implementation of Google Checkout's API means that if you use a Buy It Now button with an amount parameter, rather than resetting the item price it is added onto the item price. There is a security rationale for this, I think (it prevents people from making their own BIN button and resetting your price to a penny).

This means, unfortunately, that I can't give Google Checkout users a quantity discount through this cart. In the extraordinarily rare case (Rails reports that it was 2 out of the last 10458 sales) that someone wants to do that, I just grey out and deactivate the Checkout button.

I don't know an easy way to greyscale an image in Javascript, but with Paint.NET it is trivial.)

function pjm_setGoogleButtonActive(active) {
  $$('.pjmCartGoogleCheckoutLink').each(function(element) {
    if (!active) {
      element.onclick = function(e) { alert("Google Checkout cannot be used to buy more than one copy.  Please use Paypal instead."); return false;};
    } else { element.onclick = null;}
  });
  $$('.pjmCartGoogleCheckoutImage').each(function(element) {
    element.src = "/images/google-checkout-cart" + (active ? "" : "-bw") + ".gif";
  });
}

My philosophy is to spend developer time to polish the heck out of the common cases (50% of users want CDs, the cart must make this easy) and get around to the edge cases when convenient (a miniscule fraction of users may want multiple copies through Google Checkout).

Back to the Table of Contents


Divider

Security, Smecurity

I'm a one-man band who sells software that teachers use to prepare for class, not an international bank where billions could be lost if one overly trusted trader with delusions of grandeur was left unsupervised. Accordingly, the fact that the same URL rewriting trick that lets me change the price to give users discounts could be used to reprice my software to a penny... really doesn't worry me that much. (Indeed, if you can't accomplish it given my Javascript code... you are probably reading the wrong article.)

My investments in security, both for shopping carts and for DRM for serial keys, are aimed at keeping honest people honest. (I have a much less lax attitude when it comes to my customer's data, you'll be happy to know. Paypal/Google Checkout keep their details away from my prying eyes, and I do all the usual to protect their names and email addresses. But all people can exploit out of me is a license key... and guess what, boys, I have them in infinite supply.) You'll want to think long and hard about acceptable risk when you design complicated systems, because security will always trade off with other values.

Back to the Table of Contents


Divider

Putting It All Together

There are a couple other bits of spit and polish applied to this, but nothing of major note. Oh, that's right: you haven't seen the new cart yet. Feast your eyes:

Use online instantly for only $29.95. Purchase a single copy with your credit card.


You can compare that to the old version if you want to:

Get it through instant download for only $29.95*. Purchase a single copy with your credit card.
Get it through instant download and get a CD for only $34.95! Buy a single copy and CD with your credit card.


I know they look identical prior to you clicking on them. That is quite intentional -- I'm currently running an A/B test using Google Website Optimizer, to test which cart is ultimately superior. I've only had the new cart live for a day or two (as of 2/12/2009 when I wrote this), so there isn't any clear data either way yet. The purpose, theory, and raw mechanics of A/B testing could absorb another article in themselves. Heck, a shelf of articles. And it is already midnight, so I'm done for the day.

Back to the Table of Contents


Divider

Why Do All This?

A bit of a motivational sidenote, both in the sense of motivating people and in mentioning what the motivation is: why the heck did I spend so much time on this? Well, despite it eventually eating 12 hours of dev time instead of the allotted 2, and requiring another 2 hours to write this up, I do think that this will make me some significant money via increasing people's propensity to buy through the cart. Optimizations to your conversion funnel are wonderful because they are all multiplicative: 2% a week for two years means you increase your sales by a factor of about 680%.

I haven't done quite that well the last two years (I publish sales data, go nuts), but I rarely have an idea as good as this cart. I'm hoping for a modest 5% increase in sales from it. Which, if my rough projection of $30,000 worth of sales this year holds up to be accurate, is $1,500 in my pocket. $125 an hour is, well, I wish I made it at the day job.

Please feel free to share this article with anyone you think would like it.

Back to the Table of Contents


Divider

Results of A/B Test

Updated on 2/17/09: The preliminary results of my A/B test are in, after the cart was in operation for a week. Keep in mind that the purchase page is late in the sales cycle -- many people arrive at it after getting to a "nag screen" in the program which says they've hit one of my functional limitations and can remove the limitation if they purchase the software. I mention that to give the following numbers a bit of context.

Historically, roughly 3 to 5% of people viewing my purchase page go on to purchase. That number changes with random chance, my business cycle, events in the year, and other factors. This intrinsic malleability of metrics is one reason why you do A/B testing for changes: randomly assigning a visitor to the control group (sees the old page) or the experimental group (sees the new page) gives you some confidence that the change you see in conversions is coming from, e.g., the effects of your new shopping cart and not from Valentine's Day. If you're interested in the topic go read up at Google Website Optimizer, which is what I use.

Its what made this lovely little graph that I'm about to explain to you. A quick refresher course in high school stats: assume for a moment that for each alternative there is One True Conversion Percentage. If you randomly show N people that alternative, by observing how many of them convert, you can construct a confidence interval for what that OTCP is, with the probability that the OTCP is close to the value you observed being highest and then the probability that it was farther away decreasing in the classic bell-curve fashion. If you draw your two bells and one of them has most of its area substantially to the right of the other one, that alternative is the winner. (If they mostly overlap the test was inconclusive and you either wait for more data or you move onto your next test.)

What I've found: the cart I described making in this article most probably kicks butt. The conversion rate has increased from 5.48% to 10.6%. This represents a 94% increase in sales. (Well, OK, if you want to get technical about it I guess its a 47% increase in sales at the moment because half of visitors are still seeing the older, inferior cart.)

The chance that the new cart is actually better and this is not some spurious statistical mishap is, coincidentally, also about 94%. (Though that doesn't say anything about the magnitude -- could be 94%, could be 1%, could be a factor of four at the outer reaches of each confidence interval.)

Now if my sales had actually increased that much you'd expect to see a really freaking big jump on my sales chart, right? Observe the following (its a thumbnail, click for larger version):

Bingo Card Creator Sales as of 2/17/09

Each of these bars represents a month I have been in business, starting at $24.95 for July 2006. That first big outlier bar is 2007 Halloween. That second big one is Easter 2008. That last bar? That is sales through February 18th. If I were to graph the estimated sales for the rest of this month, I would have to rescale the Y axis. (Update on March 1st: Yep, turns out I had to, indeed.)

If you accept that 94% number for the moment and also accept my $30,000 sales projection for this year that I made prior to making the new cart, that implies that rewriting my shopping cart will earn me approximately $28,200, which is roughly 98% profit (the remainder is credit card conversion fees) because the prospects who were not converting were people who I had already drawn into my website and trial anyhow. Its not like I have to pay Google again for drawing them in, pay my writer again, buy more hosting to accomodate them, etc.

Which implies that writing the shopping cart made me about $2,300 an hour. Have I mentioned recently that I love software?

Updated on 3/1/09: Having proven to my satisfaction that the new cart is better, I have ceased use of the old one. It is only available from this page, for historical purposes.

Back to the Table of Contents


Divider

Get The Code

iBox and Prototype are OSS software available under very permissive licenses. I'd suggest you get them from the official places. As for my cart code mentioned in this article and the cart HTML to display it, I release it unto the public domain. Please feel free to use it for practice, inspiration, or a starting point for your own code.

I cannot mention strongly enough: make sure you read and understand it before you use it anywhere, or you'll be selling Bingo Card Creator on my behalf.

e-junkie is sinfully cheap ($5 a month) and the best payment processor anywhere. I recommend them without reservation to anyone selling software. I told them I wanted to make code that sharply limited my dependency on them and they helped me do it -- how cool is that?