Building an E-Commerce Site Part 3: Catalogs and Shopping Carts

This is the third and final article in a three-part series dealing with using PHP 4 and MySQL to make a comprehensive e-commerce storefront solution. This article covers the shopping cart, payment processing, and database engine considerations, among many other topics. Full source code included!

Welcome to Part 3 of the E-Commerce Step-by-Step Guide. This is the last installment of this 3 part series (hurray!). In this installment, we add scripts to let the user browse our product catalog and we let them order our products. After completing this installment, you will have:

  • created the underlying tables for the shopping cart and ordering;
  • created PHP scripts for navigating the product catalog;
  • created PHP scripts for viewing product details;
  • created PHP scripts for managing the shopping cart;
  • created PHP scripts for purchasing the shopping cart items;
  • a basic understanding of the payment process
{mospagebreak title=Assumptions and Requirements}

Before reading this guide, you should have read and completed part 2 of this series. Specifically, you should have the mymarket database up and running, and have all the user management functions running. Your PHP4 sessions should also be working.

If you have not already done so, please read Part 2 of this guide at:

Also, if you want to follow this guide and get a working site, you must use PHP4. The final release of PHP4 is out now, so you should probably upgrade to that if you are still using any pre-release version.

{mospagebreak title=Overview of The Process}

In part 1 of this series, we had a diagram of how this whole online shopping process worked. We will now extend it a little bit to show the relevant interactions:

{mospagebreak title=MyMarket Shopping Experience}

This is the set of interactions the customer makes with MyMarket:

  1. Customer browses the product catalog
  2. Customer adds items to shopping cart
  3. Customer confirms the purchase
    Payment Processing
    Once the customer has confirmed the purchase, we have to process their payment:
  4. Transaction goes to payment processing / transaction clearing company
  5. Company verifies the amount and takes if from the customer’s account
  6. Company pays MyMarket (after taking off their processing fee)
    Order Fulfillment
  7. Warehouse gathers the products in the order for shipping
  8. Courier company ships the goods off to the customer, and the purchase is complete

That’s overly simplified, but it should help to get a feel for the whole process.

{mospagebreak title=The Product Catalog}

Our product catalog will be a means for the customer to see the products that we carry. Our products are (hopefully) sorted into meaningful categories, so what we have to do is provide the user with an easy way to navigate the categories and see the products under each one.

Our category tree is built recursively, so our product navigation scripts are very easy to write. The general process will be like so (starting from the Top category):

  1. PHP page to list all sub-categories under the current category
  2. When a user clicks on a sub-category, repeat step 1 with the selected sub-category

Thatโ€™s it, we can navigate and traverse the tree using just one script — clean and simple!

When we are displaying the contents of a category, we will show:

  • The sub-categories under the current category
  • All the parent categories leading back to the top category
  • The products under the current category
  • A summary of the shopping cart

So how does this all fit on the screen? Here is a simple layout that will do the trick:

  • Section 1:
    The site header is the standard header weโ€™ve been using all along. It shows the name of the current page (DOC_TITLE) and login links.
  • Section 2:
    Our standard site navigation links go here.
  • Section 3:
    Here we will display a summary of the customerโ€™s shopping cart. We will show the number of items in their shopping cart as well as the total price.
  • Section 4:
    Here we will show the navigation path from the current category back up to the top category. For example, if you were currently in the Icecream category, it might look like this:

    Top > Snacks > Icecream

    Showing all the categories that lead from the Top category to the Icecream category.
  • Here we will show the navigation path from the current category back up to the top category. For example, if you were currently in the category, it might look like this:Showing all the categories that lead from the Top category to the Icecream category.
  • Section 5:
    This is the area in which we print out a list of all the subcategories under the current category. For example, if we were under the Snacks category, we would expect a list of sub-categories like Chips, and Icecream, etc. to show up here. If there are no sub-categories, we should print out "None".
  • This is the area in which we print out a list of all the subcategories under the current category. For example, if we were under the category, we would expect a list of sub-categories like , and , etc. to show up here. If there are no sub-categories, we should print out "None".
  • Section 6:
    This is where we print out the products that are available for sale under the current category. When a user clicks on a product, they should be taken to the product details page where we display detailed information about the product. We also provide a link here for the user to add the items into the shopping cart.
  • Section 7:
    Our standard footer that contains the cheesy slogan ๐Ÿ™‚

