Construye una tienda online con Next.js, TypeScript y Stripe

Construye una tienda online con Next.js, TypeScript y Stripe

Construye una tienda online moderna con Next.js (App Router) y TypeScript: guía paso a paso

Objetivo: en 2–3 horas tendrás una base funcional de e‑commerce con Next.js 14 (App Router), TypeScript, Prisma + PostgreSQL, Stripe (Checkout), NextAuth, testing (Jest/Playwright) y despliegue (Vercel/Docker).


1) Requisitos e instalación

Resumen: Antes de escribir código, asegúrate de tener el entorno listo: Node 18+, gestor de paquetes, Git, PostgreSQL (local o Docker), Stripe CLI y (opcional) Vercel CLI.

Pasos:

  • Instala Node LTS (>=18) y un gestor: pnpm (recomendado), npm o yarn.
  • Instala Git, Docker Desktop (opcional) y PostgreSQL local o via Docker.
  • Instala Stripe CLI y (opcional) Vercel CLI.
  • Verifica versiones:

Checklist:

  • [ ] Node 18+ y gestor de paquetes
  • [ ] Git y repositorio inicializado
  • [ ] PostgreSQL accesible (local o Docker)
  • [ ] Stripe CLI instalado
  • [ ] Comandos de versión OK

2) Inicializar proyecto Next.js + TypeScript (App Router)

Resumen: Crearemos el proyecto con create-next-app, añadiremos ESLint, Prettier y (opcional) Tailwind.

Pasos:
1) Crear proyecto:

2) Instalar dependencias clave:

3) Estructura recomendada:

4) Scripts útiles y configuración inicial.

Código (package.json):

Explicación: Define scripts para desarrollo, build, migraciones y tests. Usa Next 14 (App Router).

Código (next.config.mjs):

Explicación: Habilita server actions y dominios para next/image.

Checklist:

  • [ ] Proyecto creado con App Router + TS
  • [ ] Scripts y next.config configurados
  • [ ] Estructura de carpetas lista

3) Autenticación y perfiles con NextAuth (Credentials)

Resumen: Implementamos login con email/contraseña con NextAuth y protegemos rutas.

Pasos:

  • Añade modelo User con passwordHash.
  • Configura NextAuth con CredentialsProvider.
  • Crea endpoint de registro y usa bcrypt.
  • Protege vistas de admin con getServerSession.

Código (lib/auth.ts):

Explicación: Configura NextAuth con Credentials; compara hashes y añade role al token/session.

Código (app/api/auth/[…nextauth]/route.ts):

Explicación: Endpoint de NextAuth en App Router.

Código (app/api/register/route.ts):

Explicación: Crea usuarios nuevos de forma segura.

Protección de ruta (app/admin/orders/page.tsx):

Explicación: Protección en Server Component; redirige si no hay permisos.

Checklist:

  • [ ] NextAuth configurado con Credentials
  • [ ] Endpoint de registro creado
  • [ ] Rutas admin protegidas

4) Base de datos y Prisma (PostgreSQL)

Resumen: Definimos el schema, aplicamos migraciones y datos de ejemplo.

Pasos:

  • Configura DATABASE_URL.
  • Define modelos y relaciones.
  • Ejecuta migraciones y seed.

Código (.env.example):

Explicación: Variables necesarias para DB, Stripe y NextAuth.

Código (prisma/schema.prisma):

Explicación: Modelos para usuarios, productos, carrito, pedidos e ítems. Precios en centavos para evitar errores de flotantes.

Comandos de migración y seed:

Código (prisma/seed.ts):

Explicación: Crea productos iniciales para pruebas.

Checklist:

  • [ ] DATABASE_URL configurado
  • [ ] Migraciones aplicadas con éxito
  • [ ] Datos seed insertados

5) Catálogo y UX: listado, filtros, SSR/ISR y caching

Resumen: Renderizamos productos en Server Components, con búsqueda y paginación. Habilitamos ISR.

Pasos:

  • Página principal con listado paginado.
  • Soporta query parameters ?q=&page=.
  • Usa fetch/prisma en server y revalidate.

Código (app/page.tsx):

Explicación: Server Component que consulta Prisma directamente; activa ISR con revalidate.

Código (components/ProductCard.tsx):

Explicación: Client Component para interacción; llama a un endpoint de carrito (lo crearemos en la sección de carrito).

Checklist:

  • [ ] Home SSR con paginación y búsqueda
  • [ ] ISR activo para catálogo
  • [ ] Tarjetas de producto funcionales

6) Carrito y checkout con Stripe (Checkout Sessions vs Payment Intents)

