AppMail es un servicio de automatización de facturas para tiendas Shopify que genera facturas en PDF a partir de pedidos y las envía automáticamente por correo electrónico al cliente final.
Cuando un cliente completa una compra en tu tienda Shopify, la plataforma envía automáticamente los datos del pedido a AppMail. El sistema procesa esta información, crea una factura profesional en PDF, la guarda en el servidor y la envía por email al cliente. Todo esto ocurre de forma automática en segundo plano, sin intervención manual.
La aplicación está desplegada en un servidor Ubuntu en la ruta /home/ubuntu/AppMail y responde en el dominio appmail.bruiser.es con conexión segura HTTPS.
| Tecnología | Versión | ¿Para qué sirve? |
|---|---|---|
| Python | 3.x | Lenguaje de programación principal del proyecto |
| Flask | - | Framework web ligero para crear la API que recibe los webhooks |
| Gunicorn | - | Servidor de aplicaciones que ejecuta Flask en producción |
| Celery | - | Sistema de colas de tareas para procesar pedidos en segundo plano |
| Redis | - | Base de datos en memoria que actúa como "buzón" de tareas para Celery |
| Nginx | - | Servidor web que recibe las peticiones externas y las redirige a Gunicorn |
| Let's Encrypt (Certbot) | - | Proveedor de certificados SSL gratuitos para HTTPS |
| Gmail SMTP | - | Servicio de Google para enviar emails con las facturas |
| Ubuntu Server | 20.04/22.04 | Sistema operativo donde está desplegado todo |
| Systemd | - | Sistema de servicios de Linux que arranca automáticamente la aplicación |
- Jinja2: Para generar el HTML de las facturas desde plantillas
- WeasyPrint / ReportLab: Para convertir HTML a PDF
- Requests: Para validar y procesar webhooks de Shopify
- python-dotenv: Para cargar variables de entorno desde
.env
AppMail/
│
├── 📄 README.md # Este archivo - documentación del proyecto
├── 📄 requirements.txt # Lista de dependencias Python a instalar
├── 📄 .env # Variables de entorno (credenciales, configuración)
├── 📄 .gitignore # Archivos que Git debe ignorar
│
├── 📂 server/ # Código del servidor web (Flask)
│ ├── 📄 __init__.py
│ ├── 📄 app.py # Aplicación Flask principal
│ ├── 📄 webhook_handler.py # Lógica para recibir webhooks de Shopify
│ ├── 📄 security.py # Validación de firmas HMAC de Shopify
│ └── 📂 __pycache__/
│
├── 📂 celery_app/ # Configuración de tareas asíncronas (Celery)
│ ├── 📄 __init__.py # Inicializa la aplicación Celery
│ ├── 📄 celery_config.py # Configuración de Redis, serialización, rutas
│ ├── 📄 tasks.py # Definición de las tareas (generar PDF, enviar email)
│ └── 📂 __pycache__/
│
├── 📂 src/ # Lógica de negocio del proyecto
│ ├── 📄 models.py # Modelos de datos (pedido, cliente, producto)
│ ├── 📄 invoice_validator.py # Valida que los datos del pedido sean correctos
│ ├── 📄 pdf_generator.py # Genera el PDF de la factura
│ ├── 📄 email_sender.py # Envía emails con la factura adjunta
│ ├── 📄 shopify_client.py # Cliente para comunicarse con API de Shopify (si aplica)
│ └── 📂 __pycache__/
│
├── 📂 config/ # Archivos de configuración
│ ├── 📄 __init__.py
│ ├── 📄 settings.py # Configuración general de la aplicación
│ └── 📄 translations.py # Traducciones de textos (español, catalán, etc.)
│
├── 📂 templates/ # Plantillas HTML
│ └── 📄 invoice_template.html # Plantilla HTML base para generar las facturas
│
├── 📂 invoices/ # Carpeta donde se guardan las facturas generadas
│ ├── 📄 factura_6226.pdf
│ ├── 📄 factura_6227.pdf
│ ├── 📄 factura_6228.pdf
│ └── ...
│
├── 📂 logs/ # Registros de actividad de la aplicación
│ ├── 📂 celery/ # Logs del worker de Celery
│ │ └── 📄 processed_orders.json
│ └── 📄 app.log # Logs generales de Flask
│
├── 📂 storage/ # Almacenamiento temporal
│ └── 📄 pending_invoices.json # Facturas pendientes de procesar
│
├── 📂 tests/ # Tests unitarios y de integración
│ │── 📄 __init__.py
│
│
│
├── 📄 run_server.py # Script para arrancar el servidor Flask manualmente
├── 📄 run_celery_worker.sh # Script para arrancar Celery manualmente
├── 📄 run_flower.sh # Script para arrancar Flower (monitor de Celery)
│
└── 📂 .venv/ # Entorno virtual de Python (no se sube a Git)
└── ...
-
server/: Contiene todo lo relacionado con Flask (la API web). Aquí se reciben los webhooks de Shopify y se valida que sean auténticos. -
celery_app/: Aquí está la configuración de Celery, que es el sistema que procesa las tareas "pesadas" (generar PDF, enviar email) sin bloquear el servidor web. -
src/: La lógica de negocio pura. Aquí están las funciones que realmente generan las facturas, validan datos y envían correos. -
config/: Archivos de configuración general de la aplicación (rutas, idiomas, parámetros). -
templates/: Plantillas HTML que se usan como "molde" para generar las facturas en PDF. -
invoices/: Carpeta donde se guardan todas las facturas generadas en formato PDF. -
logs/: Archivos de registro donde se guarda qué ha hecho la aplicación (útil para depurar errores).
Imagina que la aplicación es como una cadena de montaje en una fábrica:
- Shopify es el cliente que hace un pedido
- Nginx es el portero que recibe el pedido y lo pasa dentro
- Flask es el recepcionista que anota el pedido y lo pasa al taller
- Redis es el tablón donde se cuelgan los pedidos pendientes
- Celery Worker es el operario que toma un pedido del tablón y lo procesa
- PDF Generator es la máquina que imprime la factura
- Email Sender es el repartidor que envía la factura al cliente
┌─────────────────────────────────────────────────────────────────┐
│ SHOPIFY STORE │
│ (Cliente realiza pedido) │
└────────────────────────────┬────────────────────────────────────┘
│
│ POST /webhook/orders/create
│ (Datos del pedido en JSON)
▼
┌─────────────────────────────────────────────────────────────────┐
│ SERVIDOR UBUNTU │
│ /home/ubuntu/AppMail │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ NGINX (Puerto 443 - HTTPS) │ │
│ │ 👮 "Portero" que recibe las peticiones externas │ │
│ │ y las redirige internamente │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ │ Redirige a puerto 5000 │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ GUNICORN + FLASK (Puerto 5000) │ │
│ │ 📝 "Recepcionista" que recibe el pedido │ │
│ │ y lo valida │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ │ Encola tarea │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ REDIS (Puerto 6379) │ │
│ │ 📋 "Tablón de tareas pendientes" │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ │ Worker toma tarea │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ CELERY WORKER │ │
│ │ 👷 "Operario" que procesa el pedido │ │
│ │ en segundo plano │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ LÓGICA DE NEGOCIO (src/) │ │
│ │ 🏭 Genera PDF y envía email │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
└─────────────────────────┼───────────────────────────────────────┘
│
▼
┌─────────────────┐
│ GMAIL SMTP │
│ 📧 Envía email │
└────────┬────────┘
│
▼
┌─────────────────┐
│ CLIENTE FINAL │
│ ✅ Recibe factura│
└─────────────────┘
- El cliente compra en la tienda Shopify
- Shopify envía un aviso (webhook) a nuestra URL:
https://appmail.bruiser.es/webhook/orders/create - Nginx recibe la petición y la redirige internamente a Flask
- Flask valida que el webhook sea legítimo (firma HMAC de seguridad)
- Flask guarda los datos del pedido en una cola de tareas
- Flask responde "OK" a Shopify inmediatamente
⚠️ Importante: Flask debe responder en menos de 5 segundos, por eso NO genera el PDF aquí. Solo lo apunta en la lista de pendientes.
- Celery Worker detecta que ha llegado el momento de procesar la tarea (se han cumplido las 72 h).
- Worker consulta Shopify usando el ID del pedido para obtener la información más reciente.
- Valida los datos Lee los datos de facturación desde los metafields de Shopify:
- Si existen y están completos, se usan estos datos actualizados.
- Si no existen o están vacíos, se usan los datos originales del webhook guardados al crear la tarea.
- Genera el PDF usando la plantilla HTML y los datos del pedido
- Guarda el PDF en la carpeta
invoices/con nombrefactura_XXXX.pdf - Prepara el email con el asunto, cuerpo y adjunta el PDF
- Envía el email al cliente usando Gmail SMTP
- Registra el resultado en los logs
Sin Celery (Todo en Flask):
Shopify → Flask → [Genera PDF + Envía Email] → Responde a Shopify
⏱️ Puede tardar 20-30 segundos
❌ No se da margen al cliente para corregir datos
❌ Riesgo de timeout y reintentos del webhook
Con Celery (Como está ahora):
Shopify → Flask → [Encola tarea diferida (+72 h)] → Responde "OK" a Shopify
⏱️ Respuesta rápida (solo apuntar pedido)
✅ Shopify recibe confirmación inmediata
72 h después...
Redis → Celery Worker → [Consulta metafields + Genera PDF + Envía Email]
⏱️ Puede tardar lo que haga falta, ya no afecta a Shopify
- ✅ Rapidez en la respuesta: Shopify recibe la confirmación casi al instante.
- ✅ Periodo de corrección de 72 h: el cliente puede actualizar sus datos de facturación antes de emitir la factura.
- ✅Flexibilidad con el plan básico de Shopify: se usan metafields para suplir la falta de campos avanzados en la API estándar.
- ✅ Escalabilidad: se pueden añadir más workers de Celery para procesar muchas facturas en paralelo sin saturar el servidor web.
- ✅ Fiabilidad: si falla la generación del PDF o el envío del email, no se pierde el webhook ni se rompe la experiencia en Shopify.
- ✅ Monitoreo: Los logs de Celery son independientes de Flask
El archivo .env contiene información sensible que NO se ha subido a Git. si lo necesitas esta en el servidor :) Aquí están las principales variables:
SHOPIFY_SHOP_NAME=bruiser-es
SHOPIFY_ACCESS_TOKEN=XXXXXXX
SHOPIFY_API_VERSION=2024-01
# Shopify Configuration
# Webhook Security (lo obtienes después de crear el webhook en Shopify)
WEBHOOK_SECRET=XXXXXXX
# Redis (para Celery)
REDIS_URL=redis://localhost:6379/0
# Gmail SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=XXXXXXX@gmail.com
SMTP_PASSWORD=XXXXXXX
# Rutas de la aplicación
INVOICE_DIR=/home/ubuntu/AppMail/invoices/
LOG_DIR=/home/ubuntu/AppMail/logs/🔒 Nota de seguridad: Para Gmail, debes usar una "contraseña de aplicación", NO tu contraseña normal. Se genera desde la configuración de seguridad de tu cuenta de Google.
Esta documentación continuará con:
- Instalación: Cómo desplegar AppMail desde cero en un servidor nuevo
- Configuración de servicios: Cómo configurar los servicios de systemd
- Guía de uso: Cómo probar que todo funciona correctamente
- Mantenimiento: Cómo ver logs, reiniciar servicios y solucionar problemas comunes
Para completar estas secciones, necesitaremos revisar los archivos de configuración específicos del proyecto.
Si eres nuevo en el proyecto, aquí tienes un glosario de conceptos clave:
- Webhook: Es como un "timbrazo" que Shopify da a nuestra aplicación cuando pasa algo (ej: nuevo pedido)
- API: Interfaz que permite a dos aplicaciones comunicarse (Shopify habla con nuestra app)
- HTTPS: Protocolo seguro de internet (el candado que ves en el navegador)
- SSL/TLS: Certificado digital que hace posible HTTPS
- Asíncrono: Una tarea que se ejecuta "por separado" sin bloquear otras operaciones
- Cola de tareas: Una lista de trabajos pendientes que se procesan uno por uno
- SMTP: Protocolo para enviar emails
- JSON: Formato de datos que usa Shopify para enviarnos información
- PDF: Formato de documento portable (la factura que recibe el cliente)
- Worker: Programa que trabaja en segundo plano procesando tareas
En el servidor se han creado servicios del sistema para que la aplicación se ejecute de forma automática, se pueda reiniciar fácilmente y quede bien integrada con Ubuntu.
Estos servicios se gestionan con systemd, el sistema de servicios de la mayoría de distribuciones Linux.
- Para que AppMail (API Flask) y el worker de Celery se inicien automáticamente al arrancar el servidor.
- Para poder ver su estado, reiniciarlos y ver logs con comandos simples.
- Para tener una forma estándar y segura de ejecutar la aplicación en producción (sin depender de lanzar scripts a mano).
Este servicio se encarga de arrancar la aplicación web (Flask) a través de Gunicorn.
- Escucha peticiones que le llegan desde Nginx (por ejemplo en el puerto interno 5000).
- Atiende los webhooks de Shopify y los endpoints de la API (como
/webhook/orders/create). - Es el “cerebro” que recibe las peticiones y encola las tareas para Celery.
En resumen, appmail = servidor de la API Flask en producción (detrás de Nginx).
Este servicio arranca el worker de Celery que procesa las tareas pendientes.
- Lee las tareas desde Redis.
- Se encarga de generar las facturas PDF y enviar los emails a los clientes.
- Permite que el trabajo pesado se haga en segundo plano, sin bloquear la API.
En resumen, celery-worker = “motor” que hace el trabajo de fondo (facturas y correos).
sudo systemctl status appmail
sudo systemctl status celery-worker
- Muestran si el servicio está activo, parado o si ha fallado.
- También muestran la hora del último inicio y algunos mensajes recientes.sudo systemctl restart appmail
sudo systemctl restart celery-worker
- Útil cuando se despliega una nueva versión del código o se cambian configuraciones.
- Detiene el servicio y lo vuelve a arrancar con la versión actual del código.
sudo systemctl stop appmail
sudo systemctl stop celery-worker
- Detienen por completo la aplicación web o el worker de Celery.
- Útil para mantenimiento, cambios en el servidor o diagnósticos.
sudo systemctl start appmail
sudo systemctl start celery-worker
- Arrancan el servicio si está parado.
- Se usan después de un stop o en un servidor recién configurado.
sudo journalctl -u appmail -f
sudo journalctl -u celery-worker -f
- Muestran los logs que genera cada servicio en directo (-f = follow).
- Muy útil para ver qué ocurre al hacer un pedido de prueba o al reiniciar un servicio.
- Si hay errores de arranque o de ejecución, normalmente aparecen aquí.
El sistema incluye varios scripts Python en la carpeta scripts/ para monitorear y gestionar las facturas , Estos Sripts estan unicanete en el servidor:
> python3 scripts/check_status.py
- Muestra un resumen del sistema: total de facturas, completadas, pendientes y emails enviados. > python3 scripts/check_pending.py
- Lista todas las facturas pendientes con el tiempo restante hasta su procesamiento automático. Indica cuáles están listas para procesar y cuándo será la próxima ejecución. > python3 scripts/check_invoice.py {Id.order ex: *6238* }
- Muestra el detalle completo de una factura: estado, datos del cliente, rutas de archivos, fechas de creación y procesamiento. > python3 scripts/check_services.py
- Comprueba que los servicios Celery (worker y beat) estén activos y funcionando correctamente. > python3 scripts/force_process.py
- Ejecuta manualmente la tarea de procesamiento de facturas que han cumplido las 72 horas, sin esperar a la próxima ejecución automática.Copia y pega esta sección en tu README.md. Está formateada con sintaxis Markdown e incluye ejemplos de uso para cada script.[1]