El reto
Mueblería Paty es una mueblería con entregas en la Ciudad de México y el Estado de México. Su tienda vivía en Shopify: una mensualidad, una app de pago por cada función extra y un techo para lo que el negocio necesitaba de verdad, como vender a meses con financieras mexicanas, organizar los camiones de reparto o saber quién del equipo está en la tienda.
El objetivo era salir de Shopify sin perder nada de lo que ya funcionaba: el posicionamiento en Google, los catálogos de anuncios en Google, Meta, TikTok y Pinterest, el historial de clientes y pedidos, y la app que ya estaba publicada en Google Play.
Arquitectura
Una sola aplicación Elixir / Phoenix sirve dos sitios: la tienda pública y el panel de administración. Cada uno tiene su propio endpoint, router, cookie de sesión y assets, y un despachador por dominio (HostDispatch) manda cada petición al sitio que le toca. Ambos comparten la lógica de negocio y la base de datos.
páginas, checkout, /api/tienda, feeds, MCP Panel de administración
pedidos, entregas, catálogo, analítica
| Backend y web | Elixir, Phoenix 1.8, Phoenix LiveView, Ecto, Bandit, Tailwind y daisyUI |
|---|---|
| Datos | SQLite con réplica continua a almacenamiento S3 (Litestream); imágenes en un bucket compatible con S3 |
| Contenido | Blog en Markdown renderizado al compilar (MDEx); imágenes para compartir generadas con libvips |
| Apps móviles | Kotlin: app de clientes (Views) y app de administración (Jetpack Compose) con notificaciones de Firebase Cloud Messaging |
| Agente de tienda | Rust y Tauri: app de bandeja para macOS y Windows |
| Infraestructura | Docker (Alpine), Cloudflare, Sentry, health checks con monitoreo externo, Tailscale para el acceso interno |
Una migración sin perder SEO
Lo más delicado de dejar Shopify es lo que ya está indexado. La nueva tienda conserva exactamente las mismas rutas que Shopify, así que Google, los anuncios y los enlaces compartidos siguen llegando al mismo lugar:
- Mismas URLs:
/products/:handle,/collections/:handle,/pages/:handle,/blogs/:blog/:handley/search. Las variantes de Shopify (/collections/:c/products/:p,?variant=, sitemaps hijos) redirigen con 301 a su URL canónica. - Las redirecciones de Shopify se importaron y se administran desde el panel, con la misma lógica: una ruta vieja que no existe recibe un 301 a su destino, conservando la query.
- Las imágenes que Shopify servía desde el dominio (
/cdn/shop/…) redirigen a nuestra copia, y las fotos de productos, páginas y colecciones se copiaron a nuestro propio almacenamiento antes de cerrar la tienda. - Se importaron los títulos y meta descripciones exactos de cada página, y una tarea (
mix shop.check_urls) verifica que cada URL del sitemap en vivo responda 200 en la nueva plataforma antes de cambiar el DNS. - Se importó el historial de clientes y pedidos (incluidas las ventas de punto de venta), y la numeración de pedidos continúa donde se quedó Shopify.
Pagos: cuatro pasarelas, un solo flujo
Además del pago contra entrega, el checkout ofrece Mercado Pago (tarjetas a meses, OXXO, SPEI), Atrato Pago (crédito a meses), Aplazo y Kueski Pay (pago en quincenas). Cada pasarela es una implementación del mismo behaviour de Elixir, sobre un flujo compartido:
- El pedido se crea como esperando pago y el cliente paga en el sitio del proveedor; su carrito se conserva hasta que el pago se aprueba.
- Al volver o al llegar el webhook, el pedido se vuelve a leer desde la API del proveedor, así que una notificación falsificada no puede aprobarlo. Donde el proveedor no tiene esa API (Kueski), la notificación sólo cuenta si viene firmada con un JWT válido.
- Cada 10 minutos se revisan los pedidos pendientes por si se perdió una notificación, y se cancelan solos cuando expiran (más tiempo con Mercado Pago, cuyas fichas de OXXO duran tres días).
- Las entregas y cancelaciones se reportan al proveedor, y desde el panel se hacen reembolsos totales o parciales. Cada llamada y notificación queda registrada.
Marketing y medición
Feeds de catálogo
Feeds diarios para Google Merchant Center, Meta, TikTok y Pinterest, una entrada por variante con precio, oferta, existencias y fotos. Se conservaron los IDs de Shopify para que los catálogos y campañas existentes siguieran funcionando.
Eventos de e-commerce
GA4, Google Ads, Meta, TikTok y Pinterest a partir de un solo registro de eventos. Las compras también van a Meta desde el servidor (Conversions API), deduplicadas con el pixel, para no perderlas con bloqueadores de anuncios.
Vistas previas para WhatsApp
Cada foto genera una versión ligera (menos de 280 KB) para og:image, porque WhatsApp descarta la imagen de la vista previa cuando pesa mucho más de 300 KB.
Reseñas de todas partes
Actores de Crawlee leen a diario las reseñas de Google Maps, Trustpilot y Facebook, y las guardan en la plataforma con un registro de cada sincronización.
Lista para el comercio con IA
La tienda publica un servidor MCP (Model Context Protocol) para que asistentes como ChatGPT o Claude puedan buscar en el catálogo, consultar zonas y condiciones de entrega, y armar un enlace de checkout para el comprador. Es de sólo lectura: el asistente nunca paga ni crea pedidos; el cliente termina la compra en la página de checkout.
También publica un /agents.md para agentes, y el panel tiene una sección de Asistentes IA que muestra qué preguntan, qué buscan sin encontrar y cuántos pedidos llegan por esos enlaces.
La operación, en el mismo sistema
El panel de administración está hecho con Phoenix LiveView: todo se actualiza en tiempo real vía PubSub, sin recargar la página.
Pedidos y entregas
Zonas de entrega, estatus de cada pedido y checkouts abandonados.
Camiones y viajes
Cada entrega es una parada de un viaje (camión, día y equipo); el avance del viaje mueve el estatus de sus entregas.
Analítica de ventas
Ventas de mostrador, en línea y pedidos manuales, uniendo el historial de Shopify con la tienda nueva.
Quién está en la tienda
Paty Agente, escrito en Rust, escanea la red local (ping, tabla ARP, mDNS y NetBIOS) y reporta por WebSocket qué teléfonos del equipo están conectados.
Catálogo y contenido
Productos, variantes, colecciones, páginas, blog en Markdown y redirecciones, editables desde el panel.
Admin en el teléfono
App Android que avisa de cada pedido y de cada pago aprobado, y permite editar productos y subir fotos desde la cámara.
La app de la tienda, sin Shopify
La app de clientes que ya estaba en Google Play se conectó a la nueva API (/api/tienda). El carrito vive en el teléfono y se vuelve a cotizar con los precios del día; para pagar, la app obtiene un enlace firmado que abre el checkout de la tienda en un WebView, así que zonas, pago contra entrega y todas las pasarelas funcionan igual que en la web.
Los clientes inician sesión sin contraseña, con un código de 6 dígitos enviado por correo (Resend), con límites por intento y por IP. Con sesión iniciada, el checkout se llena solo y Mis pedidos muestra también las compras hechas en la época de Shopify.
Operación confiable
- Respaldos continuos: cada escritura a la base de datos se replica a almacenamiento S3 con Litestream; si el servidor se pierde, el contenedor restaura el último respaldo antes de arrancar.
- Monitoreo: un endpoint
/healthque verifica la app y la base de datos, vigilado cada minuto desde fuera, y errores reportados a Sentry (incluido un webhook de pago rechazado o un pago que llega después de cancelar el pedido). - Seguridad: límites de peticiones por IP en la API, webhooks verificados contra la API o por firma, datos de clientes fuera del repositorio, y acceso interno por Tailscale.
- Despliegue simple: una imagen Docker con el release de Elixir; las migraciones corren solas al arrancar.
¿Tu tienda ya le quedó chica a Shopify?
Te ayudamos a migrar a una plataforma propia sin perder tu posicionamiento, tus anuncios ni tus clientes.
Platiquemos por WhatsApp