To recap, sections 4 and 5 work together in letting the customer navigate the product catalog (or more correctly the product category tree). Section 4 provides backwards navigation (to move up the category tree) while section 5 provides forward navigation (down the tree). Along the way, we display products in the current category in section 6, and we always show the shopping cart summary in section 3.

This is just an example of how you can arrange the screen. Since everything is in template files, you are free to rearrange things to make the site more customer-friendly. Make it easy to find your products, and make it even easier to buy something ๐Ÿ™‚

{mospagebreak title=The Shopping Cart}

Now that we understand how we should build our product catalog, let’s shift our attention to the shopping cart. What exactly is a shopping cart, and how should it be built? A shopping cart is simply a list of products that the customer wants to buy, so it turns out to be really easy to build. In fact, our shopping cart will just be an associative array of product ID’s and quantities, and it will be stored as a session variable ($SESSION["cart"]).

The basic operations that we’d like to be able to do with our shopping cart is:

  1. Add products to the cart
  2. Remove products from the cart
  3. Count up the number of products in the cart
  4. Calculate the total price of the products in the cart

Our shopping cart will be an object (of the Cart class). If you are new to object oriented programming in PHP, this will be a good introduction for you. Our cart will be an object with two properties:

  1. $items
    The associative array of product ID’s and quantities. The key is the product and the value is the quantity
  2. $total
    The total price of the items in the cart. We will only update this as necessary, because it is a bit of work to calculate.
  3. The associative array of product ID’s and quantities. The key is the product and the value is the quantityThe total price of the items in the cart. We will only update this as necessary, because it is a bit of work to calculate.

 

Adding Products to the Cart

Adding a product to the cart is as easy as putting the product ID into the $items array. We just increment the quantity of that product ID in our array.

Remove Products from Cart

Even easier than adding a product to the cart, we just have to remove this product ID from the $items array by unsetting it.

 

Counting the Products in the Cart

We can’t just return the number of records in our array, because that will only tell us the number of distinct products in the shopping cart. We have to iterate through our $items array and count up the quantities of each product .

 

Calculating the Price of the Cart Items

This is the toughest part of the shopping cart, we have to add up the price of all the items in our shopping cart. You will notice that we don’t keep track of the price of the products in our shopping cart, we only keep the product ID and the quantity of that product. What we’ve got to do is ask the database how much each item costs, multiply that with the quantity ordered to get a total, and then sum up all the totals. The general algorithm would be like this:


Cart_Total = 0 for each product (P) in the shopping cart SELECT price FROM products WHERE id = P Cart_Total = Cart_Total + price * quantity of P ordered loop

That’s the general algorithm, but it is slow if the customer had lots of products in his cart. Reason is that we made one separate SELECT query for each product, this is bad. Instead, what we will do is construct a list of product ID’s that we are interested in and only executing just one query. So our improved algorithm is:


Product_List = "" for each product (P) in the shopping cart Product_List = Product_list + P loop Cart_Total = 0 SELECT id, price FROM products WHERE id IN (Product_List) for each record in the query result Cart_Total = Cart_Total + price * quantity of id ordered loop

The algorithm got a little bit more complex, but that saved us from running lots of individual queries (which would kill your database when your millions of customers rush to buy things at the same time, hehe).

 

Viewing the Shopping Cart

So far we have dealt with adding, removing, and counting things up in the shopping cart. We will now look at viewing and updating the contents of the shopping cart. When the customer goes to view his/her shopping cart, we want to let them change the quantities of the products they ordered. We will also let them empty out their shopping cart. These operations are straight forward enough that we won’t explain them, the source code should be self-explanatory.

From here we let the customer purchase the products in the shopping cart. Up to this point, we have not required the customer to log in.

IMPORTANT NOTE: It is probably not a good idea to make your customers log in before you let them browse your product catalog or play with the shopping cart.

It is only when the customer really wants to make a purchase that you should ask them to log in. Why? So you can pull up their personal information (name, address) from the database. Before this point you don’t really care who they are (unless you keep some profile of them …)

Once you have they’ve logged on, you will ask them to verify their billing information and also to supply their credit card information and any special instructions or comments for this purchase.

