Architecting E-commerce
Obviously there is no "one true solution" for e-commerce - it covers too wide a range of functionality with too many special cases and ex exceptions. That said, I need an architecture that will be a good starting point for most SMB e-commerce apps with the ability to easily extend the core functionality for special cases.
Shipping
Typically at some point in either the cart or the checkout process, you need to be able to calculate, display and save the shipping cost for an order if it includes some kind of goods that require shipping. There are four "big picture" questions that immediately come to mind when I think of shipping. The first is whether they system should support n-vendor drop shipping where each product is associated to a different drop shipping vendor with their own separate shipping rules and costs. The second is whether the system should support more general n-shipment processing as many companies will still charge for two separate shipping costs if you place an order with some items on back order but need the other items ASAP. The third is whether it should be possible to assign products to locations so if you're going to need to ship one item from Chicago and the others from Atlanta you'll be able to calculate the real Fed-Ex costs for the two packages if required. The fourth is whether the system should support multiple destination shipping addresses for a single order (send this to our Kentucky location and the balance over to Detroit).
From a business perspective, none of these approaches (except for the last one) are very "customer friendly" as they often result in what seems like unreasonable shipping prices, but there is no question that we come across clients that want such functionality, and in many businesses (especially those selling business:business), these are industry best practices.
Right now I don't want to implement all of the logic for each of these features (YAGNI and all - none of the six projects I'm currently implementing require these features), but I don't want to have to refactor too much code if/when I implement this, so I'm going to introduce the concept of a shipment where each order has 1..n shipments. Right now every order will have a single shipment, but I'll base the shipping interface on interacting with a collection of 1..n shipments - each of which with its own weight, product price, and source and destination addresses. That provides a great deal of flexibility and will make the refactoring to implement the above features a simple matter of adding rules for creating shipments from an order.
So, if an Order has one or more Shipments, what would a shipping calculation look like? In practice, I'd like to be able to just query Order.get("ShippingCost"), but I also think I need a command to say Order.calculateShippingCost() for calculating the shipping cost as I don't want to be recalculating the shipping cost for an order every time I view it - if the rules change I could end up displaying incorrect information. Equally I don't want to just check to see whether or not I have a shipping price from the db as if I just changed the destination or added a new product that will affect the price, so I need a hook to allow for the re-calculation of the order totals. I'm not sure whether I'd pick a granular interface with public methods like Order.calculateShippingCost() or (more probably) a coarser public API such as Order.calculateTotals(), keeping the more granular methods private and probably using a strategy pattern so my order can delegate the details of how to actually calculate the shipping and tax costs to make it easy to vary the algorithms.
Tax
Firstly, I should say I'm only considering sales taxes here such as US sales tax and European VAT. Calculating duties or other taxes are beyond the scope of what I'm currently looking to implement. Secondly, I do want to at least handle US sales tax and European VAT so we need to be able to handle additive taxes (like in the US where the tax is added to the total cost of the order) and built in taxes (like in Europe where the tax is included in the price and just broken out during checkout). Again, I'm going to need some kind of private Order.calculateSalesTax() method together with the ability to order.get("SalesTax").
Payment Processing
I've got burnt by this many times in the past. At first when you think a payment for a order, it might seem like you just need to capture the payment information, process the card and be done. However, with the potential for features like gift certificates and some stores wanting to support multiple payment mechanisms on a single order (splitting an order between two cards), and with returns and balancing credits and the like, what you really need is a very simple accounting system with the ability to apply credits and debits towards an order. This time round I'm certainly going to be adding a table for storing all transactions separately to their order. It does make the system a little more complex than most simple carts, but it makes it more robust, and when dealing with money, that's a pretty good idea!
I'll be thinking and posting more about the details of handling shipping, tax and payment processing. In the mean time, any ideas or input on what you do, innovative ideas or lessons learned much appreciated!
Thoughts?





This e-commerce topic is turning into an informative and interesting book-to-be! In your shipping considerations, you flag weight as an important product attribute. Are height/width/depth dimensions on the list too, since they're often used for shipping calculations (Royal Mail for example)?
Ed
We are going to try and move away from it but weighing everything (13,000 items) is going to be a horrible pain.
Tax-wise we calculate tax for Colorado only using a zip code database to determine what tax rate to charge - our city, county or state tax rate. We built a tax rate table based on zip codes so we can set a separate rate for each tax zone in case one changes but the others don't (city might increase but county remains the same).
For payment processing we use Authorize.net which has worked pretty well. It allows for multiple charges and returns on a single authorization but we don't deal with complexities like multiple credit cards per order. Aacck!
This has been a great series on your thought process and has given me things to think about in our own development.
Can't wait for your system to be complete so we can work with you on projects.
First, how many people are actually going to want to use multiple cards, or a card and some PayPal, or PayPal and Google Checkout at the same time? You might, maybe, be able to make a case for multiple cards at once, but then what happens if one card is declined? You have to roll back the transactions for the other cards, and the synchronization becomes a nightmare. For what?
Second, why not treat coupons as products/items with a negative value? You get all of the benefits of them being a product with only minimal extra coding. They show up on the invoice. They can easily be removed from the order like any other product if the user decides not to use it today. If the coupon only applies to a specific item or set of items, then you can use related-product information to express that linkage. (More about this in a second.) You can then track them with whatever analysis software you use to track your items. If they have a limited run, you can track them as inventory.
This ties into another point you may want to consider: what we're calling "tag-along products". That is, you add X to your order and you automatically add Y along with it. Remove one and the other is removed. You can have a strength associated with the linkage -- is it automatically added, or just recommended that you add it? This has uses from a distribution standpoint: if you want to add a flyer for X version 2 to every shipment of X version 1, then make it an invisible tag-along product. Your warehouse then doesn't have to keep track of complex rules about what flyer goes with what product -- it's just another line item to be picked and boxed.
Having said all of that, there is a case where I can see a need for the credit/debit scenario, and we're running into it with our own system. Our company allows customers to redeem our product UPCs for points toward more product. But, obviously, people aren't going to save up a pile of UPCs and use them at checkout time -- they are going to mail them in every few weeks and expect to be credited with the points. So, the customer has a balance of points, and we need to be able to use those points as a currency. We then need to zero-out the dollar value for the price of the order, but not zero-out the cost of the product so that we don't muck up financial reporting. We also need to remove sales tax. And do we charge shipping? It's a pervasive and tricky modification to the system, so if you're thinking of ever supporting it in the future you need to get it into the original spec ... but the customer goodwill it generates is enormous. It has a measurable effect on the long-term spending of your customers.
Sorry , I guess I started rambling there, too.
Re. the multiple payments / multiple shipments... I'd highly recommend doing this always. Currently I'm stuck in a "one order, one payment, one shipment" model and it's driving me nuts when there are stock problems, customers modifying their orders, refunds, lost parcels etc.
And finally, re. the VAT (at least in the UK). If you sell a book for £5 + VAT (0%) and a DVD for £5 + VAT (17.5%), along with a delivery fee of £2 + VAT, the VAT on the delivery is £1x0.175. i.e you charge vat proportionally to the rates of the items in the order. Condoms in the UK are 5% vat so make sure you don't limit yourself to just 0% and 17.5%...
Royal Mail recently introduced various size thresholds above which a letter becomes a large letter then a packet. On Royal Mail site http://www.royalmail.com/portal/rm see page 2 of pdf ftp://ftp.royalmail.com/Downloads/public/ctf/rm/Pr...
@Ian,
We support weight and price based shipping tables for some of our clients. The thinking is that as long as they come out a total of a few percent ahead of the game over the year, it all works out in the end. Other clients use Fed-Ex and/or UPS weight plus zip code based APIs.
We've done both zip and state based tax rates as some of our clients just aren't willing to pay for ZIP code tables for sales tax in all of the states they are in.
We also use auth.net and Payflow pro although we're looking to create a single API to cover our supported transactions and then plug any payment processor into that.
Glad you're enjoying!
We're getting there ->
The real reason for the debit credit system is that some of our current systems scare me. We have discount certificates and you can get into all kinds of pain with partially completed orders and then refunds and other problems - a simple transaction table that logs all approved financial transactions would make me feel a lot safer than just having the fields in the order table! You're right though - I'm not in any rush to provide multiple card support.
The "discount as product" is an interesting approach, although we find it's properties are pretty different. Our discounts can be site wide or category/product specific, they can be percentage of qualifying products or fixed fee and can have a required base of qualifying products (e.g. $20 off every order with at least $100 of golf balls). It doesn't cover kitting based discounts where you can get 6 assorted cans of paint for the price of four, but it covers a lot of cases. We also have a free shipping option along with a drop down multi-select for the shipping methods that it provides for free.
Definitely interesting to hear your approach though - it's amazing how many different ways there are to approach this stuff!
That is a REALLY good point. We don't do that at all. At least we should store a copy of all of the discount certificate rules with the order so the rep can check.
I agree re: one payment/one shipment. It's a real pain to upgrade - especially if (like us) you've got a bunch of different e-commerce stores with different needs.
Good to know about the condoms :-> Seriously, there are things like that in the US. Each ZIP code may add up city, state and county sales taxes for a total tax rate, but each can have different rules on what isn't taxable (such as food or clothing) or even limits ($50 in clothes is tax free, $150 is taxed, etc.). On the whole we try to ignore such rules as we find out clients simply can't afford the overhead of appropriately categorizing all of their products (for instance, the definition of what is food which is often tax free is different in different jurisdictions). If you run a small chain of stores with a presence in 3-4 states, it would be a nightmare to create the appropriate associations, although eventually we may add some kind of rules system for overloading default tax rates based on jusrisdictions, but I just can't see our clients being able to afford the cost of implementing such a system . . .
Thanks! I remember something about that. Gotta bug you more when I get over there.