# 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.
