The CS619 E-Commerce Online Store is one of the most-often offered final year projects at Virtual University, and this guide builds it completely — in PHP and MySQL — so you can both learn and submit it. You start with the SRS: who uses the system, what it must do, and the rules (stock never goes below zero, prices are snapshotted into orders, and checkout runs inside a database transaction). Then you design the database with an ERD, write the code module by module with line-by-line explanations, test it with 20 test cases, and finish with viva questions and answers. The complete working code is included: login, dashboard, product and category management, catalogue with search, shopping cart, checkout, and order tracking. Follow the guide in order and you will have a working project and the understanding to defend it in the viva.
E-Commerce / Online Store — Learn + Build Guide
Course: CS619 Final Project, Virtual University of Pakistan Project: E-Commerce / Online Store (Top Project #3 — 9 projects in 4 semesters: Online Store, Web Based E-Commerce Site, B2C Online Store and Online Mobile Shop (Fall 2016); Online Mega Store, Online Jewellery Shop, PartsLo.pk (Spring 2025); Online PakBazar, Reloop Electronics Hub, SellPoint (Fall 2025); FashionHub (Spring 2026)) Stack: HTML, CSS, PHP, MySQL, WAMP Server, as stated in the official specification (Fall 2016 "Web Based E-Commerce Site": HTML, CSS, PHP, MySQL, WAMP Server; Fall 2016 "Online Store": PHP, MySQL). The Fall 2016 "Online Store" is described in the official list as: "Online shop where a business owner manages products, inventory and orders and customers browse, register and buy." The "Web Based E-Commerce Site" is an "online shop for phones, electronics and jewellery with a shopping cart and Bronze, Silver and Gold membership discounts." How to use this guide: build the project in the order of this guide — SRS, then Design, then Coding, then Testing, then Viva preparation. Every section first explains the idea in plain English, then shows the work. The complete code is in the files beside this guide; the Coding section explains each module and shows the important code with line-by-line notes.
1. Project overview — what you are building
A small shop keeps its records in notebooks: which products are in stock, who ordered what, and which orders are still waiting to be delivered. That is slow, stock counts go wrong, and orders get lost. This system replaces the notebooks with a web application — an online store.
What the system does
- The shop owner (admin) adds products and categories, manages stock, sees all orders, and changes an order's status as it moves from pending to confirmed, shipped and delivered.
- A customer registers (or logs in), browses the catalogue, searches by name or SKU, adds products to a shopping cart, checks out with a delivery address, and tracks their own orders.
- The system itself works out order totals, generates order numbers (ORD-0001, ORD-0002, ...), and reduces stock the moment an order is placed — never below zero.
Who uses it
| User | What they can do |
|---|---|
| Admin (shop owner) | Everything: products, categories, all orders, order status, dashboard |
| Customer | Browse and search products, cart, checkout, view their own orders |
Scope — what is in, and what is not
In scope: login with two roles, product catalogue and search, category management, product management, a database shopping cart, checkout with delivery details, order numbers, order items with price snapshots, stock decrease on order, order status workflow, dashboard with revenue and low-stock warnings, and a customer's own-orders page.
Out of scope (the official "Web Based E-Commerce Site" specification mentions membership discounts, but they are not built here): Bronze, Silver and Gold membership discounts, online card payment, email order confirmation, product images, and a customer "change password" page. These are listed as future improvements at the end of this guide. Saying clearly what you did NOT build — and why — is something viva examiners respect.
2. What you will learn (read this before you start coding)
You do not need to know everything below in advance. Learn each item when you reach the module that uses it.
| Topic | Where you use it |
|---|---|
| HTML forms (GET vs POST) | Every page: login, add product, cart, checkout |
| PHP basics: variables, if/else, loops, functions | Every file |
PHP sessions ($_SESSION) |
Login: remembering who is logged in |
| MySQL: tables, primary keys, foreign keys | schema.sql |
PDO prepared statements (? placeholders) |
Every database query — this is how SQL injection is stopped |
password_hash() / password_verify() |
Login |
SQL transactions (beginTransaction / commit / rollBack) |
Checkout — five changes that must happen together |
| SQL joins (products + categories, orders + users, cart + products) | Catalogue, cart, dashboard, orders |
ON DUPLICATE KEY UPDATE |
Add to Cart — same product twice increases the quantity |
DECIMAL(10, 2) for money |
Prices, totals, subtotals — never use FLOAT for money |
Suggested order of work (about 4–6 weeks for one student)
- Week 1: install XAMPP, create the database, write the SRS (Section 3).
- Week 2: design the database and draw the ERD (Section 4).
- Weeks 3–4: write the code module by module (Section 5).
- Week 5: run the test cases (Section 6) and fix the bugs they find.
- Week 6: prepare the final report and practise the viva questions (Section 7).
3. SRS — Software Requirements Specification
This section is written so you can copy it into your SRS document (with your own project details) — it follows the sections VU expects in an SRS: purpose, scope, actors, functional requirements, non-functional requirements, and use cases.
3.1 Purpose
To build a web-based E-Commerce / Online Store that lets a small business sell products online: customers browse, search and buy, and the shop owner manages products, stock and orders, replacing manual notebooks with a system that is fast, searchable and always up to date.
3.2 Scope
The system covers the daily work of a small online shop: catalogue management, category management, a shopping cart, checkout with delivery details, order numbers, order tracking, stock control, and reports (dashboard counts, revenue, low stock, recent orders). It does not cover online card payments, membership discounts, email notifications, or product images.
3.3 Actors
- Admin (shop owner): manages the whole system. There is one admin account to start with; more admin accounts can be added directly in the database.
- Customer: a registered shopper. Customers browse products, fill a cart, place orders and view their own orders. Customers cannot change the catalogue or see other customers' orders.
3.4 Functional requirements
| ID | Requirement |
|---|---|
| FR1 | The system shall let a user log in with a username and password. Wrong credentials shall be rejected with one general error message. |
| FR2 | The system shall show different menus for admin and customer after login. |
| FR3 | The admin shall add a product with name, SKU, description, price, stock and category. SKU shall be unique. |
| FR4 | The admin shall edit and delete products. A product that appears in any past order shall not be deletable. |
| FR5 | Any logged-in user shall search products by name or SKU and filter by category. |
| FR6 | The admin shall add, rename and delete categories. Deleting a category shall not delete its products (they become "Uncategorised"). |
| FR7 | A customer shall add a product to the cart with a quantity. Adding the same product again shall increase its quantity, not add a second row. |
| FR8 | The customer shall change quantities and remove products in the cart. The cart total shall be calculated from database prices. |
| FR9 | The customer shall check out with name, delivery address, phone and payment method (Cash on Delivery or Bank Transfer). |
| FR10 | On checkout, the system shall generate an order number (ORD-0001, ...), create the order and its items, decrease stock, and empty the cart — all in one transaction. |
| FR11 | Checkout shall be refused when the stock of any cart product is below the ordered quantity. |
| FR12 | Order items shall store a snapshot of the product name and unit price at order time, so later price changes never rewrite old orders. |
| FR13 | The admin shall see all orders, filter them by status, and change an order's status (pending → confirmed → shipped → delivered, or cancelled). |
| FR14 | A customer shall see only their own orders, with items, total and status. |
| FR15 | The admin dashboard shall show: total products, total categories, total customers, total orders, total revenue (non-cancelled orders), orders waiting, low-stock products, and recent orders. |
3.5 Non-functional requirements
| ID | Requirement |
|---|---|
| NFR1 | Security: passwords shall be stored as hashes, never as plain text. All database queries shall use prepared statements. Pages shall check login on every request. |
| NFR2 | Usability: every action shall show a success or error message. Forms shall keep the typed values when validation fails. |
| NFR3 | Correctness: stock shall never go below 0 (enforced by the database CHECK and by the code). An order total shall always equal the sum of its items. |
| NFR4 | Performance: the catalogue search and the dashboard shall answer in under 2 seconds with the sample data. |
| NFR5 | Portability: the system shall run on XAMPP (Apache + MySQL) or WAMP on Windows without extra software. |
3.6 Use cases
+-----------------------------+
| E-Commerce / Online Store |
| |
Admin ---------->| Login |
| | Manage Products |
| | Manage Categories |
| | View All Orders |
| | Change Order Status |
| | View Dashboard |
| |
Customer ------->| Login |
| Browse & Search Products |
| Add to Cart |
| Checkout |
| View My Orders |
+-----------------------------+
| Use case | Actor | Main flow |
|---|---|---|
| Login | Admin, Customer | Enter username + password → system checks credentials → opens the right home page |
| Add Product | Admin | Fill the product form → system validates (SKU unique) → product appears in the catalogue |
| Search Products | Admin, Customer | Type a word / pick a category → system lists matching products |
| Add to Cart | Customer | Pick a product and quantity → system checks stock → cart shows the item |
| Checkout | Customer | Fill delivery details → system runs the stock checks → order created, stock reduced, cart emptied |
| Change Order Status | Admin | Pick an order and a new status → system updates it |
| View Dashboard | Admin | System shows counts, revenue, low stock and recent orders |
| View My Orders | Customer | System shows the customer's own orders with items and status |
4. Design
4.1 Architecture
Customer / Admin browser
|
| HTTP (forms, links)
v
Apache web server (XAMPP / WAMP)
|
| PHP files (this project)
v
MySQL database (ecommerce_db)
There is no framework and no JavaScript framework. That is a deliberate choice for a final-year project: every line of code is yours, you can explain all of it in the viva, and nothing is hidden inside a library.
Request flow for every page: config.php (session + database connection)
→ header.php (login check + menu) → page logic → footer.php.
4.2 ERD — Entity Relationship Diagram
users (1) ───< cart (N) >─── (1) products
users (1) ───< orders (N) ───< order_items (N) >─── (1) products
categories (1) ───< products (N)
+----------------+ +----------------+
| users | | products |
| id (PK) | | id (PK) |
| name | | name |
| username | | sku (unique) |
| email | | description |
| password_hash | | price |
| role | | stock |
| phone, address | | category_id FK |
| status | +-------+--------+
Relationship
Meaning
users 1 — N cart (user_id)
One customer can have many cart rows; each cart row belongs to one customer.
products 1 — N cart (product_id)
One product can sit in many customers' carts; each cart row is for one product.
users 1 — N orders (customer_id)
One customer can place many orders; each order belongs to one customer.
orders 1 — N order_items (order_id)
One order has many items; each item belongs to one order.
products 1 — N order_items (product_id)
One product can appear in many orders; each item is for one product.
categories 1 — N products (category_id)
One category holds many products; each product has at most one category.
4.3 Database design, table by table
users — everyone who can log in. role is admin or customer.
password_hash holds a bcrypt hash, never the password itself. status
is active or suspended; only the admin can change it (by editing the
database — there is no member-management page in this project; see
future improvements).
categories — just id and name (unique). Kept as a table instead of
a text field so the category list is consistent and the "Add Product"
form can offer a dropdown.
products — one row per product. sku is unique — the same product
cannot be added twice by mistake. price is DECIMAL(10, 2): money
must never be stored as FLOAT, because FLOAT rounds (Rs. 99.99 can
become 99.98999...). stock is how many units are in the shop now. Two
rules protect it: a database CHECK (stock >= 0, price >= 0) and the
code, which only changes stock inside the checkout transaction
(Section 5.6). category_id is a foreign key with ON DELETE SET NULL:
deleting a category does not delete its products, it just clears their
category.
cart — the shopping cart, stored in the database (not in the PHP
session). One row per product per customer. UNIQUE (user_id, product_id)
means the same product can appear only once per customer — adding it
again increases the quantity (ON DUPLICATE KEY UPDATE). Storing the
cart in the database (rather than $_SESSION) means the cart survives a
logout and a browser restart — the customer finds their cart still
there tomorrow. A CHECK (quantity > 0) stops a zero-quantity row.
orders — one row per order. order_no is generated by the system
(ORD-0001, ...). total_amount is fixed at checkout time. status is
pending, confirmed, shipped, delivered or cancelled.
payment_method is cod (Cash on Delivery) or bank_transfer. The
shipping name, address and phone are COPIED from the customer at
checkout, so the order keeps the address it was sent to even if the
customer changes their address later.
order_items — the products inside one order. product_name and
unit_price are COPIES of the product's name and price at the moment of
ordering (a snapshot). If the admin later changes a product's name or
price, old orders still show what the customer actually paid.
subtotal = unit_price × quantity. This snapshot design is a good
example to quote in the viva when asked about a design decision.
Why the cart is a table, not a session array. A tempting design is
$_SESSION['cart'] = [product_id => quantity]. The problem: the cart
vanishes when the session ends (logout, browser closed, session timeout)
and it lives only on one computer. A cart table is permanent, survives
logout, and lets the admin (in a future version) see abandoned carts.
The cost is one extra table — worth it.
4.4 UI flow (page map)
index.php (Login)
|
+-- admin ----> dashboard.php
| |-- products.php <--> product_add / product_edit / product_delete
| |-- categories.php <--> category_add / category_edit / category_delete
| +-- orders.php <--> order_detail.php
|
+-- customer -> products.php (Shop — cards with Add to Cart)
|-- cart.php (view / update / remove)
|-- checkout.php --> order_detail.php
+-- my_orders.php <--> order_detail.php
4.5 Design decisions worth defending in the viva
- PDO prepared statements everywhere. User input is never pasted
into SQL. This stops SQL injection completely.
- Transactions for checkout. Placing an order is five changes
(order row, order items, stock decrease, cart cleared — plus the
order-number generation). A transaction makes them atomic: all happen
or none does.
FOR UPDATE row lock on checkout. If two customers order the
last unit at the same moment, the lock makes them wait in turn; the
second one then sees 0 stock and is refused.
- Price snapshot in order_items. Old orders never change when
prices change (see 4.3).
- Cart in the database, not the session (see 4.3).
DECIMAL for money, never FLOAT. Exact rupees, no rounding.
- No framework. Every line is the student's own code and can be
explained in the viva.
5. Coding — module by module
Each module is explained in plain English first, then the important code
is shown with line-by-line notes. The full code is in the files beside
this guide — read the module, then open its file and follow along.
5.1 Module 1 — Configuration and database connection (config.php)
What it does: every page needs two things — a session (to remember
who is logged in) and a database connection. config.php provides both,
plus small helper functions, so no page repeats that setup.
session_start(); // (1)
define('DB_HOST', 'localhost'); // (2)
define('DB_NAME', 'ecommerce_db');
define('DB_USER', 'root');
define('DB_PASS', ''); // XAMPP / WAMP default: root has no password
define('LOW_STOCK_LEVEL', 5); // (3)
define('CURRENCY', 'Rs.');
$pdo = new PDO( // (4)
'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4',
DB_USER, DB_PASS,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
Line by line: (1) session_start() must run before any HTML is printed,
or PHP shows a "headers already sent" error. (2) The database settings
are constants, so they are written once and used everywhere. (3) The
shop rules (low-stock warning at 5 units, currency "Rs.") are also
constants — changing a rule means changing one line, not searching every
file. (4) PDO is PHP's database layer. ERRMODE_EXCEPTION means a
database error throws an exception instead of failing silently.
FETCH_ASSOC means rows come back as ['name' => '...', 'price' => ...]
arrays.
Helpers in the same file: e() escapes text before printing it
(stops cross-site scripting); redirect() sends the browser elsewhere;
flash() / showFlash() carry a one-time message to the next page;
requireLogin() and requireAdmin() guard pages; nextOrderNo()
generates ORD-0001, ORD-0002, ... from the highest existing number;
formatPrice() prints 85000 as "Rs. 85,000".
5.2 Module 2 — Login and sessions (index.php, logout.php)
What it does: checks a username and password, then remembers the
user in the session until logout.
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && $user['status'] === 'active'
&& password_verify($password, $user['password_hash'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = (int) $user['id'];
$_SESSION['role'] = $user['role'];
// ... then redirect to dashboard (admin) or the shop (customer)
}
Line by line: the ? in the query is a placeholder — the username is
sent separately, so even a username like ' OR '1'='1 cannot change the
SQL (this is the SQL-injection defence). password_verify() compares
the typed password with the stored hash; the real password is never in
the database. session_regenerate_id(true) gives the user a fresh
session id after login, so an old session id cannot be reused.
$_SESSION is a server-side array that survives from page to page —
that is how the system "remembers" the login. The error message is
always the same ("Invalid username or password") whether the username
or the password was wrong, so an attacker cannot learn which usernames
exist.
5.3 Module 3 — Product catalogue (products.php, product_add.php, product_edit.php, product_delete.php)
What it does: list and search products; add, edit and delete them
(admin only). Customers see the same products as cards with a price,
a stock note and an "Add to Cart" form.
Search and category filter (from products.php):
$conditions = [];
$params = [];
if ($search !== '') {
$conditions[] = '(p.name LIKE ? OR p.sku LIKE ?)';
$like = '%' . $search . '%';
$params[] = $like;
$params[] = $like;
}
if ($categoryId !== null) {
$conditions[] = 'p.category_id = ?';
$params[] = $categoryId;
}
$where = $conditions ? ('WHERE ' . implode(' AND ', $conditions)) : '';
$stmt = $pdo->prepare(
'SELECT p.*, c.name AS category_name
FROM products p
LEFT JOIN categories c ON c.id = p.category_id
' . $where . '
ORDER BY p.name ASC'
);
$stmt->execute($params);
LEFT JOIN brings in the category name; a product with no category
still appears (its category shows as "—"). LIKE '%word%' matches the
word anywhere in the name or SKU. The WHERE clause is built only from
the filters the user actually chose, and every value is bound as a
parameter — the filter text itself can never reach the SQL as code.
Add (from product_add.php): a new product starts with the stock the
admin types. SKU is unique; adding the same SKU twice fails with
database error 23000, which the code catches and turns into the friendly
message "A product with this SKU already exists."
Delete (from product_delete.php): two rules. First, deletion needs a
confirmation page with a POST form, so a plain link can never delete
anything. Second, a product that appears in any order cannot be deleted,
because order_items references it and old orders must keep their
history. The admin is told to set its stock to 0 instead.
5.4 Module 4 — Categories (categories.php, category_add.php, category_edit.php, category_delete.php)
What it does: list categories with a product count per category, and
add, rename and delete them (admin only).
Renaming a category changes it everywhere at once, because products
point to the category by id, not by name — that is why a separate
table is better than typing the category name on every product.
Delete: the foreign key is ON DELETE SET NULL, so deleting a category
never deletes its products — they become "Uncategorised". The
confirmation page shows how many products that will affect, so the
admin deletes with their eyes open.
5.5 Module 5 — Shopping cart (cart_add.php, cart.php)
What it does: keep the customer's chosen products in the cart
table, and let the customer change quantities, remove items, and see
the total.
Add to cart (from cart_add.php) — the one-query trick:
$stmt = $pdo->prepare(
'INSERT INTO cart (user_id, product_id, quantity) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity)'
);
$stmt->execute([$userId, $productId, $quantity]);
Because of UNIQUE (user_id, product_id), adding a product that is
already in the cart does not create a second row — MySQL runs the
ON DUPLICATE KEY UPDATE part instead and increases the quantity. One
query, no "check first, then insert" race. Before this, the code checks
that the new total quantity is not more than the stock.
Cart total (from cart.php): the total is calculated from the prices in
the database, never from values the browser sends — a customer cannot
change a price by editing a form field, because prices are never in the
form. Each quantity change is checked against the stock, and setting a
quantity to 0 removes the row.
5.6 Module 6 — Checkout (checkout.php) — the most important module
What it does: turns the cart into an order, after stock checks,
inside a transaction.
$pdo->beginTransaction(); // (1)
// Re-read the cart inside the transaction and lock the rows.
foreach ($items as $item) {
$stmt = $pdo->prepare('SELECT * FROM products WHERE id = ? FOR UPDATE');
$stmt->execute([(int) $item['product_id']]); // (2)
$product = $stmt->fetch();
if ((int) $product['stock'] < (int) $item['quantity']) {
throw new Exception('Not enough stock for "' . $product['name'] . '".');
}
// ... remember product + quantity, add price x quantity to the total
}
// (3) Insert the order row with a generated order number.
$orderNo = nextOrderNo($pdo);
$stmt = $pdo->prepare(
'INSERT INTO orders
(order_no, customer_id, total_amount, status, payment_method,
shipping_name, shipping_address, shipping_phone)
VALUES (?, ?, ?, \'pending\', ?, ?, ?, ?)'
);
$stmt->execute([...]);
$orderId = (int) $pdo->lastInsertId();
// (4) Insert the order items (price snapshot) and decrease the stock.
foreach ($locked as $line) {
$stmt = $pdo->prepare(
'INSERT INTO order_items
(order_id, product_id, product_name, unit_price, quantity, subtotal)
VALUES (?, ?, ?, ?, ?, ?)'
);
$stmt->execute([$orderId, (int) $product['id'], $product['name'],
$price, $quantity, $price * $quantity]);
$stmt = $pdo->prepare('UPDATE products SET stock = stock - ? WHERE id = ?');
$stmt->execute([$quantity, (int) $product['id']]);
}
// (5) Empty the cart.
$stmt = $pdo->prepare('DELETE FROM cart WHERE user_id = ?');
$stmt->execute([$userId]);
$pdo->commit(); // (6)
Line by line: (1) beginTransaction() starts the transaction — from
here, nothing is saved until commit(). (2) FOR UPDATE locks the
product row: if another customer is ordering the same product at the
same moment, they wait until this transaction finishes, then see the
updated stock. (3) The order number comes from nextOrderNo() — the
highest existing ORD number plus 1. (4) Each order item stores the
product name and price as they are RIGHT NOW (the snapshot), and the
stock goes down by the ordered quantity. (5) The cart is emptied — the
products are now an order, not a cart. (6) commit() saves all five
changes together. If any check throws an exception, the catch block
calls rollBack() and nothing is saved — the database can never hold
an order without its items, or stock taken without an order.
5.7 Module 7 — Orders and dashboard (orders.php, order_detail.php, my_orders.php, dashboard.php)
What it does: turns the stored rows into answers.
orders.php (admin) lists all orders, filterable by status. The
filter is checked against an allowed list (all, pending,
confirmed, shipped, delivered, cancelled) before it reaches
the SQL — a made-up filter value can never change the query. The admin
changes a status with a small form on each row; the new status is also
checked against an allowed list.
order_detail.php shows one order with its items. An admin can open
any order; a customer can open only their own — the check uses the
customer id from the session, never from the URL, so a customer cannot
view another customer's order by changing the id in the address bar.
Prices shown here come from order_items (the snapshot), not from
the products table.
my_orders.php takes the customer id from the session, never from
the URL — for the same reason.
- Dashboard counts are single
COUNT(*) / SUM() queries — cheap and
fast. Revenue is SUM(total_amount) over non-cancelled orders only
(a cancelled order is not revenue). The low-stock list is
WHERE stock <= LOW_STOCK_LEVEL (5), ordered by stock so the emptiest
product is first.
6. Testing — test cases to run before submission
Run every case below against the working system and record Pass/Fail.
A failed case is not a disaster — it is what testing is for; fix the
bug, then run the case again.
ID
Test
Steps
Expected result
TC-1
Login (admin)
Log in as admin / admin123
Dashboard opens, admin menu shown
TC-2
Login (customer)
Log in as customer / customer123
Shop catalogue opens, customer menu shown (no Dashboard link)
TC-3
Wrong password
Log in as admin with a wrong password
"Invalid username or password", still on login page
TC-4
Page guard
Open dashboard.php without logging in
Sent back to the login page
TC-5
Customer guard
As a customer, open orders.php directly
Sent back to the catalogue with an error
TC-6
Add product
Add a product with 10 units in stock
Product appears in the list with stock 10
TC-7
Duplicate SKU
Add the same SKU again
"A product with this SKU already exists"
TC-8
Search
Search "Galaxy"
Only the Samsung Galaxy A54 is listed
TC-9
Category filter
Pick category "Fashion"
Only Fashion products are listed
TC-10
Add to cart
Add 2 x Samsung Galaxy A54 to the cart
Cart shows 2 units, subtotal Rs. 170,000
TC-11
Add same product again
Add 1 more of the same product
Still one cart row, quantity now 3
TC-12
Add more than stock
Try to add 11 units of a product with 10 in stock
Refused: "Only 10 unit(s) ... are in stock"
TC-13
Update quantity
Change a cart quantity to 1
Cart total updates
TC-14
Remove from cart
Remove a product from the cart
Product disappears, total updates
TC-15
Checkout
Check out with 2 phones (Rs. 85,000 each)
Order ORD-0001 created, total Rs. 170,000, status "pending"
TC-16
Stock decreases
After TC-15, check the product
Stock is 10 − 2 = 8
TC-17
Cart emptied
After TC-15, open the cart
Cart is empty
TC-18
Order detail snapshot
Change the phone's price, then open the old order
Old order still shows Rs. 85,000 per unit
TC-19
My Orders privacy
As Ayesha, open the order of the customer account
Sent back to My Orders with an error
TC-20
Delete with history
Delete a product that appears in a past order
Refused: product appears in past orders
Verification run (7 Nov 2026, PHP 8.3 + MariaDB 10.11): all 20 PHP files
pass php -l; schema imports cleanly (6 tables, 3 users, 10 products);
admin login reaches the dashboard; customer login reaches the catalogue;
wrong password is rejected; unauthenticated dashboard redirects to login;
a test order (2 phones) created ORD-0001 for Rs. 170,000, reduced stock
from 10 to 8, and emptied the cart; the order detail page shows the
placed-order message; the admin order list shows ORD-0001; a second
customer is blocked from opening another customer's order. Test data
was rolled back afterwards (database re-imported to the clean sample
state).
7. Viva Q&A — questions the examiner is likely to ask
Q1. What does your project do?
It is an online store: the shop owner manages products, categories and
orders, and customers browse the catalogue, fill a shopping cart, check
out, and track their own orders. The system generates order numbers and
reduces stock when an order is placed.
Q2. Which technologies did you use, and why?
HTML, CSS, PHP, MySQL and WAMP Server — the stack in the official project
specification for the Fall 2016 "Web Based E-Commerce Site" (the Fall
2016 "Online Store" states PHP and MySQL). No framework, so all the code
is mine and I can explain every line.
Q3. Show me your database design. Why six tables?
users (login), categories, products (catalogue with price and
stock), cart (one row per product per customer), orders (one row per
order), order_items (the products inside an order, with a price
snapshot). Six tables is enough because a cart row and an order item are
the only things that connect a customer to a product. The ERD is in
Section 4.2.
Q4. What is a foreign key? Give an example from your project.
A column that must match a primary key in another table.
order_items.order_id must be an id from orders — the database
refuses an order item for an order that does not exist.
Q5. How do you stop SQL injection?
Every query uses PDO prepared statements with ? placeholders. User
input is sent as data, never pasted into the SQL string, so input like
' OR '1'='1 is treated as a plain value, not as SQL.
Q6. How are passwords stored?
With password_hash(), which makes a bcrypt hash. The plain password is
never stored. Login checks with password_verify().
Q7. Why is the cart a database table instead of a session array?
Because a session cart vanishes on logout or session timeout, and it
lives only on one computer. A cart table survives logout, survives a
browser restart, and lets the shop see abandoned carts later.
Q8. What is a transaction, and where did you use one?
A group of database changes that succeed together or fail together.
Checkout is five changes (order row, order items, stock decrease, cart
cleared — plus the order number), so they run in one transaction; if the
stock update failed, the order row would be rolled back and the data
would stay consistent.
Q9. Two customers order the last unit at the same moment. What happens?
The first transaction locks the product row with FOR UPDATE. The second
waits, then sees 0 stock and is refused with "Not enough stock". Without
the lock, both could succeed and stock would go to −1.
Q10. A product's price changes. Why does an old order still show the old price?
Because order_items stores a snapshot of the name and unit price at
order time. The order shows what the customer actually paid, not the
current catalogue price. This is the correct design for any shop.
Q11. Why DECIMAL and not FLOAT for prices?
FLOAT stores approximate values and rounds — Rs. 99.99 can become
99.98999. DECIMAL(10, 2) stores exact rupees and paisa, so totals always
add up exactly.
Q12. How does the system know who is logged in on each page?
PHP sessions. After login, the user id, name and role are stored in
$_SESSION. Every page includes config.php (which starts the session)
and header.php (which calls requireLogin()).
Q13. How do you stop a customer seeing another customer's order?
my_orders.php and the owner check in order_detail.php take the
customer id from the session, never from the URL. Changing the id in the
address bar cannot show another customer's order.
Q14. What would you add if you had more time?
Bronze, Silver and Gold membership discounts (from the official
specification), online card payment, email order confirmation, product
images, a customer registration page, and a "change my password" page.
Membership discounts would need a membership_tier on the user and a
discount step in the checkout total.
Q15. What was the hardest bug you fixed?
(The student should answer from their own testing — for example the
checkout transaction rolling back when a stock check failed, or the
ON DUPLICATE KEY UPDATE cart logic. Do not memorise an answer to this
one; it must be true.)
8. Deliverables checklist (what to submit)
- [ ] SRS document (Section 3 of this guide, written as your own document)
- [ ] Design document (ERD, architecture, UI flow — Section 4)
- [ ] Source code (all files in this folder, working)
- [ ] Database file (
schema.sql)
- [ ] Test report (the test cases in Section 6, with Pass/Fail filled in)
- [ ] Final report (what you built, how, and what you learned)
- [ ] Presentation slides for the final viva
- [ ] Demo video or live demo (run the system on your own laptop)
9. Common errors and fixes
Error / symptom
Cause
Fix
"Database connection failed"
MySQL is not running, or schema.sql was not imported
Start MySQL in XAMPP; import schema.sql in phpMyAdmin
"Table 'ecommerce_db.users' doesn't exist"
The schema was imported into the wrong database, or not at all
Re-import schema.sql (it creates the database itself)
"headers already sent"
Something printed output before session_start() or header()
Check for blank lines or spaces before <?php in the file; config.php must be included first
Login always fails
The sample users were not imported, or the hash was changed
Re-import schema.sql; check the user exists in phpMyAdmin
Stock goes below 0
Ordering without the stock check, or without the transaction
Use checkout.php as written; never update stock by hand in phpMyAdmin
"A product with this SKU already exists"
SKU is unique in the database
This is correct behaviour — use a different SKU for a different product
Cart shows the wrong total
The total was calculated from browser-sent prices
The total must come from the database prices only, as in cart.php
Apache will not start
Port 80 is already in use
Stop the other program, or change Apache's port in the XAMPP control panel
Page shows nothing (white screen)
A PHP error is hidden by the server settings
Temporarily add error_reporting(E_ALL); ini_set('display_errors', '1'); at the top of config.php to see the message, then remove it
10. Future improvements
- Membership discounts — Bronze, Silver and Gold tiers from the
official specification: a
membership_tier column on users and a
discount step in the checkout total (for example 0% / 5% / 10%).
- Online payment — card payment at checkout instead of Cash on
Delivery only; store a payment reference on the order.
- Customer registration page — let shoppers create their own
account instead of the admin creating it.
- Product images — an image filename per product and an upload
form in the admin panel (images stored in the project folder).
- Email order confirmation — send the customer their order number
and total when an order is placed.
- Change password page — let customers change their own password
after first login instead of the admin setting it.
- Pagination — split the catalogue into pages when it grows past a
few dozen products.
Frequently Asked Questions
- Which stack does this E-Commerce Online Store use? HTML, CSS, PHP, MySQL and WAMP Server, as stated in the official VU specification for the Fall 2016 "Web Based E-Commerce Site" (the Fall 2016 "Online Store" states PHP and MySQL). The demo accounts are admin/admin123 and customer/customer123.
- Does the code really work? Yes. It was runtime-verified: all 20 PHP files pass
php -l, the schema imports cleanly into MySQL, the admin and customer logins work, and placing an order creates the order, empties the cart and reduces the stock (tested: 2 phones ordered, stock 10 → 8).
- Can I use this project for my CS619 final year project? Yes — it follows VU's own phases (SRS, design, coding, testing, viva). Change the sample data, add your own features from the future-improvements list, and write the SRS in your own words before submitting.
+-------+--------+ | N
| 1 |
| | 1
* +-------v--------+
+---v------------+ | categories |
| orders | | id (PK) |
| id (PK) | | name |
| order_no | +--------------+
| customer_id FK |
| total_amount |
| status |
| payment_method |
| shipping_* |
+-------+--------+
| 1
|
* +----------------+
+---v----+| order_items |
| || id (PK) |
+--------+| order_id (FK) |
| product_id (FK)|
| product_name |
| unit_price |
| quantity |
| subtotal |
+----------------+
cart: id (PK), user_id (FK), product_id (FK), quantity,
UNIQUE (user_id, product_id)
Relationships (read "1" as "one", "N" as "many"):
Comments
Post a Comment