IMPORTANT NOTE: You should not store credit card information in the database, instead it is safer to ask for it only when the customer is actually going to purchase something. I’d be pretty scared and upset if I walked into a department store and they had my credit card information on file!

Okay, so the customer enters in his information, the next step is to show a confirmation screen telling them what is going to happen. Basically a summary of the order and the billing details / instructions that the customer has entered in so far. If all is well, the customer will confirm the order.

What comes next is payment processing and order fulfillment. These will be the topics for our next sections.

{mospagebreak title=Payment Processing}

Payment processing is the part of this tutorial that I can’t really write code to explain. The reason is that each transaction clearing / payment processing company is different. The basic idea is this:

  1. Your PHP script sends billing information to the transaction clearing company. This basically includes the customer’s name, address, credit card information, and the amount to charge.
  2. The transaction clearing company will verify (1) that the credit card information is valid and (2) there are sufficient funds for the purchase. If all goes well, the customer’s credit card is billed for the purchase amount and a result is sent back to your script.
  3. Your script takes the result of the transaction and determine what should be done. If it was successful, do whatever else is necessary to fulfill the order. If payment did not go through, have the customer re-enter their payment information.

That is the process when you use a processing company that does real-time payment authorization. The other option is to do batch processing where you gather up all the orders and process a together at some later time — something for you to think about.

Anyhow, there are lots of payment processing companies. You can search the web for a list of them, for example, you can look at a Yahoo directory of these places:

http://dir.yahoo.com/Business_and_Economy/Business_to_Business/ Financial_Services/Transaction_Clearing/

There’s lots of them, so you will have to pick one that meets your needs and follow their documentation to get your scripts working properly.

Some tips:

  • PHP4 has Cybercash support (can anyone share their experience with this?)
  • PHP cannot natively make outgoing HTTPS requests, so if you need to be able to do that, you will have to call another program to do it (like CURL or maybe write a Perl script)

The sample script (shopping/complete_order.php) included here will by default assume that the transaction was authorized (see the authorize_payment() function). You will have to modify this function according to how your payment processing company works. Basically it should return true or false, depending on if the transaction was approved or not. You will have to extend it to meet your needs.

Order Fulfillment

Once you’ve accepted payment for the products that were ordered, you have to get them to the customer. If you are selling physical goods, this means getting the products from your warehouse shipped to the customer. If you are selling something software, you have to let the customer download what they ordered, or supply them with the serial number. Whatever, is relevant in your case.

Again this is a topic we won’t dicuss too much because it depends on how your operation is setup.

That’s enough theory for now, you must be bored ๐Ÿ™‚ So let’s get on to the database changes and then we will look at the new scripts. On with the show!

{mospagebreak title=Step 1: Database Changes}

First off, let’s go through the database changes since part 2. Fire up MySQL and login as the root user by issuing this command from the shell prompt:


$ mysql -u root -p
You should see MySQL started:

Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 412 to server version: 3.22.30 Type 'help' for help. mysql>
Now select the mymarket database:

USE mymarket;

Order Tracking Tables

First thing we need to add is some tables to track customer orders. Specifically, we would like to track the orders that are going through MyMarket, and the products that are being purchased.

We will need two new tables, an orders table and an order_items table. There is a one-to-many relation between orders and order_items, we say that an order can have one or more order items, and order items can belong to one or more orders.

First the orders table to hold the order details. Each record in this table will contain:

  • id – An order ID (which is auto-generated by MySQL)
  • username – The username of the customer who initiated the order
  • o_timestamp – The date/time when the order was created
  • a_timestamp – The date/time when the payment was authorized or declined
  • status – A status of the authorization process
  • status_details – A text message describing the status of the autorization process
  • custinfo – Customer information
  • comments – Any special instructions or comments to go with the order
  • amount – The amount the customer was billed
  • – An order ID (which is auto-generated by MySQL) – The username of the customer who initiated the order – The date/time when the order was created – The date/time when the payment was authorized or declined – A status of the authorization process – A text message describing the status of the autorization process – Customer information – Any special instructions or comments to go with the order – The amount the customer was billed

Issue this statement to create the table:


CREATE TABLE orders ( id int not null auto_increment, username varchar(16) not null, o_timestamp datetime, a_timestamp datetime, status tinyint, status_details varchar(255), custinfo text, comments text, amount float(5,2), PRIMARY KEY (id) INDEX username (username) )
Next, the order_items table to hold the items that are a part of an order. The fields in this table are:
  • order_id – The order ID to which this item belongs
  • product_id – The item that was ordered (from the products table)
  • price – The price of the item at the time of purchase (this may differ from the price now)
  • qty – The and the quantity ordered
  • – The order ID to which this item belongs – The item that was ordered (from the products table) – The price of the item at the time of purchase (this may differ from the price now) – The and the quantity ordered
Issue this statement to create the table:

CREATE TABLE order_items ( order_id int not null, product_id int not null, price float(5, 2) not null, qty int not null, PRIMARY KEY (order_id, product_id) )

On-Special Products

While we’re doing database changes, let’s also add an extra flag to our products table so we can mark a product as being on-special. We will display on-special products on the MyMarket homepage.

First let’s take a look at the products table as it is now:


mysql> DESCRIBE products; +-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | | PRI | 0 | auto_increment |
| name | varchar(25) | | MUL | | |
| description | varchar(255) | | | | |
| price | float(5,2) | | | 0.00 | |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

We are going to add an extra field called on_special to flag a product as being on-special. To modify an existing table, we will need to issue an ALTER statement. Issue this statement like so:

ALTER TABLE products ADD on_special tinyint default 0 AFTER price;
This will add the on_special field of type tinyint, the default value be 0. This field will be added after the price field. Now let’s look at the table again to verify the new field is there:

mysql> DESCRIBE products; +-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | | PRI | 0 | auto_increment |
| name | varchar(25) | | MUL | | |
| description | varchar(255) | | | | |
| price | float(5,2) | | | 0.00 | |
| on_special | tinyint(4) | | | 0 | |
+-------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Okay, that looks good. We will need to modify our product management scripts to deal with this new on_special field. {mospagebreak title=Step 2: Extracting the New Scripts}

Okay, we’ve got our user table, let’s make screens for the user to log in and log out. Download the mymarket3.tar.gz file and extract it into your web root directory. Note that this replaces the files from the first tutorial.

IMPORTANT NOTE: If you have made modifications to the files from the first tutorial, please make a backup copy and store it somewhere else. The files in mymarket3.tar.gz will overwrite any changes you made to the files from the first tutorial.

If you have made modifications to the files from the first tutorial, please make a backup copy and store it somewhere else. The files in will overwrite any changes you made to the files from the first tutorial.

We do the same drill from the first and second tutorial. Download the mymarket3.tar.gz file and extract it into your web root directory. For example, if your web root is in

/home/httpd/html

type


$ cd /home/httpd/html $ tar -zxf /tmp/mymarket3.tar.gz

Assuming that you’ve downloaded mymarket3.tar.gz into /tmp. Now, open up the file application.php and change the $CFG->wwwroot and $CFG->dirroot paths to match your server configuration.

Directory Structure

You should already be familiar with the contents of the mymarket directory, looking carefully, you will see a new directory called shopping.


drwxrws--- 3 apache web 1024 Jun 1 08:46 admin/
drwxrws--- 2 apache web 1024 Jun 1 02:05 images/
drwxrws--- 2 apache web 1024 Jun 1 09:18 lib/
drwxrws--- 2 apache web 1024 Jun 1 09:18 shopping
drwxrws--- 3 apache web 1024 Jun 1 11:20 templates/
drwxrws--- 3 apache web 1024 Jun 1 11:20 users/
-rw-rw---- 1 apache web 2074 Jun 1 14:58 application.php
-rw-rw---- 1 apache web 1488 Jun 1 12:21 index.php
-rw-rw---- 1 apache web 2427 Jun 1 14:51 login.php
-rw-rw---- 1 apache web 1040 Jun 1 11:19 logout.php

The shopping directory contains all the scripts that have to do with displaying the product catalog, viewing product details, the shopping cart, and purchasing. That is where most of the new scripts reside. We will now go over the script changes.

{mospagebreak title=Step 3: General Script Changes from Tutorial 2}

Weโ€™ve made a few changes since tutorial 2. A new library was added, a bunch of shopping related scripts, and more.

application.php

Now loads up the shopping cart library (lib/cart.php) and initializes the shopping cart if it hasn’t already been initialized.

index.php

The index page now lists the on-special items on the right of the screen. You mark an item as being on special from the administrative product manager screens (which have also been modified to accomodate this new feature).

admin/products.php

The products administration screen has been modified to handle the on-special flag. This is the first time we are dealing with checkboxes, they are wierd to deal with when processing HTML forms because they behave a bit differently from the other form input types. They are defined when checked, and undefined otherwise. This forces your scripts to check for the existence of form variable instead of checking for it’s value.

admin/orders.php

This is a new administrative function to show the orders that have taken place on the system. It is a simple listings of the orders with the ability to see the order details.

admin/users.php

Fixed a bug from part 2, every user you created got the password $password due to quoting problems. Thanks to the people who pointed it out ๐Ÿ™‚

lib/cart.php

This file contains the declaration of the Cart class. If you are new to objects in PHP, take a moment to look at how it is written. The session variable $SESSION["cart"] is an instance of this Cart class.

lib/mymarket.php

Added some new functions:

  • err2()
    Like the original err() function, except it prints the market >> instead of <<.
  • get_category_tree()
    Returns the category navigation tree as a string.
  • print_category_tree()
    Calls get_category_tree() and prints the result.
  • get_cart_items()
    Returns a handler ($qid) of a database query with the id, name, and unit price of all the items in the shopping cart.
  • chop_ccnum()
    Returns parts of a credit card number so that it is safe to display without giving out the customer’s credit card number.
  • save_orderinfo()
    Used in the order confirmation screen, this function saves the customer’s order information (which includes contact information and credit card information) into the session temporarily.
  • load_orderinfo()
    The counter part to save_orderinfo(), this will retrieve the customer’s order information from the session.
  • clear_orderinfo()
    Clears out the customer order information saved in the session, this is called once the orderinfo was successfully retrieved by load_orderinfo(). Together, these three functions are responsible for passing sensitive order information between the purchase and payment processing scripts.
  • Like the original err() function, except it prints the market instead of .Returns the category navigation tree as a string.Calls get_category_tree() and prints the result.Returns a handler () of a database query with the id, name, and unit price of all the items in the shopping cart.Returns parts of a credit card number so that it is safe to display without giving out the customer’s credit card number.Used in the order confirmation screen, this function saves the customer’s order information (which includes contact information and credit card information) into the session temporarily.The counter part to save_orderinfo(), this will retrieve the customer’s order information from the session.Clears out the customer order information saved in the session, this is called once the orderinfo was successfully retrieved by load_orderinfo(). Together, these three functions are responsible for passing sensitive order information between the purchase and payment processing scripts.

templates/header.php

The standard header template has been modified to show the shopping cart summary. So throughout the site, the user will always know their shopping cart summary.

{mospagebreak title=Step 4: New Shopping Scripts}

We’ve gone over the general script changes, let’s go through the product catalog, shopping cart, and purchaseing scripts (collectively called the shopping scripts). These are in the brand new shopping directory.

shopping/index.php

The main shopping index page is our product catalog page. I will display information for the selected level category (remember our discussion way before about what to show when going throught he product catalog — it is all here in this one file!). If no category is specified, it will start from the top level category.

shopping/product_details.php

This script simply loads up the details for the specified product and displays it on the screen. When a user is viewing the product details page, they can add the product to their shopping cart.

shopping/cart_add.php

This is a very simple script that adds the selected product to the shopping cart. This script is so simple that it does not even display anything, it just adds to the shopping cart and sends the customer back to where they came from.

shopping/cart_view.php

Viewing the shopping cart is a bit more work than adding to the cart. We have to show the customer the contents of their shopping cart. From here, the customer can choose to empty the shopping cart, to update the quantities of the items in the cart, or to make the purchase.

shopping/purchase_now.php

When the user decides to make the purchase, they will go to this script. At this point, we need to have the user log in so that we know who they are (up till this point, we didn’t care). This screen prompts the user for their billing information, and to specify and special instructions or comments for the purchase. After the user submits their information, they are asked to confirm the order.

shopping/complete_order.php

The user has filled out their purchase information, and confirmed the order. It is the responsibility of this script to talk to the payment authorization party and try to process they payment. As described earlier, this is the tricky part that requires your attention. I only provide a template of what should happen.

{mospagebreak title=Conclusion}

So there you have it, we’ve built a functional e-commerce site (well, except for the payment processing and order fulfillment parts). Hopefully you have learned a thing or two about how an e-commerce site works. We’ve built a toy system that is suitable for teaching / learning. To turn it into something you can sell things with is your job ๐Ÿ™‚

Here are some things to consider:

Site Security

When you are accepting payment or expecting the customer to enter in sensitive information, you site should be secure. Note that having SSL does not make your site "secure". Read up on SSL and what it does for you. A good resource is the modSSL homepage: http://www.modssl.org/

 

Shipping Calculation

If you are shipping physical goods, you need to include of shipping cost calculations in your totals. There are lots of pre-built PHP scripts that will help you calculate this, for example:

 

Tax Calculation

Tax sucks, but you have to deal with it. Some extra considerations for tax is that your customer can potentially be located anywhere in the world, so find out what tax rules apply and implement that accordingly.

 

Database Considerations
In this series, we have been using the MySQL database. We used it because MySQL is fast, easy to setup, and it is offered by most web hosting companies. For the purposes of this tutorial it was great, but it is not (at this point in time) suitable for real e-commerce use. The reason is that MySQL currently lacks a lot of features:

Transactions
MySQL currently does not support transactions. Transactions are used to make ensure that a series of statements either all get processed or don’t get processed at all — basically all or nothing. Why is this important? Let’s consider a simple banking example. You transfer $500 from one bank account into another:
  1. – $500 from account A
  2. + $500 to account B
Step 1 processes successfully, but something bad happens between step 1 and 2. Where is your money now — you just lost $500!! This is clearly not acceptable, we need to be able to confidently say that steps 1 and 2 must all process successfully, or none of it can process at all.

Enter the concept of transactions. With transactions, we would be able to do this:
  1. BEGIN TRANSACTION
  2. – $500 from account A
  3. + $500 to account B
  4. END TRANSACTION
Now steps 1 to 4 are treated as one atomic operation. If anything fails between steps 1 to 4, the entire operation is aborted and we revert back to the way things were before we started. The worst that can happen here is that the money doesn’t get transferred, but at least you will not lose $500.

So where does this come in with MyMarket? There are many places where we do multiple operations that should be treated as one atomic operation, for example when we add a new product. The steps are:
  1. INSERT the new product into the database and get the new product_id
  2. INSERT entries in the products_categories table for each category the product belongs to
Let’s say that step 1 processed successfully, but step 2 does not. We will now have a product with no categories attached to it, and you won’t know how to fix things because you don’t know what state your database is in.

Anyhow, the point is MySQL currently does not support transactions and you definitely need it for an e-commerce site.

Other Missing Features
There are also a number of other features missing from MySQL, but you can rework your scripts around this:
  1. Sub-selects
  2. UNIONs
  3. and more…
Once you become comfortable with the way the scripts work, a good exercise would be to move to a more powerful database. Some good candidates are:

Postgres (http://www.postgresql.org)
This is an Open-Source database that has a lot of features, including Transactions, Sub-selects, etc. There is good PHP support for PostgreSQL, and it is very simple to port dblib.php to use Postgres — basically search and replace the mysql_ functions with pg_.

SQL Server (http://www.microsoft.com/sql)
If you use PHP in Windows, SQL Server is a natural choice for a database. It’s obviously not free, but since you’re running in a Windows environment money probably isn’t an issue right :). All kidding aside, there is good PHP support for SQL Server. To port dblib.php to use SQL Server, basically search and replace the mysql_ functions with mssql_.

Oracle (http://www.oracle.com)
I don’t have experience with PHP+Oracle, but there is PHP support for Oracle. Looking at the Oracle functions, it would seem that porting dblib.php is not as straight forward as with the above databases.

Other E-Commerce Solutions

Now that you know the basics of how to build your own e-commerce site, you should take a look to see how some of the other ones are built. There are lots of Open-Source shopping carts and that sort of thing, for example:

So that concludes this 3 part series. Hope you enjoyed reading it! Happy programming!

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye