A Production-Grade E-Commerce Application Built Across Engineering Labs 01 – 06.
Secure JWT Auth · Catalog Search & Sort · Relational Wishlist · Global Cart State · Real Razorpay Checkout & Orders
🚀 Live Production Application: https://shopkart-vicky.vercel.app/
🛍️ Complete Full-Stack Application: Engineered modularly across Labs 01 - 06 connecting Secure Customer Authentication, Product Catalog Discovery, Relational Wishlists, Global Shopping Cart State, and Real-World Razorpay Checkout with Cryptographic Signature Verification.
🚀 Live Demo · About · Live Deployment · Quick Start · Lab Progression Matrix · Razorpay Integration · Architecture Guide · Postman API · Changelog
Modern e-commerce platforms require a continuous, unbreakable digital thread connecting user identity, real-time product discovery, persistent shopping governance (Wishlist & Cart), and tamper-proof financial transaction workflows.
ShopKart is a production-style, full-stack e-commerce web application developed systematically across six engineering labs using Node.js, Express.js (v5), MongoDB (Mongoose), and React 19 (Vite). The platform strictly rejects superficial client-side shortcuts by enforcing server-calculated currency totals, relational Mongoose referencing, atomic inventory decrements, and timing-safe cryptographic signature validation.
- Complete Frontend and Backend Implementation:
- Modern React 19 Single Page Application (SPA) with React Router DOM, dynamic Context state, and custom responsive styling.
- Robust Express.js REST API structured with Model-View-Controller (MVC) separation, dedicated routers, and centralized error handling.
- Working Relational Database Integration:
- Primary MongoDB instance with automated In-Memory MongoDB fallback (
mongodb-memory-server) ensuring 100% out-of-the-box functionality in any environment. - Relational Mongoose
ObjectIdreferencing with.populate()for wishlists and cart items, combined with static snapshot immutability for finalized orders.
- Primary MongoDB instance with automated In-Memory MongoDB fallback (
- Real-World Razorpay Payment Integration:
- Powered by the official Razorpay Node.js SDK and client-side Standard Checkout popup (
https://checkout.razorpay.com/v1/checkout.js). - Server-initiated payment orders calculated in paise, followed by HMAC-SHA256 cryptographic signature verification using
crypto.timingSafeEqual.
- Powered by the official Razorpay Node.js SDK and client-side Standard Checkout popup (
- Security-First Architecture & Zero-Secrets Guarantee:
- Credentials stored in tamper-proof, unscriptable
HttpOnlyJWT cookies, eliminating Cross-Site Scripting (XSS) token exfiltration. - Complete immunity against Insecure Direct Object Reference (IDOR) attacks by binding all operations to
req.user._id. - Zero Secrets Policy: Strict
.gitignoreconfigurations prevent any API keys, secret credentials, or.envfiles from ever touching GitHub.
- Credentials stored in tamper-proof, unscriptable
- Resilient UI State Management:
- Every view implements explicit visual lifecycles for Loading, Empty Data, and Error Handling with retry capabilities.
ShopKart is fully deployed and accessible globally on Vercel:
- Production URL: https://shopkart-vicky.vercel.app/
- Platform: Vercel Cloud Platform (React 19 Vite SPA & Optimized Client Bundles)
- Availability: 24/7 Global CDN with SSL/TLS encryption enabled
- Live Capabilities:
- Customer account registration and seamless login authentication.
- Live product catalog search with instant regex matching and price-based sorting.
- Relational customer wishlists with toggle add/remove synchronization.
- Persistent shopping cart governance with real-time stock limits.
- Interactive Razorpay Standard Checkout popup testing and dynamic Order history tracking.
Tip
Experience the complete end-to-end e-commerce flow live in your browser right now at shopkart-vicky.vercel.app!
- Node.js:
v18.xor higher (LTS recommended) - npm:
v9.xor higher - MongoDB: Local MongoDB instance on
mongodb://127.0.0.1:27017(Optional — the server automatically boots an In-Memory MongoDB engine if local MongoDB is not running) - Razorpay Test Account: Free Test Key ID & Secret from the Razorpay Dashboard (optional for catalog/cart; required for checkout)
git clone https://github.com/TheVicky1/ShopKart.git
cd ShopKartNavigate to the backend/ directory, install packages, and create your local environment configuration:
cd backend
npm installCreate a .env file inside backend/ (this file is strictly ignored by Git and will never be pushed):
PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/shopkart
JWT_SECRET=your_jwt_secret_key_here
RAZORPAY_KEY_ID=rzp_test_your_key_id_here
RAZORPAY_KEY_SECRET=your_razorpay_secret_key_hereNote
- If
MONGO_URIis left as default and local MongoDB is offline, the backend seamlessly switches to an embedded In-Memory MongoDB instance and seeds a product catalog automatically. - Razorpay
RAZORPAY_KEY_IDbegins withrzp_test_.... Never swap the Key ID and Key Secret.
Start the backend API server:
npm run devThe backend server boots at http://localhost:5000.
In a new terminal window, navigate to the frontend/ directory, install packages, and start the Vite development server:
cd ../frontend
npm install
npm run devThe frontend client boots at http://localhost:5173.
Run the local verification checks to confirm end-to-end functionality across all layers:
# 1. Run Automated Lab 06 Validation Test Suite (Address regex, Paise math, HMAC-SHA256)
cd backend
npm test
# 2. Verify Operational Server & Database Health
curl http://localhost:5000/health
# 3. Verify Product Catalog Endpoint
curl http://localhost:5000/products
# 4. Verify Frontend Production Build Compilation
cd ../frontend
npm run buildShopKart was built progressively across six focused engineering milestones:
[ Lab 01: Auth Backend ] ──► [ Lab 02: Auth Frontend ] ──► [ Lab 03: Catalog Search ]
│
[ Lab 06: Checkout & Orders ] ◄── [ Lab 05: Cart & State ] ◄── [ Lab 04: Wishlist ]
-
Lab 01 - Customer Authentication (Backend):
- Designed the
CustomerMongoose schema withemailuniqueness and phone formatting. - Implemented one-way password hashing via
bcryptwith salt rounds. - Generated cryptographically signed JSON Web Tokens (JWT) stored in
HttpOnly,SameSite: strictcookies. - Developed route protection middleware (
auth.middleware.js) verifying JWT tokens.
- Designed the
-
Lab 02 - Customer Authentication (Frontend):
- Architected a responsive React 19 Single Page Application powered by Vite.
- Implemented controlled registration and login forms with real-time error states.
- Configured centralized Axios API communication with
withCredentials: truefor automatic cookie transmission. - Implemented protected client routing and user dashboard (
/home).
-
Lab 03 - Product Catalog, Search & Filtering:
- Modeled the
Productcollection enforcing constraints (min price: 0.01,min stock: 0). - Implemented dynamic database-level regex search (
$regex: '...', $options: 'i'), category filtering, and price sorting (price_asc,price_desc). - Built interactive UI components:
SearchBar.jsx,ProductCard.jsx, and detailed product views (/products/:id). - Implemented UI feedback states: Loading Skeleton, Empty State, and Error State.
- Modeled the
-
Lab 04 - Wishlist Experience & Relational Modeling:
- Extended the
Customerschema with anObjectIdarray referencing theProductmodel (ref: 'Product'). - Created protected REST endpoints:
POST /wishlist/:productIdwith409 Conflictduplicate prevention,GET /wishlistwith.populate(), andDELETE /wishlist/:productId. - Built a dynamic Wishlist UI (
/wishlist), item cards with remove actions, and live Navbar wishlist badge counters.
- Extended the
-
Lab 05 - Shopping Cart & Global State Governance:
- Extended the
Customerschema with a relationalcartsubdocument array (productObjectId reference +quantity). - Developed protected REST APIs:
POST /cart/:productId,GET /cart,PATCH /cart/:productId, andDELETE /cart/:productId. - Enforced server-side inventory stock validation, blocking quantities that exceed available stock.
- Built a global React
CartContextproviding derived computations:totalCountandsubtotal. - Developed the Shopping Cart page (
/cart) with dynamic[-]/[+]quantity controls and an Order Summary panel.
- Extended the
-
Lab 06 - Checkout, Persistent Orders & Real Razorpay Payment:
- Designed the comprehensive
Orderschema tracking user reference, address, immutable item snapshots, total amounts, and payment lifecycles. - Connected the official Razorpay Node.js SDK (
razorpay) to initiate server-side payment orders in paise. - Integrated client-side Razorpay Standard Checkout modal (
checkout.js) with responsive user feedback. - Implemented timing-safe cryptographic signature validation (
crypto.timingSafeEqual) onrazorpay_order_id|razorpay_payment_id. - Executed atomic inventory stock decrements upon successful payment and cleared customer carts.
- Developed dedicated order management views: Order History (
/orders) and detailed receipt tracking (/orders/:orderId).
- Designed the comprehensive
| Module / Feature | Lab | Backend Endpoint | Frontend Component / View | Architectural Pattern |
|---|---|---|---|---|
| Customer Registration | Lab 01 & 02 | POST /customers/register |
Register.jsx |
Bcrypt hashing, Mongoose validation |
| Customer Login & JWT | Lab 01 & 02 | POST /customers/login |
Login.jsx |
JWT token, HttpOnly Cookies |
| Protected Profile | Lab 01 & 02 | GET /customers/me |
Home.jsx |
protect auth middleware guard |
| Customer Logout | Lab 01 & 02 | POST /customers/logout |
Navbar.jsx |
Cookie invalidation (maxAge: 0) |
| Product Catalog Creation | Lab 03 | POST /products |
Backend Seeding | Schema rules (min price, min stock) |
| Dynamic Search & Filter | Lab 03 | GET /products?search=&category= |
Products.jsx, SearchBar.jsx |
MongoDB $regex, dynamic query building |
| Price Sorting | Lab 03 | GET /products?sort=price_asc|desc |
SearchBar.jsx |
Mongoose .sort({ price: 1 | -1 }) |
| Product Detail View | Lab 03 | GET /products/:id |
ProductDetails.jsx |
React Router useParams() |
| Wishlist Reference Schema | Lab 04 | customerSchema.wishlist |
Customer Model |
ObjectId array, ref: 'Product' |
| Add to Wishlist | Lab 04 | POST /wishlist/:productId |
ProductCard.jsx |
409 Conflict duplicate check |
| Get Populated Wishlist | Lab 04 | GET /wishlist |
Wishlist.jsx, WishlistCard.jsx |
Mongoose .populate('wishlist') |
| Remove from Wishlist | Lab 04 | DELETE /wishlist/:productId |
WishlistCard.jsx |
MongoDB $pull / array filtering |
| Wishlist Navbar Badge | Lab 04 | GET /wishlist |
Navbar.jsx |
Dynamic state badge counter |
| Cart Subdocument Schema | Lab 05 | customerSchema.cart |
Customer Model |
Subdocuments: { product, quantity } |
| Add / Increment Cart Item | Lab 05 | POST /cart/:productId |
ProductCard.jsx, ProductDetails.jsx |
Stock boundary checks, auto-increment |
| Update Cart Quantity | Lab 05 | PATCH /cart/:productId |
Cart.jsx |
[-] / [+] controls with max-stock limit |
| Remove Cart Item | Lab 05 | DELETE /cart/:productId |
Cart.jsx |
Subdocument removal by product ID |
| Global Cart State | Lab 05 | Context API | CartContext.jsx |
Derived totalCount, subtotal, auto-sync |
| Order Schema & Persistence | Lab 06 | Order Model |
order.model.js |
User reference, address, item snapshot |
| Razorpay Order Initiation | Lab 06 | POST /orders/create-payment-order |
Checkout.jsx |
Real Razorpay SDK, paise calculation |
| Razorpay Standard Modal | Lab 06 | Client SDK | window.Razorpay(options).open() |
Official hosted checkout modal |
| Signature Verification | Lab 06 | POST /orders/verify-payment |
Checkout.jsx |
HMAC-SHA256, crypto.timingSafeEqual |
| Inventory Decrement | Lab 06 | POST /orders/verify-payment |
order.controller.js |
Atomic $inc: { stock: -quantity } |
| Cart Auto-Clear | Lab 06 | POST /orders/verify-payment |
CartContext.jsx |
Server clears cart; context state resets |
| Order History & Details | Lab 06 | GET /orders, GET /orders/:id |
Orders.jsx, OrderDetails.jsx |
Formatted dates, items, status badges |
ShopKart avoids simulated payment mocks by connecting directly to the Razorpay Payment Gateway API. The payment pipeline is divided into six distinct, cryptographically governed phases:
[ Phase 1: Address Validation & Stock Lock ]
│
▼
[ Phase 2: Server-Side Razorpay Order Creation (Paise) ]
│
▼
[ Phase 3: Client Checkout Script Injection (checkout.js) ]
│
▼
[ Phase 4: Customer Completes Payment on Razorpay Modal ]
│
▼
[ Phase 5: Timing-Safe HMAC-SHA256 Signature Verification ]
│
▼
[ Phase 6: Atomic Stock Decrement, Cart Wipe & Order Confirmation ]
- When the customer submits the checkout form, the backend does not trust prices submitted by the client.
- The server validates all mandatory shipping fields (Full Name, 10-digit Phone, Street Address, City, State, 6-digit Pincode).
- The server iterates over the customer's populated cart, re-queries each product from the database, confirms that
stock >= quantity, and calculates the authoritative total:$$\text{Total Amount} = \sum (\text{Product.price} \times \text{Item.quantity})$$ - A ShopKart Order document is initialized in the database with status
PENDING_PAYMENT.
- Using the official
razorpayNode.js SDK, the backend converts the total amount to paise ($\text{Amount in INR} \times 100$ ):const razorpay = getRazorpayInstance(); const razorpayOrder = await razorpay.orders.create({ amount: Math.round(newOrder.totalAmount * 100), currency: 'INR', receipt: newOrder._id.toString(), notes: { shopKartOrderId: newOrder._id.toString() } });
- The generated
razorpayOrder.idis linked to the pending ShopKart Order. - The server returns
razorpayOrderId,amount,currency, and only the publicrazorpayKeyIdto the frontend. The secret key is never sent to the browser.
- The frontend dynamically loads
https://checkout.razorpay.com/v1/checkout.js. - An instance of
new window.Razorpay(options)is created and.open()is invoked:const options = { key: data.razorpayKeyId, amount: data.amount, currency: data.currency, name: 'ShopKart', description: `Order #${data.shopKartOrderId}`, order_id: data.razorpayOrderId, handler: async function (response) { // response contains: // razorpay_payment_id, razorpay_order_id, razorpay_signature await verifyPaymentWithBackend(response); }, theme: { color: '#2563eb' } };
- Upon payment completion, Razorpay provides
razorpay_order_id,razorpay_payment_id, andrazorpay_signature. - The frontend posts these credentials to
POST /orders/verify-payment. - The backend reconstructs the expected signature using HMAC-SHA256:
$$\text{Expected Signature} = \text{HMAC-SHA256}(\text{razorpay_order_id} + "|" + \text{razorpay_payment_id}, \text{RAZORPAY_KEY_SECRET})$$ - To prevent timing attacks, verification uses
crypto.timingSafeEqual:const generatedSignature = crypto .createHmac('sha256', process.env.RAZORPAY_KEY_SECRET) .update(`${razorpayOrderId}|${razorpayPaymentId}`) .digest('hex'); const isMatch = crypto.timingSafeEqual( Buffer.from(generatedSignature, 'utf-8'), Buffer.from(razorpaySignature, 'utf-8') );
- Once verified, the order status transitions to
CONFIRMEDandpaymentStatustoPAID. - Product inventories are decremented atomically via MongoDB:
await Product.findByIdAndUpdate(item.product, { $inc: { stock: -item.quantity } });
- The customer's cart array is wiped (
customer.cart = []), and the user is redirected to/orders/:orderId.
[Browser / Client] [Express Backend Server]
│ │
│── 1. POST /customers/login { email, password } ─────────────►│
│ ├─ 2. Find user in MongoDB
│ ├─ 3. bcrypt.compare(password, hash)
│ ├─ 4. jwt.sign({ id: user._id }, SECRET)
│◄── 5. Set-Cookie: token=<JWT>; HttpOnly; SameSite ───────────┤
│ │
│── 6. GET /customers/me (Browser auto-attaches cookie) ──────►│
│ ├─ 7. auth.middleware.js verifies JWT
│ ├─ 8. Attaches user to req.user
│◄── 9. Return JSON: { success: true, customer: {...} } ───────┤
[Product Details / Catalog]
│
▼ User clicks [Add to Cart]
POST /cart/:productId ──► auth.middleware.js (protect)
│
▼
cart.controller.js
├─ Check if product exists in catalog
├─ Verify: currentCartQty + 1 <= product.stock
├─ Item in cart? Increment quantity : push { product, quantity: 1 }
▼
Save to MongoDB Customer.cart
▼
CartContext receives updated cart ──► Recalculates totalCount & subtotal
[Client: Checkout.jsx] [Server: order.controller.js] [Razorpay Gateway]
│ │ │
│── 1. POST /orders/create-payment ────►│ │
│ (shipping address) ├─ Validates address & stock │
│ ├─ Calculates authoritative total │
│ ├─ Creates Order (PENDING_PAYMENT) │
│ │── 2. orders.create({ amount }) ──────►│
│ │◄── 3. Returns { id: "order_xyz" } ────┤
│◄── 4. Returns { razorpayOrderId } ────┤ │
│ │
│── 5. Injects checkout.js & opens modal ──────────────────────────────────────►│
│◄── 6. User pays via UPI/Card/NetBanking ──────────────────────────────────────┤
│ (returns payment_id, signature) │
│ │ │
│── 7. POST /orders/verify-payment ────►│ │
│ ├─ HMAC-SHA256 signature verification │
│ ├─ Decrements product stock │
│ ├─ Clears customer cart │
│ ├─ Sets Order status: CONFIRMED │
│◄── 8. Returns { success: true } ──────┤ │
▼
Navigates to /orders/:orderId (Receipt)
ShopKart balances normalized referencing with denormalized snapshotting depending on the operational context:
┌─────────────────────────┐ ┌─────────────────────────┐
│ Customer Model │ │ Product Model │
├─────────────────────────┤ ├─────────────────────────┤
│ _id: ObjectId │ │ _id: ObjectId │
│ fullName: String │ │ name: String │
│ email: String (Unique) │ │ price: Number (min 0.01)│
│ password: String (Hash) │ │ category: String │
│ wishlist: [ ProductId ] ├─(ref)───►│ stock: Number (min 0) │
│ cart: [ { product, qty }├─(ref)───►│ image: String │
└───────────┬─────────────┘ └───────────▲─────────────┘
│ │
│ (1-to-many) (snapshot copy)
▼ │
┌────────────────────────────────────────────────┴─────────────┐
│ Order Model │
├─────────────────────────────────────────────────────────────┤
│ _id: ObjectId │
│ user: ObjectId (ref: Customer) │
│ items: [ { product: ObjectId, name, price, quantity, image }│
│ shippingAddress: { fullName, phone, addressLine1, city... } │
│ totalAmount: Number │
│ paymentStatus: 'PENDING' | 'PAID' | 'FAILED' │
│ status: 'PENDING_PAYMENT' | 'CONFIRMED' | 'DELIVERED' │
│ razorpayOrderId: String │
│ razorpayPaymentId: String │
└─────────────────────────────────────────────────────────────┘
- Wishlist & Cart Use Referencing (
ref: 'Product'):- Items in wishlists and carts represent active interest. Storing
ObjectIdreferences guarantees that if a product's price, stock, or title changes, the user's wishlist and cart reflect live catalog data via.populate().
- Items in wishlists and carts represent active interest. Storing
- Orders Use Immutable Snapshots:
- An order represents a legal, financial transaction at a specific point in time. If a merchant increases a product's price from ₹1,999 to ₹2,499 next month, historical order receipts must remain unchanged. The
Ordermodel copies product name, unit price, and image into the order document.
- An order represents a legal, financial transaction at a specific point in time. If a merchant increases a product's price from ₹1,999 to ₹2,499 next month, historical order receipts must remain unchanged. The
- Automated In-Memory MongoDB Fallback:
- In development environments without a running MongoDB service,
backend/index.jsautomatically spawns an in-memory database usingmongodb-memory-serverand seeds standard catalog items, eliminating setup barriers.
- In development environments without a running MongoDB service,
| Method | Endpoint | Access | Body / Params | Status | Description |
|---|---|---|---|---|---|
POST |
/customers/register |
Public | { fullName, email, password, phone } |
201 Created |
Registers customer, hashes password with bcrypt. |
POST |
/customers/login |
Public | { email, password } |
200 OK |
Authenticates credentials; issues HttpOnly cookie. |
GET |
/customers/me |
Protected | None (Cookie auto-attached) | 200 OK |
Returns authenticated customer profile. |
POST |
/customers/logout |
Protected | None | 200 OK |
Clears authentication cookie. |
| Method | Endpoint | Access | Body / Query Params | Status | Description |
|---|---|---|---|---|---|
POST |
/products |
Public | { name, description, price, category, stock, image } |
201 Created |
Creates a new catalog item. |
GET |
/products |
Public | search, category, sort (price_asc|price_desc) |
200 OK |
Dynamic regex search, category filtering, and sorting. |
GET |
/products/:id |
Public | Path param :id |
200 OK |
Returns product details by ID. |
| Method | Endpoint | Access | Body / Params | Status | Description |
|---|---|---|---|---|---|
POST |
/wishlist/:productId |
Protected | Path param :productId |
200 OK |
Adds product to user's wishlist (409 if duplicate). |
GET |
/wishlist |
Protected | None | 200 OK |
Retrieves wishlist with populated product objects. |
DELETE |
/wishlist/:productId |
Protected | Path param :productId |
200 OK |
Removes product from wishlist. |
| Method | Endpoint | Access | Body / Params | Status | Description |
|---|---|---|---|---|---|
POST |
/cart/:productId |
Protected | Path param :productId, Body: { quantity } |
200 OK |
Adds product or increments quantity (stock bounded). |
GET |
/cart |
Protected | None | 200 OK |
Returns cart populated with product details. |
PATCH |
/cart/:productId |
Protected | Path param :productId, Body: { quantity } |
200 OK |
Updates quantity (quantity <= stock). |
DELETE |
/cart/:productId |
Protected | Path param :productId |
200 OK |
Removes product from user's cart. |
| Method | Endpoint | Access | Body / Params | Status | Description |
|---|---|---|---|---|---|
POST |
/orders/create-payment-order |
Protected | { shippingAddress: { fullName, phone, addressLine1, city, state, pincode } } |
201 Created |
Validates stock, calculates total, creates ShopKart Order and Razorpay Order. |
POST |
/orders/verify-payment |
Protected | { shopKartOrderId, razorpayOrderId, razorpayPaymentId, razorpaySignature } |
200 OK |
Verifies HMAC-SHA256 signature, decrements stock, wipes cart, confirms order. |
GET |
/orders |
Protected | None | 200 OK |
Returns authenticated user's order history. |
GET |
/orders/:orderId |
Protected | Path param :orderId |
200 OK |
Returns detailed order receipt and delivery info. |
As instructed by academic security policies, no API keys, private tokens, or .env files are tracked in this Git repository:
backend/.envis strictly ignored by Git.backend/.env.examplehas been removed from the repository.- Root .gitignore enforces wildcard rules:
# Environment variables & secrets .env .env* *.env backend/.env* frontend/.env*
Rather than storing tokens in localStorage (which is vulnerable to XSS attacks), JWT tokens are dispatched using secure cookies:
res.cookie('token', token, {
httpOnly: true, // Prevents client-side JavaScript access via document.cookie
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict', // Mitigates Cross-Site Request Forgery (CSRF)
maxAge: 24 * 60 * 60 * 1000 // 1-day expiration
});User identity is never accepted from query parameters or request bodies (e.g., /orders?userId=123 or { userId: "..." }). The authenticated user ID is extracted strictly from the verified JWT payload via req.user._id, preventing users from reading or mutating other accounts' carts, wishlists, or orders.
When verifying Razorpay signatures, standard string comparison (===) is vulnerable to side-channel timing analysis. ShopKart enforces crypto.timingSafeEqual over fixed-length buffer digests.
| Requirement | Audit Status | Implementation Location |
|---|---|---|
| Complete Frontend Implementation | ✅ VERIFIED | React 19 SPA, Navbar, Catalog, Wishlist, Cart, Checkout, Orders, Order Details |
| Complete Backend Implementation | ✅ VERIFIED | Express 5, Customer Auth, Catalog, Wishlist, Cart, Razorpay Orders |
| Working Database Integration | ✅ VERIFIED | MongoDB Mongoose with automated mongodb-memory-server in-memory fallback |
| Proper README with Setup Instructions | ✅ VERIFIED | Prerequisites, step-by-step setup, verification commands, full API specs |
| No API Keys or Secrets Pushed to Git | ✅ VERIFIED | Strict .gitignore, zero .env files tracked, .env.example removed |
| Branded Logo Integration | ✅ VERIFIED | ./public/brand/shopkart-logo.png included and rendered |
| Real Razorpay Standard Checkout Flow | ✅ VERIFIED | Official Razorpay SDK, paise conversion, timing-safe HMAC verification |
| Live Production Deployment | ✅ VERIFIED | shopkart-vicky.vercel.app globally accessible via Vercel Edge |
This project is licensed under the MIT License.