Stop 19 · Build Full-Stack Portfolio ProjectsProjects · 2/12

E-commerce Platform (Full Stack)

Part of the Full Stack Developer Roadmap

In short

An online store touches every part of the stack: a catalogue, a cart that survives refresh, real payments and an order history. Recruiters know exactly how hard each part is.

A complete online store — product catalog, cart, checkout with real payments, and order history — one of the most résumé-relevant full-stack projects you can build.

The purple blob packs Indian sweets into a gift box behind a glass counter, a scooter waiting outside.

Think of it like this

The mithai shop at Diwali

The counter shows sweets with prices. You pick, they pack a box, you pay at the billing desk, and only after the payment clears does the delivery boy leave. Display, cart, payment, then fulfilment, in that strict order.

Watch it happen

From cart to paid order

Browser
Next.js server
Stripe
Database

checkout(cart)

1/6The cart sends only product ids and quantities. No prices from the browser.

The big ideas

What you need to know

Catalogue with search

Products with images, categories and filters, paginated on the server. Use SSG or ISR for product pages so they load fast.

A cart that survives

Keep the cart in the database for logged-in users and in localStorage for guests, then merge on login.

Server-side prices

Prices, discounts and totals are always recalculated on the server from the database.

Orders as a state machine

PENDING, PAID, SHIPPED, DELIVERED, CANCELLED. Only allowed moves, each one logged.

See it in code

Walk through the code

ts
1"use server";2import { redirect } from "next/navigation";3export async function checkout(cart: { productId: string; qty: number }[]) {4  const products = await db.product.findMany({ where: { id: { in: cart.map((c) => c.productId) } } });5  const order = await db.order.create({ data: { userId: await currentUserId(), status: "PENDING" } });6  const session = await stripe.checkout.sessions.create({7    mode: "payment",8    line_items: products.map((p) => ({9      quantity: cart.find((c) => c.productId === p.id)!.qty,10      price_data: { currency: "inr", unit_amount: p.pricePaise, product_data: { name: p.name } },11    })),12    metadata: { orderId: order.id },13    success_url: `${process.env.APP_URL}/orders/${order.id}`, cancel_url: `${process.env.APP_URL}/cart`,14  });15  redirect(session.url!);16}

1/4Load products from the database. Any price the browser might send is ignored.

A Next.js server action (stripe and db are your own lib clients). The browser sends what to buy, the server decides what it costs.

Save yourself the pain

Common mistakes

Oops

Reducing stock when an item is added to the cart.

Do this instead

Reduce stock when payment is confirmed, inside a transaction, and handle the 'sold out while paying' case.

Oops

Storing prices as floats like 499.99.

Do this instead

Store money as integers in the smallest unit (paise), and format only when displaying.

Your mission

First milestone: buy one thing

~45 min

Quick check

Where should the order total that you charge the customer be calculated?

Go deeper

  1. 1Implement the full flow: browse, cart, checkout with Stripe, order confirmation.
  2. 2Handle inventory correctly under concurrent orders.
  3. 3Add an admin view for managing products and viewing orders.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge