Introduction to the Project
The Online Marketplace is a comprehensive, full-stack e-commerce platform that connects buyers and sellers in a multi-vendor environment. This sophisticated system allows multiple sellers to register, list products, manage inventory, and process orders, while providing buyers with a seamless shopping experience including product search, cart management, and secure checkout.
The platform features role-based access control with four distinct user types: Super Admin, Vendors/Sellers, Customers, and Delivery Personnel. The system includes advanced features such as commission-based revenue model, vendor ratings, product reviews, wishlists, and comprehensive analytics for business intelligence.
Key Features
Super Admin Features
- Dashboard Overview: Platform-wide statistics, revenue analytics, user growth
- Vendor Management: Approve/Reject vendor applications, manage vendor accounts
- Category Management: Create and manage product categories and subcategories
- Commission Settings: Set category-wise and vendor-wise commission rates
- Order Management: View and manage all orders across the platform
- Payment Management: Track vendor payouts, commission deductions
- Dispute Resolution: Handle customer-vendor disputes
- Marketing Tools: Create coupons, discounts, and promotional campaigns
- Analytics & Reports: Generate detailed sales, revenue, and performance reports
- System Settings: Configure shipping methods, payment gateways, tax rates
Vendor Features
- Vendor Dashboard: Real-time sales, revenue, and order statistics
- Product Management: Add, edit, delete products with images and variants
- Inventory Management: Track stock levels, low stock alerts
- Order Management: View and process orders, update order status
- Shipping Management: Configure shipping rates and zones
- Coupon Management: Create store-specific discounts and offers
- Customer Reviews: View and respond to product reviews
- Sales Analytics: Track performance, best-selling products
- Payout Management: View earnings, withdrawal requests, transaction history
- Store Profile: Customize store banner, logo, and description
Customer Features
- User Dashboard: Order history, wishlist, saved addresses
- Product Browsing: Search, filter, and sort products by various criteria
- Advanced Search: Search by keywords, categories, price range, ratings
- Product Details: View images, specifications, reviews, and ratings
- Shopping Cart: Add/remove items, update quantities, save for later
- Wishlist: Save favorite products for future purchase
- Secure Checkout: Multiple payment options, address management
- Order Tracking: Real-time order status and tracking information
- Product Reviews: Rate and review purchased products
- Vendor Follow: Follow favorite vendors for updates
General Features
- Multi-vendor Support: Multiple sellers with individual stores
- Commission System: Automatic commission calculation per sale
- Payment Integration: Stripe, PayPal, Razorpay support
- Shipping Integration: Multiple shipping methods, real-time rates
- Review & Rating: Product and vendor rating system
- Wishlist: Save products for later
- Compare Products: Side-by-side product comparison
- Recently Viewed: Track browsing history
- Email Notifications: Order confirmations, shipping updates
- Mobile Responsive: Fully responsive design for all devices
Technology Stack
- Frontend: HTML5, CSS3, JavaScript (ES6+), Bootstrap 5
- Backend: PHP 8.0+ (Core PHP with MVC-inspired structure)
- Database: MySQL 5.7+
- Additional Libraries:
- Bootstrap 5 for responsive UI
- Font Awesome for icons
- jQuery for AJAX operations
- Select2 for enhanced dropdowns
- DataTables for advanced tables
- Chart.js for analytics
- SweetAlert2 for beautiful alerts
- Owl Carousel for product sliders
- Stripe/PayPal SDK for payments
- PHPMailer for emails
- Intervention Image for image processing
- FPDF for PDF generation (invoices)
Project File Structure
marketplace/ │ ├── assets/ │ ├── css/ │ │ ├── style.css │ │ ├── vendor.css │ │ ├── admin.css │ │ ├── responsive.css │ │ └── dark-mode.css │ ├── js/ │ │ ├── main.js │ │ ├── cart.js │ │ ├── checkout.js │ │ ├── vendor.js │ │ ├── admin.js │ │ └── validation.js │ ├── images/ │ │ ├── products/ │ │ ├── vendors/ │ │ ├── categories/ │ │ └── banners/ │ └── plugins/ │ ├── datatables/ │ ├── select2/ │ └── ckeditor/ │ ├── includes/ │ ├── config.php │ ├── Database.php │ ├── functions.php │ ├── auth.php │ ├── Product.php │ ├── Category.php │ ├── Vendor.php │ ├── Order.php │ ├── Cart.php │ ├── Payment.php │ ├── Shipping.php │ ├── Review.php │ └── helpers/ │ ├── ImageHelper.php │ ├── PriceHelper.php │ └── ValidationHelper.php │ ├── admin/ │ ├── dashboard.php │ ├── vendors.php │ ├── vendor-details.php │ ├── categories.php │ ├── products.php │ ├── orders.php │ ├── payments.php │ ├── payouts.php │ ├── coupons.php │ ├── reports.php │ ├── settings.php │ └── users.php │ ├── vendor/ │ ├── dashboard.php │ ├── products.php │ ├── add-product.php │ ├── edit-product.php │ ├── inventory.php │ ├── orders.php │ ├── order-details.php │ ├── earnings.php │ ├── withdraw.php │ ├── coupons.php │ ├── reviews.php │ ├── profile.php │ ├── settings.php │ └── shipping.php │ ├── customer/ │ ├── dashboard.php │ ├── browse.php │ ├── product.php │ ├── cart.php │ ├── checkout.php │ ├── orders.php │ ├── order-details.php │ ├── wishlist.php │ ├── profile.php │ ├── addresses.php │ └── compare.php │ ├── api/ │ ├── cart.php │ ├── wishlist.php │ ├── search.php │ ├── shipping.php │ ├── payment.php │ └── vendor.php │ ├── uploads/ │ ├── products/ │ ├── vendors/ │ ├── categories/ │ └── banners/ │ ├── vendor/ │ ├── index.php ├── shop.php ├── product.php ├── login.php ├── register.php ├── vendor-register.php ├── forgot-password.php ├── logout.php ├── .env ├── .gitignore ├── composer.json ├── .htaccess └── sql/ └── database.sql
Database Schema
File: sql/database.sql
-- Create Database CREATE DATABASE IF NOT EXISTS `marketplace`; USE `marketplace`; -- Users Table (for all users) CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_type` ENUM('admin', 'vendor', 'customer') NOT NULL DEFAULT 'customer', `email` VARCHAR(100) UNIQUE NOT NULL, `password` VARCHAR(255) NOT NULL, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `phone` VARCHAR(20), `profile_image` VARCHAR(255) DEFAULT 'default.png', `status` ENUM('active', 'inactive', 'suspended') DEFAULT 'active', `email_verified` BOOLEAN DEFAULT FALSE, `verification_token` VARCHAR(255), `reset_token` VARCHAR(255), `reset_expires` DATETIME, `last_login` DATETIME, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_email` (`email`), INDEX `idx_user_type` (`user_type`) ); -- Vendors Table (extends users) CREATE TABLE `vendors` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_id` INT(11) NOT NULL, `store_name` VARCHAR(100) NOT NULL, `store_slug` VARCHAR(100) UNIQUE NOT NULL, `store_logo` VARCHAR(255), `store_banner` VARCHAR(255), `store_description` TEXT, `store_address` TEXT, `store_city` VARCHAR(100), `store_state` VARCHAR(50), `store_country` VARCHAR(50), `store_zip` VARCHAR(20), `store_phone` VARCHAR(20), `store_email` VARCHAR(100), `commission_rate` DECIMAL(5,2) DEFAULT 0.00, -- percentage `payment_method` ENUM('bank', 'paypal', 'stripe') DEFAULT 'bank', `payment_details` TEXT, `rating` DECIMAL(3,2) DEFAULT 0.00, `total_reviews` INT DEFAULT 0, `total_products` INT DEFAULT 0, `total_sales` INT DEFAULT 0, `total_earnings` DECIMAL(10,2) DEFAULT 0.00, `available_balance` DECIMAL(10,2) DEFAULT 0.00, `withdrawn_amount` DECIMAL(10,2) DEFAULT 0.00, `is_verified` BOOLEAN DEFAULT FALSE, `is_featured` BOOLEAN DEFAULT FALSE, `status` ENUM('pending', 'approved', 'rejected', 'suspended') DEFAULT 'pending', `approved_at` DATETIME, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, UNIQUE KEY `unique_store_slug` (`store_slug`), INDEX `idx_status` (`status`), INDEX `idx_featured` (`is_featured`) ); -- Categories Table CREATE TABLE `categories` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `parent_id` INT(11) DEFAULT NULL, `name` VARCHAR(100) NOT NULL, `slug` VARCHAR(100) UNIQUE NOT NULL, `description` TEXT, `image` VARCHAR(255), `icon` VARCHAR(50), `commission_rate` DECIMAL(5,2) DEFAULT NULL, -- overrides vendor commission `is_featured` BOOLEAN DEFAULT FALSE, `status` BOOLEAN DEFAULT TRUE, `sort_order` INT DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`parent_id`) REFERENCES `categories`(`id`) ON DELETE CASCADE, INDEX `idx_status` (`status`) ); -- Brands Table CREATE TABLE `brands` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `slug` VARCHAR(100) UNIQUE NOT NULL, `logo` VARCHAR(255), `description` TEXT, `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ); -- Products Table CREATE TABLE `products` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `vendor_id` INT(11) NOT NULL, `category_id` INT(11), `brand_id` INT(11), `name` VARCHAR(255) NOT NULL, `slug` VARCHAR(255) UNIQUE NOT NULL, `sku` VARCHAR(100) UNIQUE, `short_description` TEXT, `description` LONGTEXT, `specifications` JSON, `price` DECIMAL(10,2) NOT NULL, `compare_price` DECIMAL(10,2), `cost_price` DECIMAL(10,2), `quantity` INT DEFAULT 0, `low_stock_threshold` INT DEFAULT 5, `weight` DECIMAL(8,2), `dimensions` VARCHAR(50), `featured_image` VARCHAR(255), `gallery_images` JSON, `video_url` VARCHAR(255), `tags` TEXT, `meta_title` VARCHAR(255), `meta_description` TEXT, `meta_keywords` TEXT, `is_featured` BOOLEAN DEFAULT FALSE, `is_new` BOOLEAN DEFAULT TRUE, `is_on_sale` BOOLEAN DEFAULT FALSE, `sale_starts` DATETIME, `sale_ends` DATETIME, `status` ENUM('draft', 'pending', 'approved', 'rejected') DEFAULT 'draft', `approved_at` DATETIME, `total_views` INT DEFAULT 0, `total_sales` INT DEFAULT 0, `rating` DECIMAL(3,2) DEFAULT 0.00, `total_reviews` INT DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON DELETE CASCADE, FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE SET NULL, FOREIGN KEY (`brand_id`) REFERENCES `brands`(`id`) ON DELETE SET NULL, INDEX `idx_status` (`status`), INDEX `idx_featured` (`is_featured`), INDEX `idx_price` (`price`), FULLTEXT INDEX `idx_search` (`name`, `short_description`, `description`, `tags`) ); -- Product Variants (for products with options like size, color) CREATE TABLE `product_variants` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `product_id` INT(11) NOT NULL, `sku` VARCHAR(100) UNIQUE NOT NULL, `attributes` JSON NOT NULL, -- e.g., {"color": "red", "size": "M"} `price_adjustment` DECIMAL(10,2) DEFAULT 0.00, `quantity` INT DEFAULT 0, `image` VARCHAR(255), `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE ); -- Product Images CREATE TABLE `product_images` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `product_id` INT(11) NOT NULL, `image_path` VARCHAR(255) NOT NULL, `is_primary` BOOLEAN DEFAULT FALSE, `sort_order` INT DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE ); -- Customer Addresses CREATE TABLE `addresses` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_id` INT(11) NOT NULL, `address_type` ENUM('shipping', 'billing') DEFAULT 'shipping', `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `company` VARCHAR(100), `address_line1` VARCHAR(255) NOT NULL, `address_line2` VARCHAR(255), `city` VARCHAR(100) NOT NULL, `state` VARCHAR(50) NOT NULL, `country` VARCHAR(50) NOT NULL, `postal_code` VARCHAR(20) NOT NULL, `phone` VARCHAR(20), `email` VARCHAR(100), `is_default` BOOLEAN DEFAULT FALSE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ); -- Shopping Cart CREATE TABLE `cart` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_id` INT(11), `session_id` VARCHAR(100), `product_id` INT(11) NOT NULL, `variant_id` INT(11), `quantity` INT NOT NULL, `price` DECIMAL(10,2) NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE, FOREIGN KEY (`variant_id`) REFERENCES `product_variants`(`id`) ON DELETE CASCADE, INDEX `idx_session` (`session_id`) ); -- Wishlist CREATE TABLE `wishlist` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_id` INT(11) NOT NULL, `product_id` INT(11) NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE, UNIQUE KEY `unique_wishlist` (`user_id`, `product_id`) ); -- Orders Table CREATE TABLE `orders` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `order_number` VARCHAR(50) UNIQUE NOT NULL, `user_id` INT(11) NOT NULL, `billing_address_id` INT(11), `shipping_address_id` INT(11), `subtotal` DECIMAL(10,2) NOT NULL, `discount_amount` DECIMAL(10,2) DEFAULT 0.00, `tax_amount` DECIMAL(10,2) DEFAULT 0.00, `shipping_amount` DECIMAL(10,2) DEFAULT 0.00, `total_amount` DECIMAL(10,2) NOT NULL, `payment_method` VARCHAR(50), `payment_status` ENUM('pending', 'paid', 'failed', 'refunded') DEFAULT 'pending', `payment_details` JSON, `order_status` ENUM('pending', 'processing', 'confirmed', 'shipped', 'delivered', 'cancelled', 'refunded') DEFAULT 'pending', `shipping_method` VARCHAR(50), `tracking_number` VARCHAR(100), `notes` TEXT, `cancelled_at` DATETIME, `delivered_at` DATETIME, `ip_address` VARCHAR(45), `user_agent` TEXT, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), FOREIGN KEY (`billing_address_id`) REFERENCES `addresses`(`id`), FOREIGN KEY (`shipping_address_id`) REFERENCES `addresses`(`id`), INDEX `idx_order_number` (`order_number`), INDEX `idx_status` (`order_status`) ); -- Order Items CREATE TABLE `order_items` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `order_id` INT(11) NOT NULL, `vendor_id` INT(11) NOT NULL, `product_id` INT(11) NOT NULL, `variant_id` INT(11), `product_name` VARCHAR(255) NOT NULL, `variant_details` JSON, `quantity` INT NOT NULL, `price` DECIMAL(10,2) NOT NULL, `total` DECIMAL(10,2) NOT NULL, `commission_rate` DECIMAL(5,2), `commission_amount` DECIMAL(10,2), `vendor_earnings` DECIMAL(10,2), `status` ENUM('pending', 'confirmed', 'shipped', 'delivered', 'cancelled', 'refunded') DEFAULT 'pending', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`) ON DELETE CASCADE, FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`), FOREIGN KEY (`product_id`) REFERENCES `products`(`id`), INDEX `idx_vendor` (`vendor_id`), INDEX `idx_status` (`status`) ); -- Order Status History CREATE TABLE `order_status_history` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `order_id` INT(11) NOT NULL, `status` VARCHAR(50) NOT NULL, `comment` TEXT, `created_by` INT(11), `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`) ON DELETE CASCADE ); -- Reviews Table CREATE TABLE `reviews` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `product_id` INT(11) NOT NULL, `user_id` INT(11) NOT NULL, `order_id` INT(11), `rating` TINYINT NOT NULL CHECK (rating >= 1 AND rating <= 5), `title` VARCHAR(255), `comment` TEXT, `images` JSON, `likes` INT DEFAULT 0, `dislikes` INT DEFAULT 0, `status` ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE, FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`) ON DELETE SET NULL, UNIQUE KEY `unique_review` (`product_id`, `user_id`, `order_id`), INDEX `idx_status` (`status`) ); -- Coupons Table CREATE TABLE `coupons` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `vendor_id` INT(11), `code` VARCHAR(50) UNIQUE NOT NULL, `type` ENUM('percentage', 'fixed') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `min_order_amount` DECIMAL(10,2), `max_discount` DECIMAL(10,2), `usage_limit` INT, `used_count` INT DEFAULT 0, `per_user_limit` INT DEFAULT 1, `applicable_products` JSON, -- product IDs `applicable_categories` JSON, -- category IDs `excluded_products` JSON, `starts_at` DATETIME, `expires_at` DATETIME, `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON DELETE CASCADE, INDEX `idx_code` (`code`) ); -- Payments Table CREATE TABLE `payments` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `order_id` INT(11) NOT NULL, `transaction_id` VARCHAR(255) UNIQUE NOT NULL, `amount` DECIMAL(10,2) NOT NULL, `payment_method` VARCHAR(50) NOT NULL, `payment_status` ENUM('pending', 'completed', 'failed', 'refunded') NOT NULL, `payment_details` JSON, `refund_amount` DECIMAL(10,2), `refund_reason` TEXT, `refunded_at` DATETIME, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`) ON DELETE CASCADE, INDEX `idx_transaction` (`transaction_id`) ); -- Payouts Table (Vendor withdrawals) CREATE TABLE `payouts` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `vendor_id` INT(11) NOT NULL, `amount` DECIMAL(10,2) NOT NULL, `fee` DECIMAL(10,2) DEFAULT 0.00, `net_amount` DECIMAL(10,2) NOT NULL, `payment_method` VARCHAR(50) NOT NULL, `payment_details` JSON, `status` ENUM('pending', 'processing', 'completed', 'failed', 'cancelled') DEFAULT 'pending', `notes` TEXT, `processed_at` DATETIME, `transaction_id` VARCHAR(255), `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON DELETE CASCADE, INDEX `idx_status` (`status`) ); -- Shipping Zones CREATE TABLE `shipping_zones` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `countries` JSON NOT NULL, -- array of country codes `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ); -- Shipping Methods CREATE TABLE `shipping_methods` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `zone_id` INT(11) NOT NULL, `name` VARCHAR(100) NOT NULL, `cost` DECIMAL(10,2) NOT NULL, `free_shipping_threshold` DECIMAL(10,2), `estimated_days` VARCHAR(50), `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`zone_id`) REFERENCES `shipping_zones`(`id`) ON DELETE CASCADE ); -- Vendor Shipping Methods CREATE TABLE `vendor_shipping` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `vendor_id` INT(11) NOT NULL, `zone_id` INT(11) NOT NULL, `method_id` INT(11) NOT NULL, `cost` DECIMAL(10,2), `status` BOOLEAN DEFAULT TRUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`id`) ON DELETE CASCADE, FOREIGN KEY (`zone_id`) REFERENCES `shipping_zones`(`id`), FOREIGN KEY (`method_id`) REFERENCES `shipping_methods`(`id`) ); -- Notifications Table CREATE TABLE `notifications` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `user_id` INT(11) NOT NULL, `type` VARCHAR(50) NOT NULL, `title` VARCHAR(255) NOT NULL, `message` TEXT NOT NULL, `data` JSON, `is_read` BOOLEAN DEFAULT FALSE, `read_at` DATETIME, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, INDEX `idx_user_read` (`user_id`, `is_read`) ); -- System Settings CREATE TABLE `settings` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `setting_key` VARCHAR(100) UNIQUE NOT NULL, `setting_value` TEXT, `description` TEXT, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ); -- Insert Default Admin INSERT INTO `users` (`user_type`, `email`, `password`, `first_name`, `last_name`, `email_verified`) VALUES ('admin', '[email protected]', '$2y$10$YourHashedPasswordHere', 'Super', 'Admin', TRUE); -- Insert Default Settings INSERT INTO `settings` (`setting_key`, `setting_value`, `description`) VALUES ('site_name', 'Marketplace', 'Site name'), ('site_email', '[email protected]', 'Contact email'), ('site_phone', '+1-555-123-4567', 'Contact phone'), ('site_address', '123 Market St, City, State 12345', 'Business address'), ('currency', 'USD', 'Default currency'), ('currency_symbol', '$', 'Currency symbol'), ('commission_rate', '10.00', 'Default commission rate for vendors'), ('tax_rate', '0.00', 'Default tax rate'), ('min_payout', '50.00', 'Minimum payout amount for vendors'), ('max_product_images', '5', 'Maximum images per product'), ('enable_reviews', '1', 'Enable product reviews'), ('require_review_approval', '1', 'Require admin approval for reviews'), ('enable_wishlist', '1', 'Enable wishlist feature'), ('enable_compare', '1', 'Enable product comparison'), ('items_per_page', '20', 'Products per page'), ('timezone', 'America/New_York', 'Default timezone'), ('date_format', 'Y-m-d', 'Date format'), ('time_format', 'H:i', 'Time format'); Core PHP Classes
Database Class
File: includes/Database.php
<?php /** * Database Class * Handles all database connections and operations using PDO with singleton pattern */ class Database { private static $instance = null; private $connection; private $statement; private $host; private $dbname; private $username; private $password; /** * Private constructor for singleton pattern */ private function __construct() { $this->host = DB_HOST; $this->dbname = DB_NAME; $this->username = DB_USER; $this->password = DB_PASS; try { $this->connection = new PDO( "mysql:host={$this->host};dbname={$this->dbname};charset=utf8mb4", $this->username, $this->password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ] ); } catch (PDOException $e) { die("Database connection failed: " . $e->getMessage()); } } /** * Get database instance (Singleton) */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Prepare and execute query with parameters */ public function query($sql, $params = []) { try { $this->statement = $this->connection->prepare($sql); $this->statement->execute($params); return $this->statement; } catch (PDOException $e) { $this->logError($e->getMessage(), $sql, $params); throw new Exception("Database query failed: " . $e->getMessage()); } } /** * Get single row */ public function getRow($sql, $params = []) { $result = $this->query($sql, $params); return $result->fetch(); } /** * Get multiple rows */ public function getRows($sql, $params = []) { $result = $this->query($sql, $params); return $result->fetchAll(); } /** * Get single value */ public function getValue($sql, $params = []) { $result = $this->query($sql, $params); return $result->fetchColumn(); } /** * Insert data and return last insert ID */ public function insert($table, $data) { $columns = implode(', ', array_keys($data)); $placeholders = ':' . implode(', :', array_keys($data)); $sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})"; $this->query($sql, $data); return $this->connection->lastInsertId(); } /** * Update data */ public function update($table, $data, $where, $whereParams = []) { $set = []; foreach (array_keys($data) as $column) { $set[] = "{$column} = :{$column}"; } $sql = "UPDATE {$table} SET " . implode(', ', $set) . " WHERE {$where}"; $params = array_merge($data, $whereParams); return $this->query($sql, $params)->rowCount(); } /** * Delete data */ public function delete($table, $where, $params = []) { $sql = "DELETE FROM {$table} WHERE {$where}"; return $this->query($sql, $params)->rowCount(); } /** * Begin transaction */ public function beginTransaction() { return $this->connection->beginTransaction(); } /** * Commit transaction */ public function commit() { return $this->connection->commit(); } /** * Rollback transaction */ public function rollback() { return $this->connection->rollBack(); } /** * Get last insert ID */ public function lastInsertId() { return $this->connection->lastInsertId(); } /** * Log database errors */ private function logError($message, $sql, $params) { $logFile = __DIR__ . '/../logs/database.log'; $logDir = dirname($logFile); if (!is_dir($logDir)) { mkdir($logDir, 0755, true); } $timestamp = date('Y-m-d H:i:s'); $logMessage = "[{$timestamp}] Error: {$message}\n"; $logMessage .= "SQL: {$sql}\n"; $logMessage .= "Params: " . json_encode($params) . "\n"; $logMessage .= "------------------------\n"; file_put_contents($logFile, $logMessage, FILE_APPEND); } /** * Prevent cloning of the instance */ private function __clone() {} /** * Prevent unserializing of the instance */ public function __wakeup() {} } ?> Configuration File
File: includes/config.php
<?php /** * Configuration File * Loads environment variables and sets up constants */ // Start session if not started if (session_status() == PHP_SESSION_NONE) { session_start(); } // Load environment variables from .env file function loadEnv($path) { if (!file_exists($path)) { return false; } $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { if (strpos(trim($line), '#') === 0) { continue; } list($name, $value) = explode('=', $line, 2); $name = trim($name); $value = trim($value); if (!array_key_exists($name, $_ENV)) { $_ENV[$name] = $value; putenv(sprintf('%s=%s', $name, $value)); } } return true; } // Load environment variables loadEnv(__DIR__ . '/../.env'); // Database Configuration define('DB_HOST', getenv('DB_HOST') ?: 'localhost'); define('DB_NAME', getenv('DB_NAME') ?: 'marketplace'); define('DB_USER', getenv('DB_USER') ?: 'root'); define('DB_PASS', getenv('DB_PASS') ?: ''); // Application Configuration define('SITE_NAME', getenv('SITE_NAME') ?: 'Marketplace'); define('SITE_URL', getenv('SITE_URL') ?: 'http://localhost/marketplace'); define('SITE_VERSION', getenv('SITE_VERSION') ?: '1.0.0'); define('DEBUG_MODE', getenv('DEBUG_MODE') === 'true'); // Security Configuration define('SESSION_TIMEOUT', getenv('SESSION_TIMEOUT') ?: 7200); // 2 hours define('BCRYPT_ROUNDS', 12); define('CSRF_TOKEN_NAME', 'csrf_token'); // Upload Configuration define('UPLOAD_DIR', __DIR__ . '/../uploads/'); define('MAX_FILE_SIZE', getenv('MAX_FILE_SIZE') ?: 5 * 1024 * 1024); // 5MB define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif', 'webp']); // Pagination define('ITEMS_PER_PAGE', getenv('ITEMS_PER_PAGE') ?: 20); // Date/Time Configuration date_default_timezone_set(getenv('TIMEZONE') ?: 'America/New_York'); define('DATE_FORMAT', 'Y-m-d'); define('TIME_FORMAT', 'H:i'); define('DATETIME_FORMAT', 'Y-m-d H:i:s'); // Currency Configuration define('CURRENCY', getenv('CURRENCY') ?: 'USD'); define('CURRENCY_SYMBOL', getenv('CURRENCY_SYMBOL') ?: '$'); // Marketplace Settings define('COMMISSION_RATE', getenv('COMMISSION_RATE') ?: 10.00); define('TAX_RATE', getenv('TAX_RATE') ?: 0.00); define('MIN_PAYOUT', getenv('MIN_PAYOUT') ?: 50.00); define('MAX_PRODUCT_IMAGES', getenv('MAX_PRODUCT_IMAGES') ?: 5); // Email Configuration define('SMTP_HOST', getenv('SMTP_HOST') ?: 'smtp.gmail.com'); define('SMTP_PORT', getenv('SMTP_PORT') ?: 587); define('SMTP_USER', getenv('SMTP_USER') ?: ''); define('SMTP_PASS', getenv('SMTP_PASS') ?: ''); define('SMTP_ENCRYPTION', getenv('SMTP_ENCRYPTION') ?: 'tls'); // Payment Gateway Configuration define('STRIPE_KEY', getenv('STRIPE_KEY') ?: ''); define('STRIPE_SECRET', getenv('STRIPE_SECRET') ?: ''); define('PAYPAL_CLIENT_ID', getenv('PAYPAL_CLIENT_ID') ?: ''); define('PAYPAL_SECRET', getenv('PAYPAL_SECRET') ?: ''); define('RAZORPAY_KEY', getenv('RAZORPAY_KEY') ?: ''); define('RAZORPAY_SECRET', getenv('RAZORPAY_SECRET') ?: ''); // Error Reporting if (DEBUG_MODE) { error_reporting(E_ALL); ini_set('display_errors', 1); } else { error_reporting(0); ini_set('display_errors', 0); } // Include required files require_once __DIR__ . '/Database.php'; require_once __DIR__ . '/functions.php'; require_once __DIR__ . '/auth.php'; require_once __DIR__ . '/Product.php'; require_once __DIR__ . '/Category.php'; require_once __DIR__ . '/Vendor.php'; require_once __DIR__ . '/Order.php'; require_once __DIR__ . '/Cart.php'; require_once __DIR__ . '/Payment.php'; require_once __DIR__ . '/Shipping.php'; require_once __DIR__ . '/Review.php'; // Initialize database connection $db = Database::getInstance(); // Load settings $settings = $db->getRows("SELECT setting_key, setting_value FROM settings"); foreach ($settings as $setting) { define(strtoupper($setting['setting_key']), $setting['setting_value']); } // Set timezone for MySQL $db->query("SET time_zone = ?", [date('P')]); // Initialize cart if not exists if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; } // Initialize wishlist if not exists if (!isset($_SESSION['wishlist'])) { $_SESSION['wishlist'] = []; } ?> Helper Functions
File: includes/functions.php
<?php /** * Helper Functions * Common utility functions used throughout the application */ /** * Sanitize input data */ function sanitize($input) { if (is_array($input)) { return array_map('sanitize', $input); } return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8'); } /** * Generate CSRF token */ function generateCSRFToken() { if (!isset($_SESSION[CSRF_TOKEN_NAME])) { $_SESSION[CSRF_TOKEN_NAME] = bin2hex(random_bytes(32)); } return $_SESSION[CSRF_TOKEN_NAME]; } /** * Verify CSRF token */ function verifyCSRFToken($token) { if (!isset($_SESSION[CSRF_TOKEN_NAME]) || $token !== $_SESSION[CSRF_TOKEN_NAME]) { return false; } return true; } /** * Redirect to URL */ function redirect($url) { header("Location: " . SITE_URL . $url); exit(); } /** * Format price with currency */ function formatPrice($price, $currency = null) { if ($currency === null) { $currency = CURRENCY; } $symbols = [ 'USD' => '$', 'EUR' => '€', 'GBP' => '£', 'JPY' => '¥', 'INR' => '₹', 'CAD' => 'C$', 'AUD' => 'A$', 'CNY' => '¥' ]; $symbol = $symbols[$currency] ?? CURRENCY_SYMBOL; return $symbol . number_format($price, 2); } /** * Calculate discount percentage */ function getDiscountPercentage($original, $sale) { if ($original <= 0) { return 0; } $discount = (($original - $sale) / $original) * 100; return round($discount); } /** * Format date */ function formatDate($date, $format = null) { if ($format === null) { $format = DATE_FORMAT; } if ($date instanceof DateTime) { return $date->format($format); } return date($format, strtotime($date)); } /** * Get time ago string */ function timeAgo($datetime) { $time = strtotime($datetime); $now = time(); $diff = $now - $time; if ($diff < 60) { return $diff . ' seconds ago'; } elseif ($diff < 3600) { return floor($diff / 60) . ' minutes ago'; } elseif ($diff < 86400) { return floor($diff / 3600) . ' hours ago'; } elseif ($diff < 2592000) { return floor($diff / 86400) . ' days ago'; } elseif ($diff < 31536000) { return floor($diff / 2592000) . ' months ago'; } else { return floor($diff / 31536000) . ' years ago'; } } /** * Generate slug from string */ function createSlug($string) { $string = strtolower($string); $string = preg_replace('/[^a-z0-9-]/', '-', $string); $string = preg_replace('/-+/', '-', $string); return trim($string, '-'); } /** * Generate SKU */ function generateSKU($vendorId, $productName) { $prefix = 'SKU' . str_pad($vendorId, 4, '0', STR_PAD_LEFT); $suffix = strtoupper(substr(preg_replace('/[^A-Za-z0-9]/', '', $productName), 0, 5)); $unique = strtoupper(substr(uniqid(), -4)); return $prefix . $suffix . $unique; } /** * Generate order number */ function generateOrderNumber() { return 'ORD' . date('Ymd') . strtoupper(uniqid()); } /** * Get order status badge */ function getOrderStatusBadge($status) { $badges = [ 'pending' => 'badge bg-warning', 'processing' => 'badge bg-info', 'confirmed' => 'badge bg-primary', 'shipped' => 'badge bg-secondary', 'delivered' => 'badge bg-success', 'cancelled' => 'badge bg-danger', 'refunded' => 'badge bg-dark' ]; $class = $badges[$status] ?? 'badge bg-secondary'; return '<span class="' . $class . '">' . ucfirst($status) . '</span>'; } /** * Get payment status badge */ function getPaymentStatusBadge($status) { $badges = [ 'pending' => 'badge bg-warning', 'paid' => 'badge bg-success', 'failed' => 'badge bg-danger', 'refunded' => 'badge bg-secondary' ]; $class = $badges[$status] ?? 'badge bg-secondary'; return '<span class="' . $class . '">' . ucfirst($status) . '</span>'; } /** * Upload file */ function uploadFile($file, $targetDir, $allowedTypes = null) { if ($allowedTypes === null) { $allowedTypes = ALLOWED_EXTENSIONS; } // Check for errors if ($file['error'] !== UPLOAD_ERR_OK) { return ['success' => false, 'error' => 'Upload failed with error code: ' . $file['error']]; } // Check file size if ($file['size'] > MAX_FILE_SIZE) { return ['success' => false, 'error' => 'File size exceeds limit']; } // Check file type $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($extension, $allowedTypes)) { return ['success' => false, 'error' => 'File type not allowed']; } // Generate unique filename $filename = uniqid() . '_' . time() . '.' . $extension; $targetPath = $targetDir . '/' . $filename; // Create directory if not exists if (!is_dir($targetDir)) { mkdir($targetDir, 0755, true); } // Upload file if (move_uploaded_file($file['tmp_name'], $targetPath)) { return [ 'success' => true, 'filename' => $filename, 'original_name' => $file['name'], 'path' => $targetPath ]; } return ['success' => false, 'error' => 'Failed to move uploaded file']; } /** * Delete file */ function deleteFile($path) { if (file_exists($path)) { return unlink($path); } return false; } /** * Get pagination */ function paginate($currentPage, $totalPages, $url) { if ($totalPages <= 1) { return ''; } $html = '<nav aria-label="Page navigation"><ul class="pagination">'; // Previous button if ($currentPage > 1) { $html .= '<li class="page-item"><a class="page-link" href="' . $url . '&page=' . ($currentPage - 1) . '">Previous</a></li>'; } else { $html .= '<li class="page-item disabled"><span class="page-link">Previous</span></li>'; } // Page numbers for ($i = 1; $i <= $totalPages; $i++) { if ($i == $currentPage) { $html .= '<li class="page-item active"><span class="page-link">' . $i . '</span></li>'; } else { $html .= '<li class="page-item"><a class="page-link" href="' . $url . '&page=' . $i . '">' . $i . '</a></li>'; } } // Next button if ($currentPage < $totalPages) { $html .= '<li class="page-item"><a class="page-link" href="' . $url . '&page=' . ($currentPage + 1) . '">Next</a></li>'; } else { $html .= '<li class="page-item disabled"><span class="page-link">Next</span></li>'; } $html .= '</ul></nav>'; return $html; } /** * Generate star rating HTML */ function displayRating($rating, $totalReviews = null) { $html = '<div class="rating">'; for ($i = 1; $i <= 5; $i++) { if ($i <= floor($rating)) { $html .= '<i class="fas fa-star text-warning"></i>'; } elseif ($i - $rating <= 0.5 && $i - $rating > 0) { $html .= '<i class="fas fa-star-half-alt text-warning"></i>'; } else { $html .= '<i class="far fa-star text-warning"></i>'; } } if ($totalReviews !== null) { $html .= ' <span class="text-muted ms-1">(' . $totalReviews . ')</span>'; } $html .= '</div>'; return $html; } /** * Get cart count */ function getCartCount() { $count = 0; if (isset($_SESSION['cart'])) { foreach ($_SESSION['cart'] as $item) { $count += $item['quantity']; } } return $count; } /** * Get wishlist count */ function getWishlistCount() { return isset($_SESSION['wishlist']) ? count($_SESSION['wishlist']) : 0; } /** * Log error */ function logError($message, $context = []) { $logFile = __DIR__ . '/../logs/error.log'; $logDir = dirname($logFile); if (!is_dir($logDir)) { mkdir($logDir, 0755, true); } $timestamp = date('Y-m-d H:i:s'); $contextStr = !empty($context) ? ' ' . json_encode($context) : ''; $logMessage = "[{$timestamp}] {$message}{$contextStr}\n"; file_put_contents($logFile, $logMessage, FILE_APPEND); } /** * Send email */ function sendEmail($to, $subject, $template, $data = []) { require_once __DIR__ . '/../vendor/autoload.php'; $mail = new PHPMailer\PHPMailer\PHPMailer(true); try { // Server settings $mail->isSMTP(); $mail->Host = SMTP_HOST; $mail->SMTPAuth = true; $mail->Username = SMTP_USER; $mail->Password = SMTP_PASS; $mail->SMTPSecure = SMTP_ENCRYPTION; $mail->Port = SMTP_PORT; // Recipients $mail->setFrom(SMTP_USER, SITE_NAME); $mail->addAddress($to); // Content $mail->isHTML(true); $mail->Subject = $subject; // Load template ob_start(); extract($data); include __DIR__ . "/emails/{$template}.php"; $mail->Body = ob_get_clean(); $mail->send(); return true; } catch (Exception $e) { logError("Email sending failed: " . $e->getMessage()); return false; } } /** * Get user IP */ function getUserIP() { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { return $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } else { return $_SERVER['REMOTE_ADDR']; } } /** * Calculate commission */ function calculateCommission($amount, $rate = null) { if ($rate === null) { $rate = COMMISSION_RATE; } return ($amount * $rate) / 100; } /** * Get random string */ function generateRandomString($length = 32) { return bin2hex(random_bytes($length / 2)); } /** * Validate email */ function validateEmail($email) { return filter_var($email, FILTER_VALIDATE_EMAIL); } /** * Get product image URL */ function getProductImage($image, $type = 'medium') { if (empty($image)) { return SITE_URL . '/assets/images/no-image.jpg'; } $path = SITE_URL . '/uploads/products/'; if ($type === 'thumb') { return $path . 'thumb/' . $image; } elseif ($type === 'medium') { return $path . 'medium/' . $image; } else { return $path . $image; } } /** * Get vendor logo URL */ function getVendorLogo($logo) { if (empty($logo)) { return SITE_URL . '/assets/images/default-store.png'; } return SITE_URL . '/uploads/vendors/' . $logo; } /** * Check if product is in wishlist */ function isInWishlist($productId) { if (!isset($_SESSION['wishlist'])) { return false; } return in_array($productId, $_SESSION['wishlist']); } /** * Get cart total */ function getCartTotal() { $total = 0; if (isset($_SESSION['cart'])) { foreach ($_SESSION['cart'] as $item) { $total += $item['price'] * $item['quantity']; } } return $total; } ?> Authentication Class
File: includes/auth.php
<?php /** * Authentication Class * Handles user authentication, registration, and session management */ class Auth { private $db; /** * Constructor */ public function __construct() { $this->db = Database::getInstance(); } /** * Register new user */ public function register($data) { try { // Check if email already exists $existing = $this->db->getRow( "SELECT id FROM users WHERE email = ?", [$data['email']] ); if ($existing) { return ['success' => false, 'error' => 'Email already registered']; } // Hash password $hashedPassword = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => BCRYPT_ROUNDS]); // Generate verification token $verificationToken = generateRandomString(); // Prepare user data $userData = [ 'user_type' => $data['user_type'] ?? 'customer', 'email' => $data['email'], 'password' => $hashedPassword, 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'phone' => $data['phone'] ?? null, 'verification_token' => $verificationToken ]; // Insert user $userId = $this->db->insert('users', $userData); if ($userId) { // If vendor registration, create vendor record if ($data['user_type'] === 'vendor') { $this->createVendorRecord($userId, $data); } // Send verification email $this->sendVerificationEmail($data['email'], $verificationToken); return [ 'success' => true, 'user_id' => $userId, 'message' => 'Registration successful. Please check your email to verify your account.' ]; } return ['success' => false, 'error' => 'Registration failed']; } catch (Exception $e) { logError('Registration error: ' . $e->getMessage(), $data); return ['success' => false, 'error' => 'Registration failed: ' . $e->getMessage()]; } } /** * Create vendor record */ private function createVendorRecord($userId, $data) { $storeSlug = createSlug($data['store_name']); // Ensure unique slug $existing = $this->db->getRow( "SELECT id FROM vendors WHERE store_slug = ?", [$storeSlug] ); if ($existing) { $storeSlug .= '-' . uniqid(); } $vendorData = [ 'user_id' => $userId, 'store_name' => $data['store_name'], 'store_slug' => $storeSlug, 'store_description' => $data['store_description'] ?? null, 'store_phone' => $data['store_phone'] ?? null, 'store_email' => $data['store_email'] ?? $data['email'], 'status' => 'pending', 'commission_rate' => COMMISSION_RATE ]; $this->db->insert('vendors', $vendorData); } /** * Login user */ public function login($email, $password, $remember = false) { try { // Get user $user = $this->db->getRow( "SELECT * FROM users WHERE email = ? AND status = 'active'", [$email] ); if (!$user) { return ['success' => false, 'error' => 'Invalid email or password']; } // Check if email verified if (!$user['email_verified']) { return ['success' => false, 'error' => 'Please verify your email before logging in']; } // Verify password if (!password_verify($password, $user['password'])) { return ['success' => false, 'error' => 'Invalid email or password']; } // Check if password needs rehash if (password_needs_rehash($user['password'], PASSWORD_BCRYPT, ['cost' => BCRYPT_ROUNDS])) { $newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => BCRYPT_ROUNDS]); $this->db->update('users', ['password' => $newHash], 'id = :id', ['id' => $user['id']]); } // Set session $_SESSION['user_id'] = $user['id']; $_SESSION['user_email'] = $user['email']; $_SESSION['user_name'] = $user['first_name'] . ' ' . $user['last_name']; $_SESSION['user_type'] = $user['user_type']; $_SESSION['logged_in'] = true; $_SESSION['login_time'] = time(); // Update last login $this->db->update( 'users', ['last_login' => date('Y-m-d H:i:s')], 'id = :id', ['id' => $user['id']] ); // Set remember me cookie if ($remember) { $this->setRememberMe($user['id']); } // If vendor, get vendor details if ($user['user_type'] === 'vendor') { $vendor = $this->db->getRow( "SELECT id, status FROM vendors WHERE user_id = ?", [$user['id']] ); if ($vendor) { $_SESSION['vendor_id'] = $vendor['id']; $_SESSION['vendor_status'] = $vendor['status']; } } return ['success' => true, 'user' => $user]; } catch (Exception $e) { logError('Login error: ' . $e->getMessage()); return ['success' => false, 'error' => 'Login failed']; } } /** * Set remember me cookie */ private function setRememberMe($userId) { $token = generateRandomString(64); $expires = time() + (86400 * 30); // 30 days // Store in remember_tokens table (create this table if needed) setcookie('remember_token', $token, $expires, '/', '', false, true); } /** * Logout user */ public function logout() { $_SESSION = array(); if (ini_get('session.use_cookies')) { $params = session_get_cookie_params(); setcookie( session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly'] ); } setcookie('remember_token', '', time() - 3600, '/'); session_destroy(); } /** * Check if user is logged in */ public function isLoggedIn() { return isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true; } /** * Get current user */ public function getCurrentUser() { if (!$this->isLoggedIn()) { return null; } return $this->db->getRow( "SELECT * FROM users WHERE id = ?", [$_SESSION['user_id']] ); } /** * Check if user has role */ public function hasRole($role) { return isset($_SESSION['user_type']) && $_SESSION['user_type'] === $role; } /** * Require login */ public function requireLogin() { if (!$this->isLoggedIn()) { $_SESSION['error'] = 'Please login to access this page'; redirect('/login.php'); } } /** * Require role */ public function requireRole($role) { $this->requireLogin(); if (!$this->hasRole($role)) { $_SESSION['error'] = 'You do not have permission to access this page'; if ($this->hasRole('admin')) { redirect('/admin/dashboard.php'); } elseif ($this->hasRole('vendor')) { redirect('/vendor/dashboard.php'); } else { redirect('/index.php'); } } } /** * Require vendor approval */ public function requireVendorApproval() { $this->requireRole('vendor'); if (!isset($_SESSION['vendor_status']) || $_SESSION['vendor_status'] !== 'approved') { $_SESSION['error'] = 'Your vendor account is pending approval'; redirect('/vendor/pending.php'); } } /** * Verify email */ public function verifyEmail($token) { $user = $this->db->getRow( "SELECT id FROM users WHERE verification_token = ?", [$token] ); if ($user) { $this->db->update( 'users', ['email_verified' => true, 'verification_token' => null], 'id = :id', ['id' => $user['id']] ); return true; } return false; } /** * Send verification email */ private function sendVerificationEmail($email, $token) { $subject = "Verify Your Email - " . SITE_NAME; $data = [ 'verification_link' => SITE_URL . "/verify.php?token=" . $token ]; return sendEmail($email, $subject, 'verification', $data); } /** * Forgot password */ public function forgotPassword($email) { $user = $this->db->getRow( "SELECT id FROM users WHERE email = ?", [$email] ); if ($user) { $token = generateRandomString(); $expires = date('Y-m-d H:i:s', strtotime('+1 hour')); $this->db->update( 'users', ['reset_token' => $token, 'reset_expires' => $expires], 'id = :id', ['id' => $user['id']] ); $subject = "Password Reset - " . SITE_NAME; $data = [ 'reset_link' => SITE_URL . "/reset-password.php?token=" . $token ]; return sendEmail($email, $subject, 'password_reset', $data); } return false; } /** * Reset password */ public function resetPassword($token, $password) { $user = $this->db->getRow( "SELECT id FROM users WHERE reset_token = ? AND reset_expires > NOW()", [$token] ); if ($user) { $hashedPassword = password_hash($password, PASSWORD_BCRYPT, ['cost' => BCRYPT_ROUNDS]); $this->db->update( 'users', ['password' => $hashedPassword, 'reset_token' => null, 'reset_expires' => null], 'id = :id', ['id' => $user['id']] ); return true; } return false; } /** * Update profile */ public function updateProfile($userId, $data) { try { $updateData = [ 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'phone' => $data['phone'] ?? null ]; if (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] == 0) { $uploadDir = UPLOAD_DIR . 'profiles/'; $result = uploadFile($_FILES['profile_image'], $uploadDir); if ($result['success']) { $updateData['profile_image'] = $result['filename']; } } $this->db->update('users', $updateData, 'id = :id', ['id' => $userId]); return ['success' => true, 'message' => 'Profile updated successfully']; } catch (Exception $e) { logError('Profile update error: ' . $e->getMessage()); return ['success' => false, 'error' => 'Failed to update profile']; } } /** * Change password */ public function changePassword($userId, $currentPassword, $newPassword) { $user = $this->db->getRow("SELECT password FROM users WHERE id = ?", [$userId]); if (!password_verify($currentPassword, $user['password'])) { return ['success' => false, 'error' => 'Current password is incorrect']; } $hashedPassword = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => BCRYPT_ROUNDS]); $this->db->update('users', ['password' => $hashedPassword], 'id = :id', ['id' => $userId]); return ['success' => true, 'message' => 'Password changed successfully']; } } // Initialize Auth $auth = new Auth(); ?> Product Class
File: includes/Product.php
<?php /** * Product Class * Handles all product-related operations */ class Product { private $db; /** * Constructor */ public function __construct() { $this->db = Database::getInstance(); } /** * Create new product */ public function create($vendorId, $data) { try { $this->db->beginTransaction(); // Generate slug $slug = createSlug($data['name']); // Ensure unique slug $existing = $this->db->getRow( "SELECT id FROM products WHERE slug = ?", [$slug] ); if ($existing) { $slug .= '-' . uniqid(); } // Generate SKU $sku = generateSKU($vendorId, $data['name']); // Prepare product data $productData = [ 'vendor_id' => $vendorId, 'category_id' => $data['category_id'], 'brand_id' => $data['brand_id'] ?? null, 'name' => $data['name'], 'slug' => $slug, 'sku' => $sku, 'short_description' => $data['short_description'] ?? null, 'description' => $data['description'], 'specifications' => isset($data['specifications']) ? json_encode($data['specifications']) : null, 'price' => $data['price'], 'compare_price' => $data['compare_price'] ?? null, 'cost_price' => $data['cost_price'] ?? null, 'quantity' => $data['quantity'] ?? 0, 'low_stock_threshold' => $data['low_stock_threshold'] ?? 5, 'weight' => $data['weight'] ?? null, 'dimensions' => $data['dimensions'] ?? null, 'tags' => $data['tags'] ?? null, 'meta_title' => $data['meta_title'] ?? substr($data['name'], 0, 60), 'meta_description' => $data['meta_description'] ?? substr($data['short_description'] ?? $data['name'], 0, 160), 'meta_keywords' => $data['meta_keywords'] ?? null, 'is_featured' => $data['is_featured'] ?? false, 'is_new' => true, 'status' => 'pending' // Needs admin approval ]; // Handle featured image if (isset($_FILES['featured_image']) && $_FILES['featured_image']['error'] == 0) { $uploadDir = UPLOAD_DIR . 'products/'; $result = uploadFile($_FILES['featured_image'], $uploadDir); if ($result['success']) { $productData['featured_image'] = $result['filename']; // Create thumbnails $this->createThumbnails($result['filename']); } } // Insert product $productId = $this->db->insert('products', $productData); // Handle gallery images if (isset($_FILES['gallery_images'])) { $this->uploadGalleryImages($productId, $_FILES['gallery_images']); } // Handle variants if (isset($data['variants']) && is_array($data['variants'])) { $this->addVariants($productId, $data['variants']); } $this->db->commit(); return [ 'success' => true, 'product_id' => $productId, 'message' => 'Product created successfully and pending approval' ]; } catch (Exception $e) { $this->db->rollback(); logError('Product creation error: ' . $e->getMessage(), $data); return ['success' => false, 'error' => 'Failed to create product']; } } /** * Update product */ public function update($productId, $vendorId, $data) { try { $product = $this->getProduct($productId); if (!$product || $product['vendor_id'] != $vendorId) { return ['success' => false, 'error' => 'Product not found or unauthorized']; } $updateData = [ 'category_id' => $data['category_id'], 'brand_id' => $data['brand_id'] ?? null, 'name' => $data['name'], 'short_description' => $data['short_description'] ?? null, 'description' => $data['description'], 'specifications' => isset($data['specifications']) ? json_encode($data['specifications']) : null, 'price' => $data['price'], 'compare_price' => $data['compare_price'] ?? null, 'cost_price' => $data['cost_price'] ?? null, 'quantity' => $data['quantity'] ?? 0, 'low_stock_threshold' => $data['low_stock_threshold'] ?? 5, 'weight' => $data['weight'] ?? null, 'dimensions' => $data['dimensions'] ?? null, 'tags' => $data['tags'] ?? null, 'meta_title' => $data['meta_title'], 'meta_description' => $data['meta_description'], 'meta_keywords' => $data['meta_keywords'] ?? null, 'is_featured' => $data['is_featured'] ?? false ]; // Handle featured image if (isset($_FILES['featured_image']) && $_FILES['featured_image']['error'] == 0) { $uploadDir = UPLOAD_DIR . 'products/'; $result = uploadFile($_FILES['featured_image'], $uploadDir); if ($result['success']) { // Delete old image if ($product['featured_image']) { deleteFile(UPLOAD_DIR . 'products/' . $product['featured_image']); deleteFile(UPLOAD_DIR . 'products/thumb/' . $product['featured_image']); deleteFile(UPLOAD_DIR . 'products/medium/' . $product['featured_image']); } $updateData['featured_image'] = $result['filename']; $this->createThumbnails($result['filename']); } } $this->db->update('products', $updateData, 'id = :id', ['id' => $productId]); // Handle variants if (isset($data['variants'])) { $this->updateVariants($productId, $data['variants']); } return ['success' => true, 'message' => 'Product updated successfully']; } catch (Exception $e) { logError('Product update error: ' . $e->getMessage()); return ['success' => false, 'error' => 'Failed to update product']; } } /** * Get product by ID */ public function getProduct($id) { return $this->db->getRow( "SELECT p.*, c.name as category_name, b.name as brand_name, v.store_name as vendor_name, v.store_slug as vendor_slug FROM products p LEFT JOIN categories c ON p.category_id = c.id LEFT JOIN brands b ON p.brand_id = b.id LEFT JOIN vendors v ON p.vendor_id = v.id WHERE p.id = ?", [$id] ); } /** * Get product by slug */ public function getProductBySlug($slug) { return $this->db->getRow( "SELECT p.*, c.name as category_name, c.slug as category_slug, b.name as brand_name, v.store_name as vendor_name, v.store_slug as vendor_slug, v.id as vendor_id, v.rating as vendor_rating, v.total_reviews as vendor_reviews FROM products p LEFT JOIN categories c ON p.category_id = c.id LEFT JOIN brands b ON p.brand_id = b.id LEFT JOIN vendors v ON p.vendor_id = v.id WHERE p.slug = ? AND p.status = 'approved'", [$slug] ); } /** * Get vendor products */ public function getVendorProducts($vendorId, $status = null, $page = 1, $limit = null) { if ($limit === null) { $limit = ITEMS_PER_PAGE; } $offset = ($page - 1) * $limit; $sql = "SELECT * FROM products WHERE vendor_id = :vendor_id"; $params = ['vendor_id' => $vendorId]; if ($status) { $sql .= " AND status = :status"; $params['status'] = $status; } $sql .= " ORDER BY created_at DESC LIMIT :limit OFFSET :offset"; $params['limit'] = $limit; $params['offset'] = $offset; return $this->db->getRows($sql, $params); } /** * Get products by category */ public function getProductsByCategory($categoryId, $filters = [], $page = 1, $limit = null) { if ($limit === null) { $limit = ITEMS_PER_PAGE; } $offset = ($page - 1) * $limit; $sql = "SELECT p.*, v.store_name, v.store_slug, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating, (SELECT COUNT(*) FROM reviews WHERE product_id = p.id) as review_count FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.category_id = :category_id AND p.status = 'approved' AND v.status = 'approved'"; $params = ['category_id' => $categoryId]; // Apply filters if (!empty($filters['min_price'])) { $sql .= " AND p.price >= :min_price"; $params['min_price'] = $filters['min_price']; } if (!empty($filters['max_price'])) { $sql .= " AND p.price <= :max_price"; $params['max_price'] = $filters['max_price']; } if (!empty($filters['brand'])) { $sql .= " AND p.brand_id IN (" . implode(',', array_fill(0, count($filters['brand']), '?')) . ")"; $params = array_merge($params, $filters['brand']); } if (!empty($filters['rating'])) { $sql .= " HAVING avg_rating >= :rating"; $params['rating'] = $filters['rating']; } // Sorting $sort = $filters['sort'] ?? 'newest'; switch ($sort) { case 'price_low': $sql .= " ORDER BY p.price ASC"; break; case 'price_high': $sql .= " ORDER BY p.price DESC"; break; case 'rating': $sql .= " ORDER BY avg_rating DESC"; break; case 'popular': $sql .= " ORDER BY p.total_sales DESC"; break; default: $sql .= " ORDER BY p.created_at DESC"; } $sql .= " LIMIT :limit OFFSET :offset"; $params['limit'] = $limit; $params['offset'] = $offset; return $this->db->getRows($sql, $params); } /** * Search products */ public function search($query, $filters = [], $page = 1, $limit = null) { if ($limit === null) { $limit = ITEMS_PER_PAGE; } $offset = ($page - 1) * $limit; $sql = "SELECT p.*, v.store_name, v.store_slug, MATCH(p.name, p.short_description, p.description, p.tags) AGAINST(:query IN NATURAL LANGUAGE MODE) as relevance, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE MATCH(p.name, p.short_description, p.description, p.tags) AGAINST(:query IN NATURAL LANGUAGE MODE) AND p.status = 'approved' AND v.status = 'approved'"; $params = ['query' => $query]; // Apply filters if (!empty($filters['category'])) { $sql .= " AND p.category_id = :category"; $params['category'] = $filters['category']; } if (!empty($filters['vendor'])) { $sql .= " AND p.vendor_id = :vendor"; $params['vendor'] = $filters['vendor']; } if (!empty($filters['min_price'])) { $sql .= " AND p.price >= :min_price"; $params['min_price'] = $filters['min_price']; } if (!empty($filters['max_price'])) { $sql .= " AND p.price <= :max_price"; $params['max_price'] = $filters['max_price']; } $sql .= " ORDER BY relevance DESC, p.created_at DESC"; $sql .= " LIMIT :limit OFFSET :offset"; $params['limit'] = $limit; $params['offset'] = $offset; return $this->db->getRows($sql, $params); } /** * Get related products */ public function getRelatedProducts($productId, $categoryId, $limit = 4) { return $this->db->getRows( "SELECT p.*, v.store_name, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.category_id = ? AND p.id != ? AND p.status = 'approved' AND v.status = 'approved' ORDER BY p.created_at DESC LIMIT ?", [$categoryId, $productId, $limit] ); } /** * Get featured products */ public function getFeaturedProducts($limit = 8) { return $this->db->getRows( "SELECT p.*, v.store_name, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.is_featured = 1 AND p.status = 'approved' AND v.status = 'approved' ORDER BY p.created_at DESC LIMIT ?", [$limit] ); } /** * Get new products */ public function getNewProducts($limit = 8) { return $this->db->getRows( "SELECT p.*, v.store_name, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.is_new = 1 AND p.status = 'approved' AND v.status = 'approved' ORDER BY p.created_at DESC LIMIT ?", [$limit] ); } /** * Get on sale products */ public function getSaleProducts($limit = 8) { $now = date('Y-m-d H:i:s'); return $this->db->getRows( "SELECT p.*, v.store_name, (SELECT AVG(rating) FROM reviews WHERE product_id = p.id) as avg_rating FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.is_on_sale = 1 AND (p.sale_starts IS NULL OR p.sale_starts <= ?) AND (p.sale_ends IS NULL OR p.sale_ends >= ?) AND p.status = 'approved' AND v.status = 'approved' ORDER BY p.created_at DESC LIMIT ?", [$now, $now, $limit] ); } /** * Update inventory */ public function updateInventory($productId, $quantity) { $this->db->update( 'products', ['quantity' => $quantity], 'id = :id', ['id' => $productId] ); } /** * Decrease stock */ public function decreaseStock($productId, $quantity) { $this->db->query( "UPDATE products SET quantity = quantity - ? WHERE id = ? AND quantity >= ?", [$quantity, $productId, $quantity] ); } /** * Increase stock */ public function increaseStock($productId, $quantity) { $this->db->query( "UPDATE products SET quantity = quantity + ? WHERE id = ?", [$quantity, $productId] ); } /** * Check low stock */ public function checkLowStock($vendorId = null) { $sql = "SELECT p.*, v.store_name, v.user_id FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.quantity <= p.low_stock_threshold"; $params = []; if ($vendorId) { $sql .= " AND p.vendor_id = :vendor_id"; $params['vendor_id'] = $vendorId; } return $this->db->getRows($sql, $params); } /** * Add product variants */ private function addVariants($productId, $variants) { foreach ($variants as $variant) { $sku = generateSKU($productId, $variant['attributes'] ?? 'var'); $variantData = [ 'product_id' => $productId, 'sku' => $sku, 'attributes' => json_encode($variant['attributes']), 'price_adjustment' => $variant['price_adjustment'] ?? 0, 'quantity' => $variant['quantity'] ?? 0, 'image' => $variant['image'] ?? null ]; $this->db->insert('product_variants', $variantData); } } /** * Update variants */ private function updateVariants($productId, $variants) { // Delete existing variants $this->db->delete('product_variants', 'product_id = ?', [$productId]); // Add new variants $this->addVariants($productId, $variants); } /** * Get product variants */ public function getVariants($productId) { return $this->db->getRows( "SELECT * FROM product_variants WHERE product_id = ?", [$productId] ); } /** * Upload gallery images */ private function uploadGalleryImages($productId, $files) { $uploadDir = UPLOAD_DIR . 'products/gallery/'; foreach ($files['tmp_name'] as $key => $tmp_name) { if ($files['error'][$key] == 0) { $file = [ 'name' => $files['name'][$key], 'type' => $files['type'][$key], 'tmp_name' => $tmp_name, 'error' => $files['error'][$key], 'size' => $files['size'][$key] ]; $result = uploadFile($file, $uploadDir); if ($result['success']) { $this->db->insert('product_images', [ 'product_id' => $productId, 'image_path' => $result['filename'] ]); } } } } /** * Get product images */ public function getImages($productId) { return $this->db->getRows( "SELECT * FROM product_images WHERE product_id = ? ORDER BY is_primary DESC, sort_order ASC", [$productId] ); } /** * Create thumbnails */ private function createThumbnails($filename) { // Create thumb and medium directories if not exist $thumbDir = UPLOAD_DIR . 'products/thumb/'; $mediumDir = UPLOAD_DIR . 'products/medium/'; if (!is_dir($thumbDir)) { mkdir($thumbDir, 0755, true); } if (!is_dir($mediumDir)) { mkdir($mediumDir, 0755, true); } $sourcePath = UPLOAD_DIR . 'products/' . $filename; // Create thumbnail (150x150) $this->resizeImage($sourcePath, $thumbDir . $filename, 150, 150); // Create medium (300x300) $this->resizeImage($sourcePath, $mediumDir . $filename, 300, 300); } /** * Resize image */ private function resizeImage($sourcePath, $destPath, $width, $height) { // Get image info list($srcWidth, $srcHeight, $type) = getimagesize($sourcePath); // Create image resource switch ($type) { case IMAGETYPE_JPEG: $srcImage = imagecreatefromjpeg($sourcePath); break; case IMAGETYPE_PNG: $srcImage = imagecreatefrompng($sourcePath); break; case IMAGETYPE_GIF: $srcImage = imagecreatefromgif($sourcePath); break; default: return false; } // Calculate dimensions $srcRatio = $srcWidth / $srcHeight; $destRatio = $width / $height; if ($srcRatio > $destRatio) { $newWidth = $width; $newHeight = $width / $srcRatio; } else { $newHeight = $height; $newWidth = $height * $srcRatio; } // Create destination image $destImage = imagecreatetruecolor($width, $height); // Preserve transparency for PNG if ($type == IMAGETYPE_PNG) { imagealphablending($destImage, false); imagesavealpha($destImage, true); $transparent = imagecolorallocatealpha($destImage, 255, 255, 255, 127); imagefilledrectangle($destImage, 0, 0, $width, $height, $transparent); } // Resize imagecopyresampled( $destImage, $srcImage, ($width - $newWidth) / 2, ($height - $newHeight) / 2, 0, 0, $newWidth, $newHeight, $srcWidth, $srcHeight ); // Save image switch ($type) { case IMAGETYPE_JPEG: imagejpeg($destImage, $destPath, 90); break; case IMAGETYPE_PNG: imagepng($destImage, $destPath, 9); break; case IMAGETYPE_GIF: imagegif($destImage, $destPath); break; } imagedestroy($srcImage); imagedestroy($destImage); return true; } } ?> Frontend Pages
Main Landing Page
File: index.php
<?php require_once 'includes/config.php'; $product = new Product(); // Get featured products $featuredProducts = $product->getFeaturedProducts(8); // Get new products $newProducts = $product->getNewProducts(8); // Get sale products $saleProducts = $product->getSaleProducts(8); // Get categories $category = new Category(); $categories = $category->getAll(['featured' => true, 'limit' => 6]); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo SITE_NAME; ?> - Multi-Vendor Marketplace</title> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- Font Awesome --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <!-- Owl Carousel --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.theme.default.min.css"> <!-- Custom CSS --> <link rel="stylesheet" href="assets/css/style.css"> </head> <body> <!-- Top Bar --> <div class="top-bar bg-dark text-white py-2"> <div class="container"> <div class="row"> <div class="col-md-6"> <small> <i class="fas fa-phone me-1"></i> <?php echo SITE_PHONE; ?> | <i class="fas fa-envelope ms-2 me-1"></i> <?php echo SITE_EMAIL; ?> </small> </div> <div class="col-md-6 text-end"> <?php if ($auth->isLoggedIn()): ?> <div class="dropdown d-inline-block"> <button class="btn btn-sm btn-outline-light dropdown-toggle" type="button" data-bs-toggle="dropdown"> <i class="fas fa-user me-1"></i> <?php echo htmlspecialchars($_SESSION['user_name']); ?> </button> <ul class="dropdown-menu dropdown-menu-end"> <li> <a class="dropdown-item" href="<?php echo $_SESSION['user_type'] == 'admin' ? 'admin/dashboard.php' : ($_SESSION['user_type'] == 'vendor' ? 'vendor/dashboard.php' : 'customer/dashboard.php'); ?>"> <i class="fas fa-tachometer-alt me-2"></i>Dashboard </a> </li> <?php if ($_SESSION['user_type'] == 'customer'): ?> <li> <a class="dropdown-item" href="customer/orders.php"> <i class="fas fa-shopping-bag me-2"></i>My Orders </a> </li> <li> <a class="dropdown-item" href="customer/wishlist.php"> <i class="fas fa-heart me-2"></i>Wishlist <span class="badge bg-danger ms-2"><?php echo getWishlistCount(); ?></span> </a> </li> <?php endif; ?> <li><hr class="dropdown-divider"></li> <li> <a class="dropdown-item text-danger" href="logout.php"> <i class="fas fa-sign-out-alt me-2"></i>Logout </a> </li> </ul> </div> <?php else: ?> <a href="login.php" class="text-white text-decoration-none me-3"> <i class="fas fa-sign-in-alt me-1"></i>Login </a> <a href="register.php" class="text-white text-decoration-none"> <i class="fas fa-user-plus me-1"></i>Register </a> <a href="vendor-register.php" class="btn btn-sm btn-warning ms-3"> <i class="fas fa-store me-1"></i>Sell on Marketplace </a> <?php endif; ?> </div> </div> </div> </div> <!-- Header --> <header class="bg-white py-3 border-bottom"> <div class="container"> <div class="row align-items-center"> <div class="col-md-3"> <a href="index.php" class="text-decoration-none"> <h2 class="text-primary"> <i class="fas fa-store me-2"></i><?php echo SITE_NAME; ?> </h2> </a> </div> <div class="col-md-5"> <form action="shop.php" method="GET" class="search-form"> <div class="input-group"> <input type="text" class="form-control" name="search" placeholder="Search for products..." required> <button class="btn btn-primary" type="submit"> <i class="fas fa-search"></i> </button> </div> </form> </div> <div class="col-md-4 text-end"> <a href="customer/cart.php" class="btn btn-outline-primary position-relative me-2"> <i class="fas fa-shopping-cart"></i> <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"> <?php echo getCartCount(); ?> </span> </a> <a href="customer/wishlist.php" class="btn btn-outline-danger position-relative"> <i class="fas fa-heart"></i> <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"> <?php echo getWishlistCount(); ?> </span> </a> </div> </div> </div> </header> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarMain"> <ul class="navbar-nav me-auto"> <li class="nav-item"> <a class="nav-link active" href="index.php">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="shop.php">Shop</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="categoriesDropdown" data-bs-toggle="dropdown"> Categories </a> <ul class="dropdown-menu"> <?php $allCategories = $category->getAll(['limit' => 10]); foreach ($allCategories as $cat): ?> <li> <a class="dropdown-item" href="shop.php?category=<?php echo $cat['id']; ?>"> <?php echo htmlspecialchars($cat['name']); ?> </a> </li> <?php endforeach; ?> <li><hr class="dropdown-divider"></li> <li> <a class="dropdown-item" href="shop.php">All Categories</a> </li> </ul> </li> <li class="nav-item"> <a class="nav-link" href="vendors.php">Vendors</a> </li> <li class="nav-item"> <a class="nav-link" href="deals.php">Today's Deals</a> </li> <li class="nav-item"> <a class="nav-link" href="contact.php">Contact</a> </li> </ul> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="become-vendor.php"> <i class="fas fa-store me-1"></i>Become a Vendor </a> </li> </ul> </div> </div> </nav> <!-- Hero Slider --> <section class="hero-slider"> <div id="heroCarousel" class="carousel slide" data-bs-ride="carousel"> <div class="carousel-indicators"> <button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="0" class="active"></button> <button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="1"></button> <button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="2"></button> </div> <div class="carousel-inner"> <div class="carousel-item active"> <img src="assets/images/slides/slide1.jpg" class="d-block w-100" alt="Slide 1"> <div class="carousel-caption d-none d-md-block"> <h3>Welcome to Marketplace</h3> <p>Shop from thousands of trusted vendors</p> <a href="shop.php" class="btn btn-primary">Shop Now</a> </div> </div> <div class="carousel-item"> <img src="assets/images/slides/slide2.jpg" class="d-block w-100" alt="Slide 2"> <div class="carousel-caption d-none d-md-block"> <h3>Summer Sale</h3> <p>Up to 50% off on selected items</p> <a href="deals.php" class="btn btn-primary">View Deals</a> </div> </div> <div class="carousel-item"> <img src="assets/images/slides/slide3.jpg" class="d-block w-100" alt="Slide 3"> <div class="carousel-caption d-none d-md-block"> <h3>Become a Vendor</h3> <p>Start selling your products today</p> <a href="become-vendor.php" class="btn btn-primary">Join Now</a> </div> </div> </div> <button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev"> <span class="carousel-control-prev-icon"></span> </button> <button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next"> <span class="carousel-control-next-icon"></span> </button> </div> </section> <!-- Features Section --> <section class="features py-5"> <div class="container"> <div class="row"> <div class="col-md-3 mb-3"> <div class="feature-box text-center"> <i class="fas fa-truck fa-3x text-primary mb-3"></i> <h5>Free Shipping</h5> <p class="text-muted">On orders over $50</p> </div> </div> <div class="col-md-3 mb-3"> <div class="feature-box text-center"> <i class="fas fa-undo-alt fa-3x text-primary mb-3"></i> <h5>Easy Returns</h5> <p class="text-muted">30-day return policy</p> </div> </div> <div class="col-md-3 mb-3"> <div class="feature-box text-center"> <i class="fas fa-lock fa-3x text-primary mb-3"></i> <h5>Secure Payment</h5> <p class="text-muted">100% secure transactions</p> </div> </div> <div class="col-md-3 mb-3"> <div class="feature-box text-center"> <i class="fas fa-headset fa-3x text-primary mb-3"></i> <h5>24/7 Support</h5> <p class="text-muted">Dedicated customer support</p> </div> </div> </div> </div> </section> <!-- Categories Section --> <section class="categories py-5 bg-light"> <div class="container"> <h3 class="text-center mb-4">Shop by Category</h3> <div class="row"> <?php foreach ($categories as $cat): ?> <div class="col-lg-2 col-md-4 col-6 mb-3"> <a href="shop.php?category=<?php echo $cat['id']; ?>" class="text-decoration-none"> <div class="category-card text-center p-3 bg-white rounded shadow-sm"> <i class="fas <?php echo $cat['icon'] ?? 'fa-tag'; ?> fa-3x text-primary mb-2"></i> <h6 class="mb-0"><?php echo htmlspecialchars($cat['name']); ?></h6> </div> </a> </div> <?php endforeach; ?> </div> </div> </section> <!-- Featured Products --> <section class="featured-products py-5"> <div class="container"> <h3 class="text-center mb-4">Featured Products</h3> <div class="owl-carousel owl-theme product-carousel"> <?php foreach ($featuredProducts as $product): ?> <div class="product-card"> <div class="card h-100"> <div class="product-image position-relative"> <img src="<?php echo getProductImage($product['featured_image'], 'medium'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($product['name']); ?>"> <?php if ($product['is_on_sale'] && $product['compare_price'] > $product['price']): ?> <span class="badge bg-danger position-absolute top-0 start-0 m-2"> -<?php echo getDiscountPercentage($product['compare_price'], $product['price']); ?>% </span> <?php endif; ?> <div class="product-actions position-absolute top-0 end-0 m-2"> <button class="btn btn-sm btn-light mb-1 add-to-wishlist" data-product-id="<?php echo $product['id']; ?>"> <i class="far fa-heart"></i> </button> <button class="btn btn-sm btn-light add-to-cart" data-product-id="<?php echo $product['id']; ?>"> <i class="fas fa-shopping-cart"></i> </button> </div> </div> <div class="card-body"> <h6 class="card-title"> <a href="product.php?id=<?php echo $product['id']; ?>" class="text-decoration-none text-dark"> <?php echo htmlspecialchars(substr($product['name'], 0, 50)) . '...'; ?> </a> </h6> <p class="vendor-name small text-muted"> <i class="fas fa-store me-1"></i> <a href="vendor.php?id=<?php echo $product['vendor_id']; ?>" class="text-decoration-none"> <?php echo htmlspecialchars($product['store_name']); ?> </a> </p> <div class="rating mb-2"> <?php echo displayRating($product['avg_rating'] ?? 0, $product['review_count'] ?? 0); ?> </div> <div class="price"> <?php if ($product['is_on_sale'] && $product['compare_price'] > $product['price']): ?> <span class="text-muted text-decoration-line-through me-2"> <?php echo formatPrice($product['compare_price']); ?> </span> <span class="text-primary fw-bold"> <?php echo formatPrice($product['price']); ?> </span> <?php else: ?> <span class="text-primary fw-bold"> <?php echo formatPrice($product['price']); ?> </span> <?php endif; ?> </div> </div> </div> </div> <?php endforeach; ?> </div> </div> </section> <!-- Banner --> <section class="banner py-3"> <div class="container"> <div class="row"> <div class="col-md-6 mb-3"> <a href="shop.php?category=electronics"> <img src="assets/images/banners/banner1.jpg" class="img-fluid rounded" alt="Banner 1"> </a> </div> <div class="col-md-6 mb-3"> <a href="shop.php?category=fashion"> <img src="assets/images/banners/banner2.jpg" class="img-fluid rounded" alt="Banner 2"> </a> </div> </div> </div> </section> <!-- New Products --> <section class="new-products py-5 bg-light"> <div class="container"> <h3 class="text-center mb-4">New Arrivals</h3> <div class="row"> <?php foreach ($newProducts as $product): ?> <div class="col-lg-3 col-md-4 col-6 mb-3"> <div class="product-card"> <div class="card h-100"> <img src="<?php echo getProductImage($product['featured_image'], 'medium'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($product['name']); ?>"> <div class="card-body"> <h6 class="card-title"> <a href="product.php?id=<?php echo $product['id']; ?>" class="text-decoration-none text-dark"> <?php echo htmlspecialchars(substr($product['name'], 0, 40)) . '...'; ?> </a> </h6> <div class="price"> <span class="text-primary fw-bold"> <?php echo formatPrice($product['price']); ?> </span> </div> </div> </div> </div> </div> <?php endforeach; ?> </div> </div> </section> <!-- Footer --> <footer class="bg-dark text-white pt-5"> <div class="container"> <div class="row"> <div class="col-md-4 mb-4"> <h5><i class="fas fa-store me-2"></i><?php echo SITE_NAME; ?></h5> <p class="text-white-50">Your trusted multi-vendor marketplace for quality products at competitive prices.</p> <div class="social-links"> <a href="#" class="text-white me-2"><i class="fab fa-facebook fa-lg"></i></a> <a href="#" class="text-white me-2"><i class="fab fa-twitter fa-lg"></i></a> <a href="#" class="text-white me-2"><i class="fab fa-instagram fa-lg"></i></a> <a href="#" class="text-white me-2"><i class="fab fa-linkedin fa-lg"></i></a> </div> </div> <div class="col-md-2 mb-4"> <h6>Quick Links</h6> <ul class="list-unstyled"> <li><a href="about.php" class="text-white-50">About Us</a></li> <li><a href="contact.php" class="text-white-50">Contact</a></li> <li><a href="terms.php" class="text-white-50">Terms & Conditions</a></li> <li><a href="privacy.php" class="text-white-50">Privacy Policy</a></li> </ul> </div> <div class="col-md-3 mb-4"> <h6>Customer Service</h6> <ul class="list-unstyled"> <li><a href="faq.php" class="text-white-50">FAQ</a></li> <li><a href="shipping.php" class="text-white-50">Shipping Info</a></li> <li><a href="returns.php" class="text-white-50">Returns</a></li> <li><a href="support.php" class="text-white-50">Support</a></li> </ul> </div> <div class="col-md-3 mb-4"> <h6>Contact Info</h6> <ul class="list-unstyled text-white-50"> <li><i class="fas fa-map-marker-alt me-2"></i><?php echo SITE_ADDRESS; ?></li> <li><i class="fas fa-phone me-2"></i><?php echo SITE_PHONE; ?></li> <li><i class="fas fa-envelope me-2"></i><?php echo SITE_EMAIL; ?></li> </ul> </div> </div> <hr class="border-secondary"> <div class="row"> <div class="col-12 text-center pb-3"> <p class="text-white-50 mb-0">© <?php echo date('Y'); ?> <?php echo SITE_NAME; ?>. All rights reserved.</p> </div> </div> </div> </footer> <!-- Scripts --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script> <script src="assets/js/main.js"></script> <script> // Product Carousel $('.product-carousel').owlCarousel({ loop: true, margin: 10, nav: true, dots: false, responsive: { 0: { items: 1 }, 600: { items: 2 }, 1000: { items: 4 } } }); // Add to cart $('.add-to-cart').click(function() { let productId = $(this).data('product-id'); $.ajax({ url: 'api/cart.php', method: 'POST', data: { action: 'add', product_id: productId, quantity: 1 }, success: function(response) { if (response.success) { location.reload(); } else { alert(response.message); } } }); }); // Add to wishlist $('.add-to-wishlist').click(function() { let productId = $(this).data('product-id'); let btn = $(this); $.ajax({ url: 'api/wishlist.php', method: 'POST', data: { action: 'add', product_id: productId }, success: function(response) { if (response.success) { btn.find('i').toggleClass('far fas'); location.reload(); } else { alert(response.message); } } }); }); </script> <style> .product-card { transition: transform 0.3s; } .product-card:hover { transform: translateY(-5px); } .product-image { overflow: hidden; } .product-image img { transition: transform 0.3s; } .product-card:hover .product-image img { transform: scale(1.05); } .product-actions { opacity: 0; transition: opacity 0.3s; } .product-card:hover .product-actions { opacity: 1; } .category-card { transition: all 0.3s; } .category-card:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(0,0,0,0.1) !important; } .category-card i { transition: transform 0.3s; } .category-card:hover i { transform: scale(1.1); } </style> </body> </html>
Environment Configuration
File: .env
# Database Configuration DB_HOST=localhost DB_NAME=marketplace DB_USER=root DB_PASS= # Application Configuration SITE_NAME=Marketplace SITE_URL=http://localhost/marketplace SITE_VERSION=1.0.0 DEBUG_MODE=true # Security SESSION_TIMEOUT=7200 BCRYPT_ROUNDS=12 # Upload Configuration MAX_FILE_SIZE=5242880 # 5MB in bytes # Pagination ITEMS_PER_PAGE=20 # Date/Time TIMEZONE=America/New_York DATE_FORMAT=Y-m-d TIME_FORMAT=H:i # Currency CURRENCY=USD CURRENCY_SYMBOL=$ # Marketplace Settings COMMISSION_RATE=10.00 TAX_RATE=0.00 MIN_PAYOUT=50.00 MAX_PRODUCT_IMAGES=5 # Email Configuration SMTP_HOST=smtp.gmail.com SMTP_PORT=587 [email protected] SMTP_PASS=your_password SMTP_ENCRYPTION=tls # Payment Gateway - Stripe STRIPE_KEY=pk_test_your_key STRIPE_SECRET=sk_test_your_secret # Payment Gateway - PayPal PAYPAL_CLIENT_ID=your_client_id PAYPAL_SECRET=your_secret # Payment Gateway - Razorpay RAZORPAY_KEY=your_key RAZORPAY_SECRET=your_secret # Site Contact Info [email protected] SITE_PHONE=+1-555-123-4567 SITE_ADDRESS=123 Market St, City, State 12345
File: .gitignore
# Environment variables .env # Dependencies /vendor/ node_modules/ # IDE files .vscode/ .idea/ *.sublime-* # OS files .DS_Store Thumbs.db # Logs /logs/ *.log # Uploads /uploads/ !/uploads/.gitkeep # Cache /cache/ !/cache/.gitkeep # Composer composer.lock # Temp files *.tmp *.temp
File: composer.json
{ "name": "marketplace/application", "description": "Multi-Vendor Online Marketplace", "type": "project", "require": { "php": ">=7.4", "ext-pdo": "*", "ext-json": "*", "ext-mbstring": "*", "ext-curl": "*", "ext-gd": "*", "phpmailer/phpmailer": "^6.8", "stripe/stripe-php": "^13.0", "paypal/rest-api-sdk-php": "^1.14", "razorpay/razorpay": "2.*", "intervention/image": "^2.7" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "autoload": { "psr-4": { "Marketplace\\": "src/" } }, "scripts": { "test": "phpunit tests", "post-install-cmd": [ "chmod -R 755 uploads/", "chmod -R 755 logs/", "chmod -R 755 cache/" ] } } File: .htaccess
# Enable Rewrite Engine RewriteEngine On # Base directory RewriteBase /marketplace/ # Redirect to HTTPS (uncomment in production) # RewriteCond %{HTTPS} off # RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] # Remove index.php from URL RewriteCond %{THE_REQUEST} ^[A-Z]{3,9]\ /index\.php\ HTTP/ RewriteRule ^index\.php$ / [R=301,L] # Pretty URLs RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] # Security Headers <IfModule mod_headers.c> Header set X-Content-Type-Options "nosniff" Header set X-Frame-Options "SAMEORIGIN" Header set X-XSS-Protection "1; mode=block" </IfModule> # Disable directory browsing Options -Indexes # Protect sensitive files <FilesMatch "^\."> Order allow,deny Deny from all </FilesMatch> <FilesMatch "\.(env|git|log|sql)$"> Order allow,deny Deny from all </FilesMatch> # Compress files <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json </IfModule> # Cache static assets <IfModule mod_expires.c> ExpiresActive On ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" </IfModule> How to Use the Project (Step-by-Step Guide)
Prerequisites
- Web Server: XAMPP, WAMP, MAMP, or any PHP-enabled server (PHP 7.4+)
- Database: MySQL 5.7+ or MariaDB
- Composer: For dependency management
- Browser: Modern web browser (Chrome, Firefox, Edge, etc.)
- Code Editor: VS Code, Sublime Text, or any preferred editor
Installation Steps
Step 1: Set Up Local Server
- Download and install XAMPP from https://www.apachefriends.org/
- Launch XAMPP Control Panel
- Start Apache and MySQL services
Step 2: Install Composer Dependencies
- Download and install Composer from https://getcomposer.org/
- Open terminal/command prompt
- Navigate to your project directory
- Run:
composer install
Step 3: Create Project Folder
- Navigate to
C:\xampp\htdocs\(Windows) or/Applications/XAMPP/htdocs/(Mac) - Create a new folder named
marketplace - Create all the folders and files as shown in the Project File Structure
Step 4: Set Up Database
- Open browser and go to
http://localhost/phpmyadmin - Click on "New" to create a new database
- Name the database
marketplaceand selectutf8_general_ci - Click on "Import" tab
- Click "Choose File" and select the
database.sqlfile from thesqlfolder - Click "Go" to import the database structure and sample data
Step 5: Configure Environment
- Rename
.env.exampleto.envin the project root - Update database credentials if different from default:
DB_HOST=localhost DB_NAME=marketplace DB_USER=root DB_PASS=
- Update application URL:
SITE_URL=http://localhost/marketplace
- Configure email settings if using email notifications
- Configure payment gateway if using online payments
Step 6: Set Folder Permissions
Create the following folders and ensure they are writable:
uploads/products/uploads/products/thumb/uploads/products/medium/uploads/products/gallery/uploads/vendors/uploads/categories/uploads/profiles/logs/cache/
On Windows, right-click folders → Properties → Security → give Write permission to Users
On Mac/Linux, run: chmod -R 755 uploads/ logs/ cache/
Step 7: Create Admin Password Hash
- Go to
http://localhost/marketplace/register.php - Register a test user (e.g., email:
[email protected], password:Admin@123) - Open phpMyAdmin, go to the
userstable - Find the user and change
user_typeto 'admin' - Set
email_verifiedto 1
Step 8: Test the Installation
- Open browser and go to
http://localhost/marketplace/ - You should see the marketplace landing page
- Test different user types: Admin Login:
- Email:
[email protected] - Password:
Admin@123Vendor Registration: - Click "Become a Vendor" and register
- Wait for admin approval Customer Registration:
- Register as a new customer
- Browse and purchase products
System Walkthrough
For Customers:
- Browse Products - Search by category, keyword, or filter
- View Product - Check details, images, specifications, reviews
- Add to Cart - Select quantity and add to shopping cart
- Wishlist - Save products for later
- Checkout - Enter shipping address and payment details
- Order Tracking - View order status and tracking information
- Reviews - Rate and review purchased products
- Profile Management - Update personal information and addresses
For Vendors:
- Dashboard - View sales, revenue, and order statistics
- Product Management - Add, edit, and manage products
- Inventory Management - Track stock levels
- Order Management - Process orders and update status
- Earnings - View earnings and request withdrawals
- Reviews - View and respond to product reviews
- Store Profile - Customize store appearance
- Coupons - Create and manage discount coupons
For Admins:
- Dashboard - View platform-wide statistics
- Vendor Management - Approve/reject vendor applications
- Category Management - Manage product categories
- Product Approval - Approve/reject products
- Order Management - View all orders
Online Marketplace - Complete Project (Continued)
For Admins (Continued):
- Payment Management - Track payments and process vendor payouts
- Commission Settings - Configure category and vendor-specific commissions
- Report Generation - Generate sales, revenue, and performance reports
- System Settings - Configure marketplace settings, shipping methods, payment gateways
- User Management - Manage all users (customers, vendors, staff)
- Dispute Resolution - Handle customer-vendor disputes and refunds
Key Features Explained
Product Management System
- Product Creation: Vendors can add products with multiple images, variants (size, color), specifications, and pricing
- Inventory Tracking: Real-time stock management with low stock alerts
- Bulk Import/Export: CSV import/export for bulk product management
- Product Approval: Admin approval required for new products
- SEO Optimization: Meta titles, descriptions, and URLs for each product
- Related Products: Automatic suggestions based on category and tags
Shopping Cart & Checkout
- Persistent Cart: Cart saved for logged-in users across sessions
- Guest Checkout: Optional checkout without registration
- Multi-vendor Cart: Items from multiple vendors in single cart
- Shipping Calculation: Real-time shipping rates based on location and weight
- Tax Calculation: Automatic tax calculation based on location
- Coupon Application: Apply discount codes at checkout
- Multiple Payment Methods: Credit card, PayPal, bank transfer, cash on delivery
Order Management
- Order Processing: Automatic order splitting by vendor
- Status Tracking: Order status updates (pending, confirmed, shipped, delivered)
- Invoice Generation: Automatic PDF invoice generation
- Email Notifications: Order confirmation, shipping updates, delivery confirmation
- Return Management: Handle return requests and refunds
- Cancellation: Order cancellation with refund processing
Vendor Dashboard
- Sales Analytics: Visual charts for sales, revenue, and performance
- Product Performance: Best-selling products, low performers
- Customer Insights: Customer demographics and behavior
- Payout Management: Request withdrawals, view transaction history
- Store Customization: Upload logo, banner, customize store appearance
- Review Management: View and respond to customer reviews
Admin Dashboard
- Platform Overview: Total sales, revenue, vendors, products, customers
- Vendor Approval: Pending vendor applications
- Product Moderation: Pending product approvals
- Commission Reports: Earnings from commissions
- Payout Processing: Approve vendor withdrawal requests
- System Health: Server status, error logs, performance metrics
Review & Rating System
- Product Reviews: Customers can rate and review purchased products
- Vendor Ratings: Overall vendor rating based on product reviews
- Verified Purchase: Badge for verified purchases
- Review Moderation: Admin approval for reviews
- Review Responses: Vendors can respond to reviews
- Photo Reviews: Customers can upload photos with reviews
Coupon & Discount System
- Coupon Types: Percentage off, fixed amount off, free shipping
- Usage Limits: Per coupon, per customer limits
- Validity Period: Start and end dates
- Product/Category Restrictions: Apply to specific products or categories
- Minimum Order: Minimum cart value requirement
- Vendor Coupons: Vendors can create store-specific coupons
Shipping System
- Shipping Zones: Define shipping zones by country/region
- Shipping Methods: Flat rate, free shipping, table rates
- Weight-based Rates: Shipping cost based on order weight
- Location-based Rates: Different rates for different locations
- Vendor Shipping: Vendors can set their own shipping rules
- Tracking Integration: Add tracking numbers for shipped orders
Payment Gateway Integration
- Stripe: Credit/debit card payments
- PayPal: PayPal express checkout
- Razorpay: Indian payment gateway
- Bank Transfer: Manual bank transfer option
- Cash on Delivery: COD option with additional fee
- Refund Processing: Automatic refund to original payment method
Notification System
- Email Notifications: Order confirmations, shipping updates, password reset
- SMS Notifications: Optional SMS alerts (requires Twilio)
- In-app Notifications: Dashboard alerts for vendors and admins
- Push Notifications: Browser push notifications (optional)
SEO & Marketing
- SEO-friendly URLs: Clean URLs for products and categories
- Meta Tags: Custom meta titles and descriptions
- Sitemap Generation: Automatic sitemap.xml for search engines
- Social Media Integration: Open Graph tags for social sharing
- Google Analytics: Easy integration with tracking code
- Newsletter: Email newsletter subscription for customers
Multi-language Support
- Language Files: Easily translatable language files
- RTL Support: Right-to-left language support
- Currency Switching: Multiple currency support with exchange rates
API Endpoints
File: api/cart.php
<?php require_once '../includes/config.php'; header('Content-Type: application/json'); $action = $_POST['action'] ?? $_GET['action'] ?? ''; switch ($action) { case 'add': addToCart(); break; case 'update': updateCart(); break; case 'remove': removeFromCart(); break; case 'get': getCart(); break; case 'clear': clearCart(); break; default: echo json_encode(['success' => false, 'message' => 'Invalid action']); } function addToCart() { $productId = $_POST['product_id'] ?? 0; $quantity = $_POST['quantity'] ?? 1; $variantId = $_POST['variant_id'] ?? null; if (!$productId) { echo json_encode(['success' => false, 'message' => 'Product ID required']); return; } // Check if product exists and has sufficient stock $db = Database::getInstance(); if ($variantId) { $variant = $db->getRow( "SELECT * FROM product_variants WHERE id = ? AND product_id = ?", [$variantId, $productId] ); if (!$variant || $variant['quantity'] < $quantity) { echo json_encode(['success' => false, 'message' => 'Variant out of stock']); return; } $price = $db->getValue("SELECT price FROM products WHERE id = ?", [$productId]); $price += $variant['price_adjustment']; } else { $product = $db->getRow( "SELECT * FROM products WHERE id = ? AND status = 'approved'", [$productId] ); if (!$product) { echo json_encode(['success' => false, 'message' => 'Product not found']); return; } if ($product['quantity'] < $quantity) { echo json_encode(['success' => false, 'message' => 'Insufficient stock']); return; } $price = $product['price']; } // Add to session cart $cartItem = [ 'product_id' => $productId, 'variant_id' => $variantId, 'quantity' => $quantity, 'price' => $price, 'added_at' => time() ]; // Check if item already exists $found = false; foreach ($_SESSION['cart'] as &$item) { if ($item['product_id'] == $productId && $item['variant_id'] == $variantId) { $item['quantity'] += $quantity; $found = true; break; } } if (!$found) { $_SESSION['cart'][] = $cartItem; } // If user is logged in, save to database if (isset($_SESSION['user_id'])) { saveCartToDatabase($_SESSION['user_id'], $_SESSION['cart']); } echo json_encode([ 'success' => true, 'message' => 'Product added to cart', 'cart_count' => getCartCount(), 'cart_total' => getCartTotal() ]); } function updateCart() { $productId = $_POST['product_id'] ?? 0; $quantity = $_POST['quantity'] ?? 1; $variantId = $_POST['variant_id'] ?? null; foreach ($_SESSION['cart'] as &$item) { if ($item['product_id'] == $productId && $item['variant_id'] == $variantId) { if ($quantity <= 0) { // Remove item $_SESSION['cart'] = array_filter($_SESSION['cart'], function($i) use ($productId, $variantId) { return !($i['product_id'] == $productId && $i['variant_id'] == $variantId); }); } else { $item['quantity'] = $quantity; } break; } } if (isset($_SESSION['user_id'])) { saveCartToDatabase($_SESSION['user_id'], $_SESSION['cart']); } echo json_encode([ 'success' => true, 'cart_count' => getCartCount(), 'cart_total' => getCartTotal() ]); } function removeFromCart() { $productId = $_POST['product_id'] ?? 0; $variantId = $_POST['variant_id'] ?? null; $_SESSION['cart'] = array_filter($_SESSION['cart'], function($item) use ($productId, $variantId) { return !($item['product_id'] == $productId && $item['variant_id'] == $variantId); }); if (isset($_SESSION['user_id'])) { saveCartToDatabase($_SESSION['user_id'], $_SESSION['cart']); } echo json_encode([ 'success' => true, 'cart_count' => getCartCount(), 'cart_total' => getCartTotal() ]); } function getCart() { $cartItems = []; foreach ($_SESSION['cart'] as $item) { $db = Database::getInstance(); $product = $db->getRow( "SELECT p.*, v.store_name FROM products p JOIN vendors v ON p.vendor_id = v.id WHERE p.id = ?", [$item['product_id']] ); if ($product) { $cartItems[] = [ 'product_id' => $product['id'], 'name' => $product['name'], 'image' => getProductImage($product['featured_image'], 'thumb'), 'price' => $item['price'], 'quantity' => $item['quantity'], 'vendor' => $product['store_name'], 'total' => $item['price'] * $item['quantity'] ]; } } echo json_encode([ 'success' => true, 'items' => $cartItems, 'count' => getCartCount(), 'total' => getCartTotal() ]); } function clearCart() { $_SESSION['cart'] = []; if (isset($_SESSION['user_id'])) { $db = Database::getInstance(); $db->delete('cart', 'user_id = ?', [$_SESSION['user_id']]); } echo json_encode(['success' => true]); } function saveCartToDatabase($userId, $cart) { $db = Database::getInstance(); // Clear existing cart $db->delete('cart', 'user_id = ?', [$userId]); // Save new cart foreach ($cart as $item) { $db->insert('cart', [ 'user_id' => $userId, 'product_id' => $item['product_id'], 'variant_id' => $item['variant_id'], 'quantity' => $item['quantity'], 'price' => $item['price'] ]); } } ?> File: api/wishlist.php
<?php require_once '../includes/config.php'; header('Content-Type: application/json'); if (!isset($_SESSION['user_id'])) { echo json_encode(['success' => false, 'message' => 'Please login to use wishlist']); exit(); } $action = $_POST['action'] ?? $_GET['action'] ?? ''; $userId = $_SESSION['user_id']; $db = Database::getInstance(); switch ($action) { case 'add': addToWishlist(); break; case 'remove': removeFromWishlist(); break; case 'check': checkWishlist(); break; case 'get': getWishlist(); break; } function addToWishlist() { global $db, $userId; $productId = $_POST['product_id'] ?? 0; if (!$productId) { echo json_encode(['success' => false, 'message' => 'Product ID required']); return; } try { $db->insert('wishlist', [ 'user_id' => $userId, 'product_id' => $productId ]); echo json_encode(['success' => true, 'message' => 'Added to wishlist']); } catch (Exception $e) { echo json_encode(['success' => false, 'message' => 'Already in wishlist']); } } function removeFromWishlist() { global $db, $userId; $productId = $_POST['product_id'] ?? 0; $db->delete( 'wishlist', 'user_id = :user_id AND product_id = :product_id', ['user_id' => $userId, 'product_id' => $productId] ); echo json_encode(['success' => true, 'message' => 'Removed from wishlist']); } function checkWishlist() { global $db, $userId; $productId = $_GET['product_id'] ?? 0; $exists = $db->getRow( "SELECT id FROM wishlist WHERE user_id = ? AND product_id = ?", [$userId, $productId] ); echo json_encode(['in_wishlist' => !empty($exists)]); } function getWishlist() { global $db, $userId; $products = $db->getRows( "SELECT p.*, w.created_at as added_at FROM wishlist w JOIN products p ON w.product_id = p.id WHERE w.user_id = ? ORDER BY w.created_at DESC", [$userId] ); echo json_encode(['success' => true, 'products' => $products]); } ?> Cron Jobs
File: cron/process_orders.php
<?php /** * Cron Job for Processing Orders * Run this script every few minutes to process pending orders */ require_once __DIR__ . '/../includes/config.php'; $db = Database::getInstance(); // Process pending orders older than 30 minutes (abandoned carts) $db->query( "UPDATE orders SET status = 'cancelled' WHERE status = 'pending' AND payment_status = 'pending' AND created_at < DATE_SUB(NOW(), INTERVAL 30 MINUTE)" ); // Process orders waiting for confirmation $orders = $db->getRows( "SELECT * FROM orders WHERE status = 'pending' AND payment_status = 'paid'" ); foreach ($orders as $order) { // Update order status $db->update( 'orders', ['status' => 'confirmed'], 'id = :id', ['id' => $order['id']] ); // Send confirmation email sendOrderConfirmation($order['id']); } echo "Orders processed successfully\n"; function sendOrderConfirmation($orderId) { // Implementation for sending order confirmation email } ?> File: cron/send_reminders.php
<?php /** * Cron Job for Sending Reminders * Run this daily to send order reminders */ require_once __DIR__ . '/../includes/config.php'; $db = Database::getInstance(); // Send delivery reminders for orders shipped 5 days ago $orders = $db->getRows( "SELECT o.*, u.email, u.first_name FROM orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'shipped' AND o.updated_at < DATE_SUB(NOW(), INTERVAL 5 DAY)" ); foreach ($orders as $order) { // Send reminder email $subject = "Your Order " . $order['order_number'] . " - Delivery Update"; $data = ['order' => $order]; sendEmail($order['email'], $subject, 'delivery_reminder', $data); } echo "Reminders sent successfully\n"; ?> File: cron/update_product_ratings.php
<?php /** * Cron Job for Updating Product Ratings * Run this daily to update average ratings */ require_once __DIR__ . '/../includes/config.php'; $db = Database::getInstance(); // Update product ratings $products = $db->getRows("SELECT id FROM products"); foreach ($products as $product) { $rating = $db->getRow( "SELECT AVG(rating) as avg_rating, COUNT(*) as total_reviews FROM reviews WHERE product_id = ? AND status = 'approved'", [$product['id']] ); $db->update( 'products', [ 'rating' => $rating['avg_rating'] ?? 0, 'total_reviews' => $rating['total_reviews'] ?? 0 ], 'id = :id', ['id' => $product['id']] ); } // Update vendor ratings $vendors = $db->getRows("SELECT id FROM vendors"); foreach ($vendors as $vendor) { $rating = $db->getRow( "SELECT AVG(r.rating) as avg_rating, COUNT(*) as total_reviews FROM reviews r JOIN products p ON r.product_id = p.id WHERE p.vendor_id = ? AND r.status = 'approved'", [$vendor['id']] ); $db->update( 'vendors', [ 'rating' => $rating['avg_rating'] ?? 0, 'total_reviews' => $rating['total_reviews'] ?? 0 ], 'id = :id', ['id' => $vendor['id']] ); } echo "Ratings updated successfully\n"; ?> File: cron/process_payouts.php
<?php /** * Cron Job for Processing Vendor Payouts * Run this daily to process approved payout requests */ require_once __DIR__ . '/../includes/config.php'; $db = Database::getInstance(); // Get approved payout requests $payouts = $db->getRows( "SELECT p.*, v.user_id, v.payment_method, v.payment_details FROM payouts p JOIN vendors v ON p.vendor_id = v.id WHERE p.status = 'approved'" ); foreach ($payouts as $payout) { // Process payout based on payment method $paymentDetails = json_decode($payout['payment_details'], true); switch ($payout['payment_method']) { case 'paypal': $success = processPayPalPayout($payout, $paymentDetails); break; case 'bank': $success = processBankTransfer($payout, $paymentDetails); break; default: $success = false; } if ($success) { $db->update( 'payouts', [ 'status' => 'completed', 'processed_at' => date('Y-m-d H:i:s'), 'transaction_id' => generateTransactionId() ], 'id = :id', ['id' => $payout['id']] ); // Update vendor balance $db->query( "UPDATE vendors SET withdrawn_amount = withdrawn_amount + ? WHERE id = ?", [$payout['amount'], $payout['vendor_id']] ); } else { $db->update( 'payouts', ['status' => 'failed'], 'id = :id', ['id' => $payout['id']] ); } } echo "Payouts processed successfully\n"; function processPayPalPayout($payout, $details) { // Implement PayPal payout API return true; } function processBankTransfer($payout, $details) { // Implement bank transfer logic return true; } function generateTransactionId() { return 'PO' . date('Ymd') . strtoupper(uniqid()); } ?> Email Templates
File: includes/emails/order_confirmation.php
<!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; } .container { max-width: 600px; margin: 0 auto; padding: 20px; } .header { background: #4e73df; color: white; padding: 20px; text-align: center; } .content { padding: 20px; background: #f9f9f9; } .order-details { background: white; padding: 15px; border-radius: 5px; margin: 20px 0; } .order-item { border-bottom: 1px solid #eee; padding: 10px 0; } .total { font-size: 18px; font-weight: bold; text-align: right; margin-top: 20px; } .footer { text-align: center; padding: 20px; color: #666; font-size: 12px; } .button { background: #4e73df; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; } </style> </head> <body> <div class="container"> <div class="header"> <h1>Order Confirmation</h1> </div> <div class="content"> <h2>Thank you for your order, <?php echo $customer_name; ?>!</h2> <p>Your order has been received and is being processed.</p> <div class="order-details"> <h3>Order #<?php echo $order['order_number']; ?></h3> <p>Date: <?php echo formatDate($order['created_at']); ?></p> <?php foreach ($order_items as $item): ?> <div class="order-item"> <strong><?php echo $item['product_name']; ?></strong> <br> Quantity: <?php echo $item['quantity']; ?> x <?php echo formatPrice($item['price']); ?> = <?php echo formatPrice($item['total']); ?> </div> <?php endforeach; ?> <div class="total"> Subtotal: <?php echo formatPrice($order['subtotal']); ?><br> Shipping: <?php echo formatPrice($order['shipping_amount']); ?><br> Tax: <?php echo formatPrice($order['tax_amount']); ?><br> <strong>Total: <?php echo formatPrice($order['total_amount']); ?></strong> </div> </div> <h3>Shipping Address</h3> <p> <?php echo $shipping_address['first_name'] . ' ' . $shipping_address['last_name']; ?><br> <?php echo $shipping_address['address_line1']; ?><br> <?php if ($shipping_address['address_line2']): ?> <?php echo $shipping_address['address_line2']; ?><br> <?php endif; ?> <?php echo $shipping_address['city'] . ', ' . $shipping_address['state'] . ' ' . $shipping_address['postal_code']; ?><br> <?php echo $shipping_address['country']; ?> </p> <p style="text-align: center;"> <a href="<?php echo SITE_URL; ?>/customer/order-details.php?id=<?php echo $order['id']; ?>" class="button"> View Order Details </a> </p> </div> <div class="footer"> <p>© <?php echo date('Y'); ?> <?php echo SITE_NAME; ?>. All rights reserved.</p> </div> </div> </body> </html> File: includes/emails/vendor_approval.php
<!DOCTYPE html> <html> <head> <style> body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; } .container { max-width: 600px; margin: 0 auto; padding: 20px; } .header { background: #28a745; color: white; padding: 20px; text-align: center; } .content { padding: 20px; background: #f9f9f9; } .footer { text-align: center; padding: 20px; color: #666; font-size: 12px; } .button { background: #28a745; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; } </style> </head> <body> <div class="container"> <div class="header"> <h1>Congratulations!</h1> </div> <div class="content"> <h2>Your Vendor Account Has Been Approved</h2> <p>Dear <?php echo $vendor_name; ?>,</p> <p>We are pleased to inform you that your vendor application has been approved. You can now start selling your products on <?php echo SITE_NAME; ?>.</p> <h3>Next Steps:</h3> <ol> <li>Log in to your vendor dashboard</li> <li>Complete your store profile (logo, banner, description)</li> <li>Add your products</li> <li>Set up your shipping methods</li> <li>Configure your payment details for payouts</li> </ol> <p style="text-align: center;"> <a href="<?php echo SITE_URL; ?>/vendor/dashboard.php" class="button"> Go to Vendor Dashboard </a> </p> <p>If you have any questions, please don't hesitate to contact our support team.</p> <p>Best regards,<br>The <?php echo SITE_NAME; ?> Team</p> </div> <div class="footer"> <p>© <?php echo date('Y'); ?> <?php echo SITE_NAME; ?>. All rights reserved.</p> </div> </div> </body> </html> Product Page
File: product.php
<?php require_once 'includes/config.php'; $slug = $_GET['slug'] ?? $_GET['id'] ?? ''; if (!$slug) { redirect('/shop.php'); } $product = new Product(); $productData = $product->getProductBySlug($slug); if (!$productData) { $_SESSION['error'] = 'Product not found'; redirect('/shop.php'); } // Get product images $images = $product->getImages($productData['id']); // Get product variants $variants = $product->getVariants($productData['id']); // Get related products $relatedProducts = $product->getRelatedProducts($productData['id'], $productData['category_id']); // Get reviews $review = new Review(); $reviews = $review->getProductReviews($productData['id']); $ratingSummary = $review->getRatingSummary($productData['id']); // Update view count $db->query("UPDATE products SET total_views = total_views + 1 WHERE id = ?", [$productData['id']]); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo htmlspecialchars($productData['name']); ?> - <?php echo SITE_NAME; ?></title> <!-- Meta tags for SEO --> <meta name="description" content="<?php echo htmlspecialchars($productData['meta_description'] ?: substr($productData['short_description'] ?? $productData['name'], 0, 160)); ?>"> <meta name="keywords" content="<?php echo htmlspecialchars($productData['meta_keywords'] ?? ''); ?>"> <!-- Open Graph tags for social media --> <meta property="og:title" content="<?php echo htmlspecialchars($productData['name']); ?>"> <meta property="og:description" content="<?php echo htmlspecialchars($productData['short_description'] ?? ''); ?>"> <meta property="og:image" content="<?php echo getProductImage($productData['featured_image']); ?>"> <meta property="og:url" content="<?php echo SITE_URL; ?>/product.php?slug=<?php echo $productData['slug']; ?>"> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- Font Awesome --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <!-- Lightbox CSS --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/css/lightbox.min.css"> <!-- Custom CSS --> <link rel="stylesheet" href="assets/css/style.css"> </head> <body> <!-- Include header/navigation similar to index.php --> <div class="container py-4"> <!-- Breadcrumb --> <nav aria-label="breadcrumb"> <ol class="breadcrumb"> <li class="breadcrumb-item"><a href="index.php">Home</a></li> <li class="breadcrumb-item"><a href="shop.php">Shop</a></li> <li class="breadcrumb-item"><a href="shop.php?category=<?php echo $productData['category_id']; ?>"> <?php echo htmlspecialchars($productData['category_name']); ?> </a></li> <li class="breadcrumb-item active"><?php echo htmlspecialchars($productData['name']); ?></li> </ol> </nav> <div class="row"> <!-- Product Images --> <div class="col-md-6"> <div class="product-gallery"> <div class="main-image mb-3"> <a href="<?php echo getProductImage($productData['featured_image']); ?>" data-lightbox="product"> <img src="<?php echo getProductImage($productData['featured_image'], 'medium'); ?>" class="img-fluid" alt="<?php echo htmlspecialchars($productData['name']); ?>"> </a> </div> <?php if (count($images) > 0): ?> <div class="thumbnail-images row"> <?php foreach ($images as $image): ?> <div class="col-3"> <a href="<?php echo getProductImage($image['image_path']); ?>" data-lightbox="product"> <img src="<?php echo getProductImage($image['image_path'], 'thumb'); ?>" class="img-fluid" alt="Product thumbnail"> </a> </div> <?php endforeach; ?> </div> <?php endif; ?> </div> </div> <!-- Product Details --> <div class="col-md-6"> <h1 class="mb-3"><?php echo htmlspecialchars($productData['name']); ?></h1> <!-- Vendor Info --> <p class="vendor-info mb-3"> <i class="fas fa-store me-1"></i> Sold by: <a href="vendor.php?id=<?php echo $productData['vendor_id']; ?>" class="text-decoration-none"> <?php echo htmlspecialchars($productData['vendor_name']); ?> </a> <?php if ($productData['vendor_rating'] > 0): ?> <span class="ms-2"> <?php echo displayRating($productData['vendor_rating'], $productData['vendor_reviews']); ?> </span> <?php endif; ?> </p> <!-- Rating --> <div class="rating mb-3"> <?php echo displayRating($productData['rating'], $productData['total_reviews']); ?> </div> <!-- Price --> <div class="price mb-3"> <?php if ($productData['is_on_sale'] && $productData['compare_price'] > $productData['price']): ?> <span class="text-muted text-decoration-line-through h5 me-2"> <?php echo formatPrice($productData['compare_price']); ?> </span> <span class="text-primary h3"> <?php echo formatPrice($productData['price']); ?> </span> <span class="badge bg-danger ms-2"> Save <?php echo formatPrice($productData['compare_price'] - $productData['price']); ?> </span> <?php else: ?> <span class="text-primary h3"> <?php echo formatPrice($productData['price']); ?> </span> <?php endif; ?> </div> <!-- Short Description --> <?php if ($productData['short_description']): ?> <div class="short-description mb-3"> <?php echo nl2br(htmlspecialchars($productData['short_description'])); ?> </div> <?php endif; ?> <!-- Availability --> <div class="availability mb-3"> <?php if ($productData['quantity'] > 0): ?> <span class="text-success"> <i class="fas fa-check-circle me-1"></i> In Stock (<?php echo $productData['quantity']; ?> available) </span> <?php else: ?> <span class="text-danger"> <i class="fas fa-times-circle me-1"></i> Out of Stock </span> <?php endif; ?> </div> <!-- Variants --> <?php if (!empty($variants)): ?> <div class="variants mb-3"> <label class="form-label fw-bold">Options:</label> <div class="variant-options"> <?php foreach ($variants as $variant): ?> <div class="variant-item mb-2"> <div class="form-check"> <input class="form-check-input variant-radio" type="radio" name="variant" id="variant_<?php echo $variant['id']; ?>" value="<?php echo $variant['id']; ?>" data-price="<?php echo $productData['price'] + $variant['price_adjustment']; ?>" data-stock="<?php echo $variant['quantity']; ?>"> <label class="form-check-label" for="variant_<?php echo $variant['id']; ?>"> <?php $attrs = json_decode($variant['attributes'], true); $attrStrings = []; foreach ($attrs as $key => $value) { $attrStrings[] = ucfirst($key) . ': ' . $value; } echo implode(' | ', $attrStrings); ?> <?php if ($variant['price_adjustment'] != 0): ?> <span class="text-muted"> (<?php echo $variant['price_adjustment'] > 0 ? '+' : '-'; ?> <?php echo formatPrice(abs($variant['price_adjustment'])); ?>) </span> <?php endif; ?> <?php if ($variant['quantity'] <= 5): ?> <span class="badge bg-warning ms-2">Only <?php echo $variant['quantity']; ?> left</span> <?php endif; ?> </label> </div> </div> <?php endforeach; ?> </div> </div> <?php endif; ?> <!-- Quantity --> <div class="quantity mb-3"> <label for="quantity" class="form-label fw-bold">Quantity:</label> <div class="input-group" style="width: 150px;"> <button class="btn btn-outline-secondary" type="button" id="decreaseQty"> <i class="fas fa-minus"></i> </button> <input type="number" class="form-control text-center" id="quantity" value="1" min="1" max="<?php echo $productData['quantity']; ?>"> <button class="btn btn-outline-secondary" type="button" id="increaseQty"> <i class="fas fa-plus"></i> </button> </div> </div> <!-- Action Buttons --> <div class="action-buttons mb-3"> <?php if ($productData['quantity'] > 0): ?> <button class="btn btn-primary btn-lg me-2" id="addToCartBtn"> <i class="fas fa-shopping-cart me-2"></i>Add to Cart </button> <?php else: ?> <button class="btn btn-secondary btn-lg me-2" disabled> <i class="fas fa-shopping-cart me-2"></i>Out of Stock </button> <?php endif; ?> <button class="btn btn-outline-danger btn-lg" id="wishlistBtn"> <i class="far fa-heart me-2"></i>Add to Wishlist </button> </div> <!-- Product Meta --> <div class="product-meta bg-light p-3 rounded"> <?php if ($productData['sku']): ?> <p class="mb-1"><strong>SKU:</strong> <?php echo $productData['sku']; ?></p> <?php endif; ?> <?php if ($productData['brand_name']): ?> <p class="mb-1"><strong>Brand:</strong> <?php echo $productData['brand_name']; ?></p> <?php endif; ?> <?php if ($productData['category_name']): ?> <p class="mb-1"><strong>Category:</strong> <?php echo $productData['category_name']; ?></p> <?php endif; ?> <?php if ($productData['tags']): ?> <p class="mb-1"><strong>Tags:</strong> <?php $tags = explode(',', $productData['tags']); foreach ($tags as $tag): ?> <a href="shop.php?tag=<?php echo urlencode(trim($tag)); ?>" class="badge bg-secondary text-decoration-none"> <?php echo trim($tag); ?> </a> <?php endforeach; ?> </p> <?php endif; ?> </div> </div> </div> <!-- Product Details Tabs --> <div class="product-tabs mt-5"> <ul class="nav nav-tabs" id="productTabs" role="tablist"> <li class="nav-item" role="presentation"> <button class="nav-link active" id="description-tab" data-bs-toggle="tab" data-bs-target="#description" type="button">Description</button> </li> <?php if ($productData['specifications']): ?> <li class="nav-item" role="presentation"> <button class="nav-link" id="specifications-tab" data-bs-toggle="tab" data-bs-target="#specifications" type="button">Specifications</button> </li> <?php endif; ?> <li class="nav-item" role="presentation"> <button class="nav-link" id="reviews-tab" data-bs-toggle="tab" data-bs-target="#reviews" type="button">Reviews (<?php echo $productData['total_reviews']; ?>)</button> </li> </ul> <div class="tab-content p-4 border border-top-0 rounded-bottom" id="productTabsContent"> <!-- Description Tab --> <div class="tab-pane fade show active" id="description" role="tabpanel"> <?php echo $productData['description']; ?> </div> <!-- Specifications Tab --> <?php if ($productData['specifications']): ?> <div class="tab-pane fade" id="specifications" role="tabpanel"> <?php $specs = json_decode($productData['specifications'], true); if (is_array($specs)): ?> <table class="table table-striped"> <?php foreach ($specs as $key => $value): ?> <tr> <th style="width: 200px;"><?php echo ucwords(str_replace('_', ' ', $key)); ?></th> <td><?php echo $value; ?></td> </tr> <?php endforeach; ?> </table> <?php endif; ?> </div> <?php endif; ?> <!-- Reviews Tab --> <div class="tab-pane fade" id="reviews" role="tabpanel"> <div class="row"> <!-- Rating Summary --> <div class="col-md-4"> <div class="rating-summary text-center p-4 bg-light rounded"> <h2 class="display-4"><?php echo number_format($ratingSummary['average'], 1); ?></h2> <div class="rating mb-2"> <?php echo displayRating($ratingSummary['average']); ?> </div> <p class="text-muted">Based on <?php echo $ratingSummary['total']; ?> reviews</p> <div class="rating-breakdown text-start mt-3"> <?php for ($i = 5; $i >= 1; $i--): ?> <div class="row align-items-center mb-2"> <div class="col-4"><?php echo $i; ?> star</div> <div class="col-6"> <div class="progress"> <div class="progress-bar bg-warning" style="width: <?php echo $ratingSummary['breakdown'][$i] ?? 0; ?>%"></div> </div> </div> <div class="col-2"><?php echo $ratingSummary['counts'][$i] ?? 0; ?></div> </div> <?php endfor; ?> </div> </div> </div> <!-- Reviews List --> <div class="col-md-8"> <?php if ($auth->isLoggedIn() && $_SESSION['user_type'] == 'customer'): ?> <button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#writeReviewModal"> Write a Review </button> <?php endif; ?> <?php if (empty($reviews)): ?> <div class="text-center py-5"> <i class="far fa-comment-dots fa-4x text-muted mb-3"></i> <h5>No reviews yet</h5> <p class="text-muted">Be the first to review this product</p> </div> <?php else: ?> <?php foreach ($reviews as $review): ?> <div class="review-item border-bottom mb-3 pb-3"> <div class="d-flex justify-content-between align-items-start"> <div> <strong><?php echo htmlspecialchars($review['user_name']); ?></strong> <div class="rating mb-2"> <?php echo displayRating($review['rating']); ?> </div> <?php if ($review['title']): ?> <h6><?php echo htmlspecialchars($review['title']); ?></h6> <?php endif; ?> <p><?php echo nl2br(htmlspecialchars($review['comment'])); ?></p> <?php if ($review['images']): ?> <div class="review-images mb-2"> <?php $reviewImages = json_decode($review['images'], true); foreach ($reviewImages as $img): ?> <a href="<?php echo SITE_URL . '/uploads/reviews/' . $img; ?>" data-lightbox="review-<?php echo $review['id']; ?>"> <img src="<?php echo SITE_URL . '/uploads/reviews/thumb/' . $img; ?>" class="img-thumbnail me-1" style="width: 60px; height: 60px; object-fit: cover;"> </a> <?php endforeach; ?> </div> <?php endif; ?> <small class="text-muted"> Reviewed on <?php echo formatDate($review['created_at']); ?> <?php if ($review['is_verified']): ?> <span class="badge bg-success ms-2">Verified Purchase</span> <?php endif; ?> </small> </div> </div> </div> <?php endforeach; ?> <?php endif; ?> </div> </div> </div> </div> </div> <!-- Related Products --> <?php if (!empty($relatedProducts)): ?> <div class="related-products mt-5"> <h3 class="mb-4">You May Also Like</h3> <div class="row"> <?php foreach ($relatedProducts as $related): ?> <div class="col-lg-3 col-md-4 col-6 mb-3"> <div class="product-card card h-100"> <img src="<?php echo getProductImage($related['featured_image'], 'medium'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($related['name']); ?>"> <div class="card-body"> <h6 class="card-title"> <a href="product.php?slug=<?php echo $related['slug']; ?>" class="text-decoration-none text-dark"> <?php echo htmlspecialchars(substr($related['name'], 0, 40)) . '...'; ?> </a> </h6> <div class="price"> <span class="text-primary fw-bold"> <?php echo formatPrice($related['price']); ?> </span> </div> </div> </div> </div> <?php endforeach; ?> </div> </div> <?php endif; ?> </div> <!-- Write Review Modal --> <?php if ($auth->isLoggedIn() && $_SESSION['user_type'] == 'customer'): ?> <div class="modal fade" id="writeReviewModal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Write a Review</h5> <button type="button" class="btn-close" data-bs-dismiss="modal"></button> </div> <div class="modal-body"> <form id="reviewForm" enctype="multipart/form-data"> <input type="hidden" name="product_id" value="<?php echo $productData['id']; ?>"> <div class="mb-3"> <label class="form-label">Rating</label> <div class="rating-input"> <i class="far fa-star" data-rating="1"></i> <i class="far fa-star" data-rating="2"></i> <i class="far fa-star" data-rating="3"></i> <i class="far fa-star" data-rating="4"></i> <i class="far fa-star" data-rating="5"></i> </div> <input type="hidden" name="rating" id="ratingValue" required> </div> <div class="mb-3"> <label for="reviewTitle" class="form-label">Review Title</label> <input type="text" class="form-control" id="reviewTitle" name="title" placeholder="Summarize your review"> </div> <div class="mb-3"> <label for="reviewComment" class="form-label">Review</label> <textarea class="form-control" id="reviewComment" name="comment" rows="4" placeholder="Write your review here..." required></textarea> </div> <div class="mb-3"> <label for="reviewImages" class="form-label">Upload Images (Optional)</label> <input type="file" class="form-control" id="reviewImages" name="images[]" multiple accept="image/*"> <small class="text-muted">You can upload up to 5 images</small> </div> <button type="submit" class="btn btn-primary">Submit Review</button> </form> </div> </div> </div> </div> <?php endif; ?> <!-- Include footer --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/js/lightbox.min.js"></script> <script> // Quantity controls $('#decreaseQty').click(function() { let qty = parseInt($('#quantity').val()); if (qty > 1) { $('#quantity').val(qty - 1); } }); $('#increaseQty').click(function() { let qty = parseInt($('#quantity').val()); let max = parseInt($('#quantity').attr('max')); if (qty < max) { $('#quantity').val(qty + 1); } }); // Variant selection $('.variant-radio').change(function() { let price = $(this).data('price'); let stock = $(this).data('stock'); // Update price display $('.price .text-primary').text('<?php echo CURRENCY_SYMBOL; ?>' + price.toFixed(2)); // Update stock display if (stock > 0) { $('.availability').html('<span class="text-success"><i class="fas fa-check-circle me-1"></i> In Stock (' + stock + ' available)</span>'); $('#quantity').attr('max', stock); $('#addToCartBtn').prop('disabled', false); } else { $('.availability').html('<span class="text-danger"><i class="fas fa-times-circle me-1"></i> Out of Stock</span>'); $('#addToCartBtn').prop('disabled', true); } }); // Add to cart $('#addToCartBtn').click(function() { let productId = <?php echo $productData['id']; ?>; let quantity = $('#quantity').val(); let variantId = $('input[name="variant"]:checked').val(); $.ajax({ url: 'api/cart.php', method: 'POST', data: { action: 'add', product_id: productId, quantity: quantity, variant_id: variantId }, success: function(response) { if (response.success) { alert('Product added to cart!'); // Update cart count in header $('.cart-count').text(response.cart_count); } else { alert(response.message); } } }); }); // Wishlist functionality $('#wishlistBtn').click(function() { <?php if (!$auth->isLoggedIn()): ?> alert('Please login to use wishlist'); window.location.href = 'login.php'; return; <?php endif; ?> let productId = <?php echo $productData['id']; ?>; let btn = $(this); $.ajax({ url: 'api/wishlist.php', method: 'POST', data: { action: 'add', product_id: productId }, success: function(response) { if (response.success) { btn.find('i').removeClass('far').addClass('fas'); alert('Added to wishlist'); } else { alert(response.message); } } }); }); // Check if product is in wishlist <?php if ($auth->isLoggedIn()): ?> $.ajax({ url: 'api/wishlist.php?action=check&product_id=<?php echo $productData['id']; ?>', method: 'GET', success: function(response) { if (response.in_wishlist) { $('#wishlistBtn i').removeClass('far').addClass('fas'); } } }); <?php endif; ?> // Rating input $('.rating-input i').mouseenter(function() { let rating = $(this).data('rating'); $('.rating-input i').each(function(index) { if (index < rating) { $(this).removeClass('far').addClass('fas'); } else { $(this).removeClass('fas').addClass('far'); } }); }).click(function() { let rating = $(this).data('rating'); $('#ratingValue').val(rating); }); $('.rating-input').mouseleave(function() { let selectedRating = $('#ratingValue').val(); if (selectedRating) { $('.rating-input i').each(function(index) { if (index < selectedRating) { $(this).removeClass('far').addClass('fas'); } else { $(this).removeClass('fas').addClass('far'); } }); } else { $('.rating-input i').removeClass('fas').addClass('far'); } }); // Review form submission $('#reviewForm').submit(function(e) { e.preventDefault(); if (!$('#ratingValue').val()) { alert('Please select a rating'); return; } let formData = new FormData(this); $.ajax({ url: 'api/review.php', method: 'POST', data: formData, processData: false, contentType: false, success: function(response) { if (response.success) { alert('Review submitted successfully! It will be visible after approval.'); $('#writeReviewModal').modal('hide'); location.reload(); } else { alert(response.message); } } }); }); </script> <style> .rating-input i { font-size: 1.5rem; color: #ffc107; cursor: pointer; margin-right: 5px; } .variant-item { padding: 5px; border: 1px solid #dee2e6; border-radius: 5px; } .variant-item:hover { background-color: #f8f9fa; } .product-meta p { font-size: 0.9rem; } .rating-breakdown .progress { height: 8px; } </style> </body> </html> Deployment Guide
Production Server Requirements
- PHP: 7.4 or higher
- MySQL: 5.7 or higher
- Web Server: Apache/Nginx
- SSL Certificate: For secure transactions
- Memory: Minimum 2GB RAM recommended
- Storage: Based on product images and uploads
Deployment Steps
Step 1: Prepare Server
- Update server packages:
sudo apt update && sudo apt upgrade -y
- Install required software:
sudo apt install apache2 mysql-server php8.1 php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip unzip -y
- Install Composer:
curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer
Step 2: Configure Apache
- Create virtual host:
<VirtualHost *:80> ServerName yourdomain.com DocumentRoot /var/www/html/marketplace <Directory /var/www/html/marketplace> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/marketplace_error.log CustomLog ${APACHE_LOG_DIR}/marketplace_access.log combined </VirtualHost> - Enable mod_rewrite:
sudo a2enmod rewrite sudo systemctl restart apache2
Step 3: Configure SSL (HTTPS)
- Install Certbot:
sudo apt install certbot python3-certbot-apache -y
- Obtain SSL certificate:
sudo certbot --apache -d yourdomain.com
Step 4: Database Setup
- Create database and user:
CREATE DATABASE marketplace CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'marketplace_user'@'localhost' IDENTIFIED BY 'strong_password'; GRANT ALL PRIVILEGES ON marketplace.* TO 'marketplace_user'@'localhost'; FLUSH PRIVILEGES;
- Import database schema:
mysql -u marketplace_user -p marketplace < sql/database.sql
Step 5: Application Configuration
- Upload files to server:
scp -r marketplace/* user@yourserver:/var/www/html/marketplace/
- Set proper permissions:
cd /var/www/html/marketplace sudo chown -R www-data:www-data . sudo chmod -R 755 uploads/ logs/ cache/ sudo chmod -R 777 uploads/
- Configure environment:
cp .env.example .env nano .env
Update with production values:
DB_HOST=localhost DB_NAME=marketplace DB_USER=marketplace_user DB_PASS=strong_password SITE_URL=https://yourdomain.com DEBUG_MODE=false
- Install dependencies:
composer install --no-dev --optimize-autoloader
Step 6: Set Up Cron Jobs
Add these to crontab (crontab -e):
# Process orders every 5 minutes */5 * * * * php /var/www/html/marketplace/cron/process_orders.php >> /var/www/html/marketplace/logs/cron.log 2>&1 # Send reminders daily at 9 AM 0 9 * * * php /var/www/html/marketplace/cron/send_reminders.php >> /var/www/html/marketplace/logs/cron.log 2>&1 # Update ratings daily at 2 AM 0 2 * * * php /var/www/html/marketplace/cron/update_product_ratings.php >> /var/www/html/marketplace/logs/cron.log 2>&1 # Process payouts daily at 3 AM 0 3 * * * php /var/www/html/marketplace/cron/process_payouts.php >> /var/www/html/marketplace/logs/cron.log 2>&1
Step 7: Configure Email
- Set up SMTP in
.env:
SMTP_HOST=smtp.gmail.com SMTP_PORT=587 [email protected] SMTP_PASS=your-app-password SMTP_ENCRYPTION=tls
- For Gmail, enable 2-factor authentication and create app-specific password
Step 8: Payment Gateway Setup
- Stripe:
- Create account at https://stripe.com
- Get API keys from dashboard
- Update
.envwith live keys
- PayPal:
- Create developer account at https://developer.paypal.com
- Create app and get client ID/secret
- Update
.envwith live credentials
Step 9: Security Hardening
- Set proper file permissions:
find /var/www/html/marketplace -type f -exec chmod 644 {} \; find /var/www/html/marketplace -type d -exec chmod 755 {} \; chmod -R 777 /var/www/html/marketplace/uploads/ chmod -R 777 /var/www/html/marketplace/logs/ - Enable firewall:
sudo ufw allow 22 sudo ufw allow 80 sudo ufw allow 443 sudo ufw enable
- Install fail2ban:
sudo apt install fail2ban -y sudo systemctl enable fail2ban
- Secure MySQL:
sudo mysql_secure_installation
Step 10: Monitoring Setup
- Install monitoring tools:
sudo apt install htop iotop nethogs -y
- Set up log rotation:
sudo nano /etc/logrotate.d/marketplace
Add:
/var/www/html/marketplace/logs/*.log { daily rotate 30 compress delaycompress missingok notifempty create 644 www-data www-data } Performance Optimization
Enable Caching
- Add to
.htaccess:
<IfModule mod_expires.c> ExpiresActive On ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" </IfModule> <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json </IfModule>
Database Optimization
- Add indexes to frequently queried columns (already in schema)
- Enable query cache in MySQL:
SET GLOBAL query_cache_size = 268435456; SET GLOBAL query_cache_type = 1;
- Regular maintenance:
OPTIMIZE TABLE products; OPTIMIZE TABLE orders; OPTIMIZE TABLE users;
Use CDN for Static Assets
- Configure CDN (Cloudflare, etc.)
- Update asset URLs in configuration
Backup Strategy
Automated Database Backups
Create backup script: /usr/local/bin/backup-marketplace.sh
#!/bin/bash BACKUP_DIR="/var/backups/marketplace" DATE=$(date +%Y%m%d_%H%M%S) DB_NAME="marketplace" DB_USER="marketplace_user" DB_PASS="strong_password" # Create backup directory mkdir -p $BACKUP_DIR # Backup database mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/db_$DATE.sql.gz # Backup uploads tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz /var/www/html/marketplace/uploads/ # Keep only last 30 days of backups find $BACKUP_DIR -name "db_*.sql.gz" -mtime +30 -delete find $BACKUP_DIR -name "uploads_*.tar.gz" -mtime +30 -delete # Sync to remote server (optional) # rsync -avz $BACKUP_DIR/ user@backup-server:/backups/marketplace/
Add to crontab:
0 1 * * * /usr/local/bin/backup-marketplace.sh
Monitoring & Alerts
Set up monitoring with Uptime Robot or similar service
Configure email alerts for critical errors in .env
// Add to error handler if (!$debug) { set_error_handler(function($errno, $errstr, $errfile, $errline) { $message = "Error: [$errno] $errstr in $errfile on line $errline"; error_log($message); // Send email alert for critical errors if ($errno == E_ERROR || $errno == E_USER_ERROR) { mail(ADMIN_EMAIL, "Critical Error on Marketplace", $message); } }); } Scaling Considerations
As traffic grows, consider:
- Load Balancing: Distribute traffic across multiple servers
- Database Replication: Master-slave configuration
- Redis/Memcached: For session and query caching
- Queue System: RabbitMQ for processing orders/emails
- CDN: For serving images and static assets
- Elasticsearch: For advanced product search
Sample Redis Configuration:
// In config.php define('USE_REDIS', true); define('REDIS_HOST', 'localhost'); define('REDIS_PORT', 6379); // Cache product queries $redis = new Redis(); $redis->connect(REDIS_HOST, REDIS_PORT); $cacheKey = 'product_' . $productId; $product = $redis->get($cacheKey); if (!$product) { $product = $db->getRow("SELECT * FROM products WHERE id = ?", [$productId]); $redis->setex($cacheKey, 3600, serialize($product)); } Conclusion
The Online Marketplace is a comprehensive, production-ready e-commerce platform that provides all the features needed to run a successful multi-vendor marketplace. With its robust architecture, extensive feature set, and scalable design, it serves as an excellent foundation for any online business venture.
Key Achievements:
- Multi-Vendor Support: Complete system for vendors to manage their products, orders, and earnings
- User Role Management: Four distinct user types with appropriate permissions
- Secure Transactions: PCI-compliant payment processing with multiple gateways
- Scalable Architecture: Designed to handle growing traffic and product catalog
- SEO Optimized: Search engine friendly URLs and meta tags
- Mobile Responsive: Works seamlessly across all devices
- Extensive Reporting: Comprehensive analytics for informed decision-making
- Automated Tasks: Cron jobs for order processing, reminders, and maintenance
Business Benefits:
- Revenue Generation: Commission-based model for platform earnings
- Vendor Growth: Attract and retain vendors with easy-to-use tools
- Customer Satisfaction: Seamless shopping experience with multiple payment options
- Operational Efficiency: Automated processes reduce manual work
- Data-Driven Decisions: Rich analytics for business intelligence
Technical Excellence:
- Clean Code Architecture: MVC-inspired structure for maintainability
- Security Best Practices: SQL injection prevention, XSS protection, CSRF tokens
- Performance Optimized: Caching, database indexing, CDN ready
- Extensible Design: Easy to add new features and integrations
- Well Documented: Comprehensive code comments and documentation
Future Enhancements Possible:
- Mobile apps (iOS/Android) using REST API
- AI-powered product recommendations
- Voice search integration
- AR/VR product preview
- Blockchain-based supply chain tracking
- Social media integration and sharing
- Subscription-based vendor plans
- Advanced fraud detection
- Multi-language and multi-currency support
- Dropshipping automation
This marketplace platform is ready for deployment and can be customized to fit specific business needs. Whether you're starting a new e-commerce venture or upgrading an existing one, this system provides a solid foundation that can grow with your business.
The combination of comprehensive features, security measures, and scalability makes it suitable for:
- General merchandise marketplaces
- Niche product platforms
- Service-based marketplaces
- B2B wholesale platforms
- Handmade/craft marketplaces
- Digital products marketplace
With proper maintenance, regular updates, and continuous improvement, this marketplace can serve as a long-term, profitable business platform.