Seguridad (crítico): - Los webhooks de Bold y dLocal solo validaban la firma si el atacante la enviaba: sin cabecera se aceptaba cualquier payload. Ahora es obligatoria. - GET /pago-exitoso marcaba contratos como pagados leyendo un query param del navegador. Ahora solo muestra estado; la confirmación la hace la verificación contra la API de la pasarela o el webhook firmado. - /uploads se servía como estático público: se descargaban RUTs, facturas y entregables sabiendo la ruta. Ahora exige sesión. - Los secretos JWT no se podían sobreescribir por entorno (faltaba el tag env:) y su valor estaba en el repo, permitiendo firmarse una sesión de admin. Ahora son configurables y el arranque se detiene si siguen con el valor publicado. - .env y session.db salen del control de versiones. - Query Runner, gestión de usuarios/roles/módulos y seeds quedan restringidos a administradores; antes bastaba con tener sesión. Pasarelas de pago: - dLocal generaba enlaces que nunca se reconciliaban: mandaba el ID numérico en vez de "contrato-N", la URL de retorno apuntaba a la API de dLocal y nunca se enviaba notification_url, así que su webhook jamás se disparaba. - PayPal solo tenía pantalla de configuración. Se implementa el servicio completo (OAuth, orden, captura, verificación de webhook) y queda seleccionable como pasarela. - La moneda estaba fija en COP: un contrato en USD generaba un cobro por esa cifra en pesos. Contratos: - pago_confirmado nunca volvía a false, así que el segundo ciclo de renovación no se cobraba aunque el cliente pagara. Se reinicia al generar enlace nuevo. - Los contratos vencidos nunca cambiaban de estado y recibían correo a diario de forma indefinida; ahora se cierran tras 30 días de gracia. Otros: - Coolify: coolifyCall ignoraba el status HTTP y reportaba errores como éxito. El agente pasa de 10 a cobertura completa (servicios, bases de datos, variables de entorno, proyectos, equipos y recursos de servidor). - SeedBalanceData ya no corre en cada arranque (recreaba transacciones borradas); ahora se invoca con SEED_BALANCE=1. - Los seeds dejan de devolver permisos revocados en cada despliegue. - Timeouts en las llamadas HTTP a Telegram y dLocal que podían colgarse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.3 KiB
Markdown
82 lines
3.3 KiB
Markdown
# Variables de entorno requeridas
|
|
|
|
> **Leer antes del siguiente despliegue.** El arranque falla a propósito si los
|
|
> secretos JWT siguen teniendo el valor que estuvo publicado en el repositorio.
|
|
|
|
## 1. Secretos que hay que definir sí o sí
|
|
|
|
`.env` y `config.yml` estuvieron versionados con credenciales reales, así que
|
|
**todo lo que aparecía ahí debe considerarse comprometido y rotarse**, no solo
|
|
sacarse del repositorio: el historial de git lo sigue conteniendo.
|
|
|
|
Genera cada valor con `openssl rand -hex 32` y cárgalos como variables de
|
|
entorno del contenedor (en Coolify: *Environment Variables*):
|
|
|
|
| Variable | Para qué sirve | Si no se define |
|
|
|---|---|---|
|
|
| `APP_JWT_SECRET` | Firma las cookies de sesión del panel | **El arranque se detiene** |
|
|
| `API_JWT_SECRET` | Firma los tokens de la API v1 | **El arranque se detiene** |
|
|
| `APP_KEY` | Cifra las contraseñas de SMTP e integraciones | Arranca, pero avisa en los logs |
|
|
| `ADMIN_API_KEY` | Única llave de toda la API `/api/v2` | La API v2 responde 503 |
|
|
|
|
Al rotar `APP_JWT_SECRET` se cierran las sesiones abiertas: hay que volver a
|
|
iniciar sesión, nada más.
|
|
|
|
### Cuidado con `APP_KEY`
|
|
|
|
`APP_KEY` cifra las contraseñas guardadas en la base de datos. Si la cambias,
|
|
**las contraseñas cifradas con la clave anterior dejan de poder descifrarse** y
|
|
hay que volver a guardarlas desde el panel:
|
|
|
|
- Configuración SMTP (`/app/smtp-config`)
|
|
- Credenciales de las pasarelas de pago (`/app/pasarelas`)
|
|
- Cualquier otra integración con contraseña
|
|
|
|
Por eso el sistema solo advierte en vez de detenerse: para que elijas el momento.
|
|
|
|
## 2. Otras credenciales a rotar
|
|
|
|
Estaban en el `.env` versionado:
|
|
|
|
- Contraseña de PostgreSQL
|
|
- Contraseña del correo saliente
|
|
- Cualquier token de integración (Coolify, Cloudflare, Hostinger, Telegram…)
|
|
|
|
## 3. Webhooks de pago
|
|
|
|
Las notificaciones ahora **exigen firma válida**. Verifica en cada proveedor que
|
|
el secreto de firma coincida con el configurado en `/app/pasarelas`:
|
|
|
|
| Pasarela | URL del webhook | Requiere |
|
|
|---|---|---|
|
|
| Bold | `https://TU-DOMINIO/webhooks/bold` | Secret de firma en la config |
|
|
| dLocal | `https://TU-DOMINIO/webhooks/dlocal` | Secret de firma en la config |
|
|
| PayPal | `https://TU-DOMINIO/webhooks/paypal` | **Webhook ID** en la config |
|
|
|
|
Para PayPal hay que crear el webhook en el panel de PayPal suscrito a los
|
|
eventos `CHECKOUT.ORDER.APPROVED` y `PAYMENT.CAPTURE.COMPLETED`, y pegar el
|
|
Webhook ID que devuelve en `/app/pasarelas`. Sin ese ID no se pueden verificar
|
|
las notificaciones y se rechazan.
|
|
|
|
> PayPal no opera en pesos colombianos. Los contratos que se cobren por PayPal
|
|
> deben tener la moneda en USD (u otra soportada), o PayPal rechazará la orden.
|
|
|
|
## 4. Datos contables de ejemplo
|
|
|
|
`SeedBalanceData` (27 transacciones de 2026 escritas en el código) ya **no**
|
|
corre en cada arranque: si el contador editaba o borraba una, el siguiente
|
|
reinicio la recreaba y el balance quedaba duplicado.
|
|
|
|
Para cargarla puntualmente:
|
|
|
|
```bash
|
|
SEED_BALANCE=1 ./apiv2 -config config.yml
|
|
```
|
|
|
|
## 5. Archivos subidos
|
|
|
|
`/uploads` ya no es público. Antes, sabiendo la ruta se podía descargar el RUT de
|
|
un cliente o una factura sin iniciar sesión. Ahora requiere sesión en el panel;
|
|
los clientes del portal siguen descargando sus documentos por los endpoints de
|
|
siempre, que además validan que el archivo les pertenezca.
|