Skip to content

Juanmd14/traeapp

Repository files navigation

Trae App

CI Next.js TypeScript Supabase Tailwind CSS Mercado Pago License: MIT

🇪🇸 Español · 🇺🇸 English

🟢 En producción — primer cliente confirmado · Deploy continuo en Vercel

🌐 Demo pública: vadelivery.vercel.app

Plataforma de delivery local tipo PedidosYa/Rappi para una ciudad pequeña. Stack: Next.js (App Router) · Supabase · PostgreSQL · TailwindCSS · TypeScript · Vercel.

🎬 Ver la demo

La instancia en vadelivery.vercel.app está en producción con un cliente real, así que sólo se puede recorrer la UI del marketplace en modo navegación (home, fichas de comercio, catálogo, carrito) — no se realizan pedidos de prueba para no contaminar la base del cliente.

Para ver el flujo completo (checkout con Mercado Pago, tracking en vivo, panel del comercio con KDS, app del repartidor) hay dos caminos:

  • 📹 Pedirme un walkthrough en video o una llamada corta donde lo recorro contra una instancia de staging.
  • 🛠️ Clonarlo local con el setup de abajo (15 min): trae 5 comercios + 25 productos en el seed y se puede probar punta a punta con credenciales TEST de MP.

🖼️ Capturas

Home — listado de comercios Checkout — dirección + pago Tracking en vivo del pedido


🚀 Cómo arrancar (15 minutos)

1. Requisitos

2. Instalar

pnpm install
cp .env.example .env.local

3. Crear proyecto en Supabase

  1. https://supabase.com/dashboardNew project
  2. Settings → API → copiar Project URL, anon key y service_role key a .env.local

4. Aplicar el schema

SQL Editor → New query → pegar supabase/schema.sql → Run. Después correr supabase/seed/seed.sql para datos demo.

5. Configurar Auth

  • Authentication → Providers → habilitar Email (provider OTP).
  • Authentication → URL Configuration → Site URL: http://localhost:3000.
  • Authentication → Email Templates → opcional: traducir el copy.

6. Configurar Mercado Pago

  • En el panel de developers MP, crear una aplicación.
  • Copiar MP_ACCESS_TOKEN y MP_PUBLIC_KEY a .env.local (usar credenciales TEST).
  • Para webhooks en local: usar ngrok (ngrok http 3000) y configurar https://XXX.ngrok.io/api/webhooks/mercadopago en MP → Webhooks → seleccionar evento "Pagos".
  • Copiar el secret de webhook a MP_WEBHOOK_SECRET.

7. Generar tipos y arrancar

pnpm db:types
pnpm dev

🧠 Arquitectura

Las decisiones de diseño (stack, seguridad, idempotencia de webhooks, realtime, trade-offs conocidos) están explicadas en docs/ARCHITECTURE.md.


✅ Bloques implementados

Bloque 1 — Base + diseño

  • Estructura completa de carpetas, route groups
  • Sistema de diseño: paleta coral + acento verde + neutros stone, tipografía Geist
  • Schema SQL completo (13 migraciones, RLS por rol, RPC idempotente)
  • Seed con 5 comercios + 25 productos demo
  • Home, ficha comercio (catálogo SSR + ISR), 404, error boundary
  • Componentes shop: StoreCard, ProductCard, CategoryPill, PromoBanner

Bloque 2 — Auth + Onboarding

  • Login passwordless con OTP de 6 dígitos
  • Helpers de sesión y RBAC (getSession, requireAuth, requireRole)
  • next-safe-action con action, authAction, adminAction
  • Onboarding del comercio en 5 pasos: datos → dirección → operación → productos → publicar
  • Layout panel con sidebar (desktop) + bottom nav (móvil)
  • Server Actions: stores, products
  • Envío de OTP por Brevo SMTP (subdominio compartido brevosend.com; con dominio propio + DKIM el sender quedaría como noreply@<dominio> — detalle en ARCHITECTURE.md)

