🛒 Cartelle - Custom eCommerce Platform

A fully custom ecommerce platform built with Next.js (App Router), Prisma, and Stripe, focused on inventory safety and clear server architecture.

🔗 Live Links

🎯 Goal

Build a production-style ecommerce architecture implementing atomic inventory reservation, Stripe checkout, and admin tooling using a modern full-stack React architecture.

🏗️ Architecture Overview

Built with Next.js (App Router) using Server Components and Server Actions for data mutations. Prisma + PostgreSQL handle persistence, and Stripe manages payment intents and webhooks.

🎬 Demo

Here is a video of a complete checkout flow with stripe integration:

🧩 Key Engineering Decisions

  • Checkout-time inventory reservation to prevent overselling.
  • Clear boundary between Server Actions and API Routes.
  • Container/presentational component split for Storybook isolation.

🧠 System Design Deep Dive

1️⃣ Inventory Reservation

Problem:

Two or more users can start checkout for the same product at the same time. If their total requested quantity is greater than available stock, both can reach payment before the system validates quantity, causing an oversell.

Alternatives considered:

  1. Validate quantity in the Stripe webhook: Rejected because users would only discover stock issues after entering their payment details, creating a poor user experience.
  2. Reserve stock at add to cart: Temporarily hold the item for a user when they add it into their cart. This approach was avoided as stock would be locked by people who are just browsing and never intend to buy.

Solution:

Stock is reserved when the user clicks checkout: At this stage, purchase intent is significantly higher than at “Add to Cart,” making it a better balance between protecting inventory and preserving availability. When checkout is initiated, the cart status moves to CHECKOUT and the reserved quantity is deducted from available stock.

To handle abandoned checkouts, a scheduled job (implemented via an API route and configured in vercel.json) transitions carts from CHECKOUT to ABANDONED after 15 minutes of inactivity, releasing the reserved stock back into inventory.

Trade-offs:

Being on a Vercel Hobby plan meant that the cron job could run only once a day. In a production system this would be replaced with a more frequent job or a Redis-based time to live on the reservation key, which handles expiry atomically at the data layer.

2️⃣ Server Actions vs API Routes

Problem:

Next.js supports both Server Actions and API Routes for server-side logic. Without a clear rule for when to use each, the codebase becomes inconsistent and harder to maintain.

Alternatives considered:

  1. Use API routes for everything: Familiar and explicit, but adds unnecessary HTTP overhead for simple internal mutations that do not need public endpoints.
  2. Use Server Actions for everything: Removes HTTP overhead, but cannot handle external callbacks (for example webhooks/cron) and is not suitable for all payload types.

Solution:

Use Server Actions by default, and API Routes for integration boundaries: Most app-owned mutations use Server Actions for lower overhead. API Routes are reserved for cases where Server Actions are not a fit:

  1. Large file payloads: Server Actions have payload constraints, so large binary uploads should go through API Routes.
  2. External callers: Stripe webhooks and CRON triggers from vercel.json require an HTTP endpoint.

Trade-offs:

A Server-Action-first approach can make debugging less transparent because there is no explicit request/response cycle in the browser network tab like there is with API routes.

3️⃣ Component Splitting

Problem:

Components that mix server logic and JSX are hard to test and preview in isolation. If a component depends directly on Server Actions or data fetching, it cannot be rendered without full app context.

Alternatives considered:

  1. Keep logic and UI together: Simpler structure, but poor Storybook compatibility and harder isolated testing.
  2. Mock server context in Storybook: Possible, but adds maintenance overhead and tends to break as server logic evolves.

Solution:

Split into container and presentational layers:

  • CheckoutButton - owns Server Action wiring and control flow
  • CheckoutButtonUI - pure presentational JSX driven by props

This means the UI layer can be rendered in any context, including Storybook, without needing a server environment.

Trade-offs:

This adds files and prop plumbing, and it can complicate barrel exports because mixing server/client exports in the same index.ts may conflict with Storybook.

✨ Features

  • 🛒 Persistent cart with server-side mutations - cookie-backed cart ID, quantity guardrails, and transactional updates for line items.
  • 💳 Stripe checkout with webhook-driven order lifecycle - payment intent creation, multi-step checkout UI, and webhook-based order state transitions.
  • 📦 Checkout-time inventory reservation - stock is locked at checkout, released on timeout, and finalized on successful payment.
  • ⚙️ Admin CMS for products and blog - create/edit tooling for products, authors, and content with role-friendly flows.
  • 📚 Storybook-driven component system - isolated UI previews, reusable presentational components, and faster iteration.

🛠️ Tech Stack

  • Next.js 16 (App Router, Server Components, Server Actions)
  • TypeScript
  • PostgreSQL + Prisma ORM
  • Stripe (Checkout + Webhooks)
  • Tailwind CSS 4 + shadcn/ui
  • Framer Motion
  • react-hook-form + zod
  • Vercel Blob
  • TanStack Table
  • Storybook

🎨 Design Attribution

The visual direction for this project was inspired by the Cartelle Framer template.

No template code was used. The implementation was developed independently from design references (wireframes and typography) and extended with additional custom pages and features.

🚀 If Taken to Production

  • Security - Add real authentication and role-based access control for the admin dashboard (currently demo-flag gated).
  • Testing - Add integration tests covering Server Actions, checkout flow, and Stripe webhook lifecycle.
  • Observability - Add structured logging and alerting around Stripe webhooks and background cleanup jobs.
  • Product & UX - Introduce smarter product recommendations on PDPs.