Resumen: Implementamos un carrito persistente en DB y un flujo de pago con Stripe Checkout.

Diferencias clave:

  • Stripe Checkout: hosted page, rápida de integrar, ideal para MVPs y tiendas estándar.
  • Payment Intents + Elements: más control UI/UX, ideal para flujos avanzados o internacionales.

Pasos carrito:

  • Endpoint /api/cart para agregar/leer.
  • Página /cart con botón de Checkout.

Código (app/api/cart/route.ts):

Explicación: CRUD mínimo para leer/agregar al carrito asociado al usuario autenticado.

Página de carrito (app/cart/page.tsx):

Explicación: Server Component que calcula total y muestra el botón de pago.

Código (components/CheckoutButton.tsx):

Explicación: Crea una sesión de Checkout y redirige al usuario.

Código (lib/stripe.ts):

Explicación: Cliente Stripe inicializado con API version fija.

Código (app/api/checkout/route.ts):

Explicación: Crea sesión de Checkout con line items y registra Orden PENDING.

Checklist:

  • [ ] Endpoint de carrito listo
  • [ ] Página /cart con total y botón de pago
  • [ ] Endpoint /api/checkout funcionando

Diagrama de flujo pago:


7) Webhooks y confirmación de pago segura

Resumen: Verificamos la firma del webhook, aplicamos idempotencia y actualizamos la orden.

Pasos:

  • Crea endpoint /api/webhooks/stripe.
  • Usa stripe.webhooks.constructEvent.
  • Marca orden como PAID e implementa idempotencia.

Código (app/api/webhooks/stripe/route.ts):

Nota: Añade el modelo WebhookEvent a Prisma (ver abajo). Explicación: Verifica firma, guarda event.id para idempotencia, actualiza orden y limpia carrito dentro de una transacción.

Amplía schema para idempotencia (añade a schema.prisma):

Aplica migración: pnpm prisma:migrate

Probar con Stripe CLI:

Prueba con HTTPie/curl (para health):

Checklist:

  • [ ] Webhook verifica firma correctamente
  • [ ] Idempotencia implementada con tabla WebhookEvent
  • [ ] Orden pasa de PENDING a PAID y carrito se vacía

8) Gestión de pedidos y panel admin (CRUD básico)

Resumen: Panel protegido para consultar productos y pedidos.

Pasos:

  • Crea páginas admin protegidas.
  • Lista pedidos y cambia estados.

Código (app/admin/orders/page.tsx):

Explicación: Vista simple de pedidos para admins.

Checklist:

  • [ ] Rutas admin protegidas por rol
  • [ ] Listado básico de pedidos implementado

9) Seguridad y buenas prácticas

Resumen: Asegura claves, valida entradas, limita peticiones y protege CSRF.

Pasos clave:

  • Variables sensibles solo en el servidor (env y Vercel Project Env).
  • Validación con zod en endpoints.
  • Rate limiting a endpoints públicos (ej. usando headers/caches o middlewares externos).
  • CSRF: en credenciales, usar NextAuth pages y POSTs con SameSite Lax; evita exponer endpoints mutadores al mundo sin auth.
  • Limita tamaño de payload (Next.js bodySizeLimit en route handler si es necesario).
  • CORS: webhooks de Stripe no requieren CORS; acepta solo POST y verifica firma.

Checklist:

  • [ ] .env no comiteado
  • [ ] Validación de inputs con zod
  • [ ] Rutas mutadoras protegidas
  • [ ] Verificación de firma en webhooks

10) Testing: unit, integration y E2E

Resumen: Añadimos tests unitarios con Jest y E2E con Playwright.

Pasos:

  • Configura Jest con ts-jest.
  • Test unitario simple.
  • Playwright para flujo de checkout (hasta redirección).

Config Jest (jest.config.js):

Código test unitario (tests/unit/product.test.ts):

Explicación: Test mínimo de utilidad.

Playwright config (package.json ya incluye test:e2e). Test E2E (tests/e2e/checkout.spec.ts):

Explicación: Test E2E simple (puedes extender para login y add-to-cart si mockeas auth/stripe).

Ejecutar tests:

Checklist:

  • [ ] Jest configurado y test unitario pasando
  • [ ] Playwright instalado y test E2E básico operativo

11) Observabilidad y métricas

Resumen: Añadimos logging, health check y métricas Prometheus.

Pasos:

  • Endpoint /api/health.
  • Métricas con prom-client.

Código (app/api/health/route.ts):

Código (app/api/metrics/route.ts):

Explicación: Exporta métricas estándar; integrable con Prometheus.

Checklist:

  • [ ] Health check responde 200
  • [ ] Métricas disponibles en /api/metrics

12) Performance y SEO técnico en Next.js

Resumen: Mejora LCP/TTFB, usa next/image, metadata y accesibilidad.

Pasos:

  • Usa Image optimizada y tamaños adecuados.
  • Añade metadata en layout.
  • Headings semánticos y ARIA.
  • Revisa Lighthouse.

Código (app/layout.tsx):

Explicación: Metadata para SEO/OG y layout básico.

Checklist:

  • [ ] next/image usado en tarjetas
  • [ ] Metadata rellenada
  • [ ] Lighthouse sin fallos críticos

13) CI/CD y despliegue (Vercel o Docker)

Resumen: Pipeline con GitHub Actions para lint/test/build y deploy a Vercel. Alternativa: Docker multi-stage.

Pasos:

  • Configura secrets en GitHub (DATABASEURL, STRIPESECRET_KEY, etc.).
  • Pipeline CI con Node + pnpm.

Código (.github/workflows/ci.yml):

Explicación: CI compila y prueba. Para desplegar en Vercel, conecta el repo desde el dashboard y configura variables de entorno.

Despliegue Vercel CLI (opcional):

Checklist:

  • [ ] Secrets configurados en el proveedor
  • [ ] CI ejecuta lint/test/build
  • [ ] Deploy realizado (Vercel recomendado)

14) Contenerización local con Docker (opcional)

Resumen: Dockerfile multi-stage y docker-compose con Postgres.

Código (Dockerfile):

Código (docker-compose.yml):

Explicación: Levanta Postgres y la app. Ajusta STRIPE variables.

Comandos:

Checklist:

  • [ ] Docker build OK
  • [ ] Postgres en contenedor
  • [ ] App accesible en localhost:3000

15) Archivos y ejemplos clave extra

Código (lib/prisma.ts):

Explicación: Singleton de Prisma para evitar conexiones extra en dev.

Código (components/CartIcon.tsx):

Explicación: Enlace rápido al carrito.


FAQ: preguntas frecuentes (respuestas directas)

  • ¿Stripe Checkout o Payment Intents? Checkout para MVPs y rapidez; Payment Intents + Elements para UX altamente personalizada.
  • ¿Dónde guardo el carrito? En DB (persistente) o en sesión/LocalStorage para guest; aquí usamos DB ligada al usuario.
  • ¿Cómo pruebo webhooks? stripe listen –forward-to localhost:3000/api/webhooks/stripe y stripe trigger checkout.session.completed.
  • ¿Puedo desplegar sin Vercel? Sí, con Docker en proveedores como Fly.io, Render o tu propio servidor.
  • ¿Cómo evito montos incorrectos? Usa enteros en centavos y valida en el backend.

Resolución de problemas comunes

  • Migraciones fallando: revisa DATABASE_URL, borra prisma/dev.db si usas SQLite, o run pnpm prisma:migrate reset (cuidado: borra datos) y vuelve a pnpm prisma:migrate.
  • Webhooks no llegan: verifica stripe listen apuntando a la ruta correcta y que el puerto 3000 esté activo; usa STRIPEWEBHOOKSECRET correcto.
  • Claves env faltantes: copia .env.example a .env.local y rellena todas; reinicia el servidor.
  • CORS en webhooks: no uses fetch desde navegador; los webhooks son server-to-server, no requieren CORS.
  • Errores de idempotencia: guarda event.id en tabla WebhookEvent y haz early return si ya procesado.

Checklist global final

  • [ ] Entorno listo (Node, Stripe CLI, Postgres)
  • [ ] Proyecto Next.js + TS creado (App Router)
  • [ ] Prisma y migraciones aplicadas + seed
  • [ ] NextAuth con Credentials y registro
  • [ ] Catálogo SSR/ISR con búsqueda y paginación
  • [ ] Carrito persistente y página /cart
  • [ ] Checkout con Stripe Checkout Sessions
  • [ ] Webhook verificado, idempotente y orden actualizada
  • [ ] Panel admin protegido
  • [ ] Tests Jest + Playwright básicos
  • [ ] Observabilidad (health, metrics)
  • [ ] SEO/Performance (metadata, image, Lighthouse)
  • [ ] CI configurado y despliegue (Vercel/Docker)

Próximos pasos y conclusión

Hemos construido una base sólida de e‑commerce: catálogo ISR, carrito en DB, Checkout con Stripe, webhooks seguros, admin básico, tests y CI/CD. A partir de aquí, añade variantes (tallas/colores), cupones, email de confirmación, internacionalización y un diseño más pulido.