Bloque 3 — Carrito

  • Zustand store persistido en localStorage
  • Lógica de "carrito por comercio único" (modal de switch)
  • ProductCard con add to cart + animación de feedback
  • Página de carrito con resumen, control de cantidades, validación de mínimo
  • CartFloatingButton sticky

Bloque 4 — Checkout + Mercado Pago + Tracking

💳 Mercado Pago — integración completa

  • Checkout con creación de preferencia (createPreference) y redirect al flow oficial de MP
  • Webhook en /api/webhooks/mercadopago que re-consulta el pago a la API de MP (getPayment) antes de actualizar BD — mitiga inyección de webhooks falsos sin HMAC
  • RPC apply_payment_webhook en Postgres: idempotente (INSERT ... ON CONFLICT), sobrevive reintentos sin duplicar pedidos
  • Mapeo de estados MP → enum interno (pending / approved / rejected / cancelled)
  • Verificación HMAC pendiente — documentado como decisión consciente en ARCHITECTURE.md

🛒 Order pricing seguro

  • Pricing service: subtotal / total / comisión calculados 100% server-side
  • createOrderAction: nunca confía en precios del cliente, los lee desde BD por ID
  • CheckoutForm: dirección + método pago + notas, en una sola pantalla

📡 Realtime tracking

  • useOrderRealtime: hook con suscripción a postgres_changes (Supabase Realtime), sin polling
  • OrderTracker: stepper visual de 5 pasos con animaciones
  • Página /pedido/[id]: tracking en vivo + detalle completo + contacto comercio
  • Acciones del comercio: aceptar / marcar listo / rechazar

Bloque 5 — Panel comercio + Admin + App repartidor

  • Panel comercio: KDS de pedidos en vivo, CRUD de productos con imágenes y modifier groups (combos, opciones, mínimos/máximos), promociones, estadísticas de ventas, horarios de operación
  • Multi-store switcher: owners con varios locales cambian de comercio desde un selector en el panel
  • Notificaciones WhatsApp al comercio: en cada pedido nuevo vía Meta Cloud API con template aprobado (nuevo_pedido_ es_MX) — system user con token sin expiración
  • Panel admin: gestión de comercios, repartidores, usuarios, finanzas y pedidos
  • App del repartidor: pedidos disponibles, aceptar/rechazar, marcar estados (/driver/disponibles, /driver/activo)
  • Tracking del repartidor en mapa: posición en vivo cliente ↔ cliente vía Supabase Realtime broadcast

🔜 Próximos pasos

  • Email transaccional para confirmación / cambios de estado y push web
  • Verificación HMAC del webhook MP (gap conocido — documentado)
  • Tests: setup de Vitest + Playwright (en progreso, parte del curso de testing del autor)

📁 Estructura

src/
├── app/
│   ├── (auth)/                    Login + registro (OTP)
│   ├── (shop)/                    Marketplace cliente final
│   │   ├── page.tsx               Home
│   │   ├── s/[storeSlug]/         Ficha de comercio
│   │   ├── carrito/
│   │   ├── checkout/              ← bloque 4
│   │   └── pedido/[id]/           ← bloque 4 (tracking)
│   ├── (account)/                 Zona logueada del cliente
│   ├── comercio/
│   │   ├── onboarding/            5 pasos
│   │   └── (panel)/               Panel principal
│   ├── driver/                    App del repartidor
│   ├── admin/
│   └── api/webhooks/mercadopago/  ← bloque 4
│
├── components/
│   ├── ui/                        button, input, label, switch, form-field
│   ├── shop/                      store-card, product-card, category-pill, promo-banner
│   ├── cart/                      cart-floating-button
│   ├── checkout/                  checkout-form ← bloque 4
│   ├── order/                     order-tracker, order-tracker-live ← bloque 4
│   ├── store-admin/               onboarding-{stepper,basic,address,operation,products,publish}
│   └── shared/                    shop-header, bottom-nav, login-form
│
├── server/
│   ├── auth/session.ts
│   ├── actions/
│   │   ├── safe-action.ts         clients
│   │   ├── auth.ts
│   │   ├── stores.ts
│   │   ├── products.ts
│   │   └── orders.ts              ← bloque 4
│   └── services/
│       ├── pricing.service.ts     ← bloque 4
│       └── mercadopago.service.ts ← bloque 4
│
├── lib/
│   ├── supabase/                  client, server, admin
│   └── utils.ts
│
├── stores/cart.ts                 Zustand
├── schemas/                       Zod
├── hooks/use-order-realtime.ts    ← bloque 4
├── styles/globals.css
└── middleware.ts

supabase/
├── migrations/                    13 archivos
├── schema.sql                     concatenado
├── seed/seed.sql                  5 comercios + 25 productos
└── config.toml

🔄 Flujo end-to-end del pedido

  1. Cliente arma carrito → /checkout
  2. Confirma → createOrderAction:
    • Trae productos reales de la BD (no confía en precios del cliente)
    • Aplica promoción si vino código
    • Calcula pricing server-side
    • Inserta orders + order_items + payments (status pending)
  3. Si efectivo → status pending → comercio acepta y pasa a preparing
  4. Si Mercado Pago:
    • Crea preferencia con external_reference = order.id
    • Redirige al init_point
    • Cliente paga en MP
    • Webhook /api/webhooks/mercadopago recibe notificación
    • getPayment(id) contra MP para confirmar el pago (HMAC pendiente)
    • Llama RPC apply_payment_webhook (idempotente):
      • Upsert en payments por mp_payment_id
      • Si approvedorders.payment_status='approved', status='confirmed', crea fila deliveries
  5. Comercio acepta → preparingready
  6. Repartidor toma → picked_updeliveredcompleted
  7. Realtime: el cliente ve los cambios en /pedido/[id] sin refrescar

🎨 Sistema de diseño

Token Hex Uso
primary-500 #FF4D3A Marca
primary-600 #E63823 CTAs
accent-500 #22C55E Envío gratis, success
warning-500 #F59E0B Promos % off
neutral-900 #1C1917 Títulos
neutral-500 #78716C Texto secundario

Tipografía: Geist. Mobile-first: container max-w-screen-sm.


🔑 Roles

Rol Puede
customer Navegar, pedir, ver sus pedidos, cancelar antes de aceptación
store_owner Todo lo de su comercio + aceptar/rechazar/marcar listo
store_staff Lo permitido en store_users
delivery_driver Tomar pedidos disponibles, marcar estados
admin Todo

RLS aplica reglas a nivel BD. Mutaciones críticas vía Server Action con service_role previa validación de permisos.


📦 Scripts

pnpm dev          # next dev
pnpm build        # next build
pnpm lint
pnpm type-check
pnpm db:types     # regenera src/types/database.types.ts
pnpm db:seed      # corre seed.sql

🐛 Debug Mercado Pago

  • Sandbox: usar credenciales TEST y la cuenta de comprador de test (creada en MP Developers).
  • Webhook local: ngrok http 3000 y pegar la URL HTTPS en MP → Webhooks.
  • Idempotencia: borrar payments con el mismo mp_payment_id para reproducir.

📚 Variables de entorno

NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_NAME=Trae App
NEXT_PUBLIC_SUPABASE_URL=https://XXX.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
MP_ACCESS_TOKEN=TEST-...
MP_PUBLIC_KEY=TEST-...
MP_WEBHOOK_SECRET=tu_secret

👤 Autor

Juan M. — Desarrollador full-stack enfocado en producto.

Proyecto construido como caso end-to-end: arquitectura, datos, auth, pagos, realtime y UX, manteniendo el stack acotado a herramientas de producción reales.


📄 Licencia

MIT © 2026 Juan M.

About

Marketplace de delivery local con Next.js 14, Supabase y Mercado Pago. Auth OTP, RLS, pagos con webhook firmado y tracking realtime.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors