# Novasis Pay

Pasarela de pagos multi-tenant, multi-provider, white-label.

**Plan de arquitectura completo**: `../smartfactvoice-backend/docs/plan-payments-gateway.md`

## Stack

- NestJS 10 + TypeScript
- Prisma 5 + PostgreSQL 16
- BullMQ + Redis 7
- pnpm
- Vitest

## Requisitos previos

- **Node.js** >= 20
- **pnpm** >= 10
- **Docker** + Docker Compose (para Postgres y Redis)

## Variables de entorno obligatorias

Se copian desde `.env.example`. Estas tres **no traen valor por defecto** y hay
que generarlas o la app no arranca correctamente:

| Variable | Para qué sirve | Cómo generarla |
| --- | --- | --- |
| `MASTER_ENCRYPTION_KEY` | Clave maestra AES-256-GCM (cifra credenciales de providers). 32 bytes en base64. | `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"` |
| `ADMIN_JWT_SECRET` | Firma los JWT del login de usuarios admin. Mínimo 32 chars. | `node -e "console.log(require('crypto').randomBytes(48).toString('base64'))"` |
| `ADMIN_API_TOKEN` | Token del `AdminGuard` (crear merchants) y bootstrap del primer superadmin. | `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"` |

`DATABASE_URL`, `REDIS_*`, `PORT`, `CHECKOUT_BASE_URL`, `API_BASE_URL`,
`DLOCAL_GO_BASE_URL` y `PUBLIC_CORS_ORIGINS` ya vienen con valores válidos para
dev en `.env.example`; ajustar solo si tu entorno difiere.

## Setup local

```bash
# 1. Levantar infra (postgres + redis)
docker compose up -d

# 2. Variables de entorno
cp .env.example .env
# Editar .env y completar MASTER_ENCRYPTION_KEY, ADMIN_JWT_SECRET y ADMIN_API_TOKEN
# (ver comandos de generación en la tabla de arriba)

# 3. Dependencias
pnpm install

# 4. Prisma client
pnpm prisma:generate

# 5. Migraciones — OBLIGATORIO. Crea las 17 tablas (merchant, admin_user, etc.).
#    Sin este paso la BD está vacía y el login/API fallan (ver Troubleshooting).
#    En una BD nueva podés usar deploy en vez de dev:
pnpm prisma:migrate:dev        # (o: pnpm prisma:migrate:deploy)

# 6. Crear el primer usuario superadmin — OBLIGATORIO para poder loguearte.
#    Ajustá email/password/name a gusto (password mínimo 8 chars).
pnpm seed:admin --email admin@novasis.com --password "TuPassword123" --name "Admin"

# 7. Arrancar
pnpm start:dev
```

> **Importante:** los pasos **5 (migraciones)** y **6 (seed admin)** son
> obligatorios en cada instalación nueva. Levantar solo `docker compose` crea la
> base pero **vacía** (sin tablas ni usuarios); el gateway no funcionará hasta
> aplicar las migraciones y sembrar el primer admin.

Servidor en `http://localhost:3010` (puerto en `.env`).
- Healthcheck: `GET /health`
- **Referencia de la API (Swagger)**: `http://localhost:3010/docs` (JSON en `/docs-json`; spec versionada en `docs/openapi/openapi.json`). Fuente de verdad de cada endpoint y sus campos.
- **Guía de integración** (cómo integrarse de punta a punta: auth, modalidades de cobro, webhooks con firma HMAC, flujos `curl`): [`docs/api-gateway-integracion.md`](docs/api-gateway-integracion.md).

Postgres expuesto en `localhost:5435`, Redis en `localhost:6381` (puertos elegidos para no chocar con otras instancias del dev host).

## Troubleshooting

**`The table 'public.admin_user' does not exist` (o cualquier otra tabla) al loguearte / usar la API.**
La base está vacía porque no se aplicaron las migraciones. Verificá y corregí:

```bash
pnpm prisma migrate status      # muestra las migraciones pendientes
pnpm prisma:migrate:deploy      # aplica todas las pendientes
```

Luego, si aún no podés loguearte (`credenciales inválidas`), falta el usuario admin:

```bash
pnpm seed:admin --email admin@novasis.com --password "TuPassword123" --name "Admin"
```

**`ADMIN_JWT_SECRET no configurado` al hacer login.** Completá `ADMIN_JWT_SECRET`
en `.env` (mínimo 32 chars) y reiniciá el server. Ver tabla de variables obligatorias.

**`MASTER_ENCRYPTION_KEY debe ser 32 bytes en base64`.** El valor no es una clave
de 32 bytes en base64. Regenerala con el comando de la tabla de variables y reiniciá.

## Estructura

```
src/
├── main.ts                 # Bootstrap
├── app.module.ts           # Root module
├── health/                 # Healthcheck (Fase 0)
├── prisma/                 # PrismaService global
├── common/                 # Utils transversales (encryption, hmac, ids)
├── merchants/              # Multi-tenancy (Fase 1)
├── customers/              # Clientes finales (Fase 1)
├── intents/                # payment_intent (Fase 1)
├── attempts/               # payment_attempt (Fase 1)
├── refunds/                # payment_refund (Fase 1)
├── checkout-sessions/      # Sesiones de hosted page / drop-in (Fase 1)
├── webhooks-in/            # Eventos entrantes de providers (Fase 2)
├── webhooks-out/           # Notificaciones salientes a merchants (Fase 3)
├── providers/
│   ├── dlocal/             # Adapter dLocal Go (Fase 2)
│   └── bancard/            # Adapter Bancard (Fase futura)
└── billing/                # Pricing plans + facturación al merchant (Fase 8)
```

## Roadmap

Ver `docs/plan-payments-gateway.md` en el repo del ERP. Estado actual: **MVP** (merchants, API keys, payment intents, checkout sessions, auth admin y billing base implementados).

---

# 🚀 Despliegue a producción (sin Docker · Node + PM2 + Apache2)

Guía completa **desde cero** para un servidor Linux con Node/PM2 y Apache2 (sin Docker).
Los tres componentes de Novasis Pay se despliegan en subdominios distintos:

| Componente | Repo | Dominio | Cómo se sirve |
|---|---|---|---|
| **Gateway (API)** | `novasis-pay` | `novasispay-gateway.novasispy.com` | Node (PM2) detrás de Apache (reverse proxy) |
| **Checkout** (SPA) | `novasis-pay-checkout` | `novasispay-checkout.novasispy.com` | Estático (Apache) — ver su README |
| **Panel admin** (SPA) | `novasis-pay-admin` | `novasispay-panel.novasispy.com` | Estático (Apache) — ver su README |

```
Navegador ──HTTPS──> Apache (vhost por dominio)
   · gateway  → ProxyPass a 127.0.0.1:3010   (este repo, PM2)
   · checkout → archivos estáticos dist/       (SPA)
   · panel    → archivos estáticos dist/       (SPA)
Gateway ──> PostgreSQL (novasispay) + Redis (local)
```

> Este README cubre el **gateway**. El checkout y el panel tienen su propia sección de
> despliegue en sus READMEs respectivos.

## 1. Requisitos del servidor

```bash
# Node 20 LTS (vía nvm o nodesource) + pnpm + PM2
node -v            # >= 20
corepack enable && corepack prepare pnpm@latest --activate
npm i -g pm2

# PostgreSQL 15+ y Redis instalados y corriendo como servicios del sistema
sudo systemctl enable --now postgresql redis-server

# Apache2 con los módulos necesarios para reverse proxy + SSL + SPA
sudo a2enmod proxy proxy_http headers rewrite ssl
sudo systemctl restart apache2

# Certbot para TLS (Let's Encrypt)
sudo apt install -y certbot python3-certbot-apache
```

### Redis dedicado (puerto 6381)

Novasis Pay es una pasarela de pagos: usa Redis para colas (webhooks, billing) y conviene
**aislarlo** de otros servicios. Si el server **ya tiene un Redis** (6379) para otras apps, corré
una **instancia dedicada en 6381** para el gateway (deja intacto el 6379 de los demás):

Cada bloque es **un comando de una línea** (copiar/pegar entero, sin editores ni heredocs):

```bash
# Directorio de datos
sudo mkdir -p /var/lib/redis-novasispay && sudo chown redis:redis /var/lib/redis-novasispay

# Config dedicada: copia la base y le agrega los overrides
sudo cp /etc/redis/redis.conf /etc/redis/redis-novasispay.conf
printf '\nport 6381\npidfile /run/redis/redis-novasispay.pid\ndbfilename dump-novasispay.rdb\ndir /var/lib/redis-novasispay\ndaemonize no\n' | sudo tee -a /etc/redis/redis-novasispay.conf
# `daemonize no` es CLAVE: el redis.conf base trae `daemonize yes`, que con systemd hace
# que el proceso se caiga en loop (start-limit-hit). En foreground systemd lo gestiona bien.

# Servicio systemd dedicado
printf '[Unit]\nDescription=Redis novasispay 6381\nAfter=network.target\n\n[Service]\nExecStart=/usr/bin/redis-server /etc/redis/redis-novasispay.conf\nRestart=always\nUser=redis\nGroup=redis\n\n[Install]\nWantedBy=multi-user.target\n' | sudo tee /etc/systemd/system/redis-novasispay.service

# Arrancar y verificar
sudo systemctl daemon-reload
sudo systemctl enable --now redis-novasispay
redis-cli -p 6381 ping            # PONG
```

Luego el gateway usa `REDIS_PORT=6381` (ver `.env`). Si el server **no tiene** otros servicios
con Redis, podés usar la instancia estándar en `6379` y poner `REDIS_PORT=6379`.

## 2. Base de datos

```bash
sudo -u postgres psql <<'SQL'
CREATE USER novasispay WITH PASSWORD 'CAMBIA_ESTA_PASSWORD';
CREATE DATABASE novasispay OWNER novasispay;
SQL
```

`DATABASE_URL` → `postgresql://novasispay:CAMBIA_ESTA_PASSWORD@localhost:5432/novasispay?schema=public`

## 3. Código, dependencias y build

```bash
cd /var/www/html/novasispay/novasis-pay         # ubicación en el servidor (ajustar)
git clone <repo> .
pnpm install --frozen-lockfile
pnpm prisma generate
pnpm build                             # nest build → dist/main.js
```

## 4. Variables de entorno (`.env` en la raíz del gateway)

La app carga `.env` de su directorio. Generá secretos fuertes:
- `MASTER_ENCRYPTION_KEY` → **32 bytes en base64**: `openssl rand -base64 32` (da ~44 caracteres).
  Cifra las credenciales de los providers (AES-256-GCM). **No la rotes sin plan de migración** o
  no se podrán descifrar las credenciales guardadas.
- `ADMIN_JWT_SECRET` / `ADMIN_API_TOKEN` → `openssl rand -hex 32`.

```ini
NODE_ENV=production
PORT=3010
LOG_LEVEL=info

# --- Base de datos y Redis ---
DATABASE_URL=postgresql://novasispay:CAMBIA_ESTA_PASSWORD@localhost:5432/novasispay?schema=public
REDIS_HOST=127.0.0.1
REDIS_PORT=6381                      # instancia Redis DEDICADA de novasispay (ver "Redis dedicado"). Usar 6379 si es la única app con Redis.
REDIS_PASSWORD=

# --- Secretos ---
MASTER_ENCRYPTION_KEY=<32 bytes base64>   # openssl rand -base64 32 — cifra credenciales de providers
ADMIN_JWT_SECRET=<hex>                     # openssl rand -hex 32
ADMIN_API_TOKEN=<token largo>             # openssl rand -hex 32 — protege endpoints admin server-to-server

# --- URLs públicas (deben coincidir con los dominios reales) ---
API_BASE_URL=https://novasispay-gateway.novasispy.com
CHECKOUT_BASE_URL=https://novasispay-checkout.novasispy.com
# Orígenes de los SPA que llaman la API desde el navegador (CORS). Separados por coma:
PUBLIC_CORS_ORIGINS=https://novasispay-checkout.novasispy.com,https://novasispay-panel.novasispy.com

# --- Providers ---
DPAGO_API_BASE_URL=https://api.dpago.com
DLOCAL_GO_BASE_URL=https://api.dlocalgo.com

# --- Checkout / billing ---
CHECKOUT_SESSION_DEFAULT_EXPIRATION_HOURS=24
BILLING_CRON_DISABLED=false
SELF_BILLING_MERCHANT_ID=
```

> `API_BASE_URL` se usa para construir las URLs de webhook que se dan a los providers.
> `CHECKOUT_BASE_URL` arma el link del checkout hospedado (`<CHECKOUT_BASE_URL>/c/<token>`).
> `PUBLIC_CORS_ORIGINS` **debe** incluir los dominios del checkout y del panel, o el navegador
> bloqueará las llamadas. Con `NODE_ENV=production` Swagger (`/docs`) queda **deshabilitado**.

## 5. Migraciones y seeds (OBLIGATORIO)

```bash
pnpm prisma migrate deploy            # crea/actualiza todas las tablas
pnpm seed:admin                       # primer usuario admin (ver prompts/env del script)
pnpm seed:dpago-platforms             # catálogo de medios de pago de Dpago (platformIds)
```

> Los seeds usan `ts-node` (dependencia de dev). Si instalaste solo prod, corré los seeds una
> vez con las devDependencies presentes, o con `pnpm dlx ts-node ...`.

## 6. PM2

Crear `ecosystem.config.cjs` en la raíz del gateway:

```js
module.exports = {
  apps: [{
    name: "novasispay-gateway",
    script: "dist/main.js",
    cwd: "/var/www/html/novasispay/novasis-pay",   // así carga el .env de este directorio
    instances: 1,                          // subir si escalás (requiere Redis compartido, ya lo usa)
    exec_mode: "fork",
    env: { NODE_ENV: "production" },
    max_memory_restart: "500M",
  }],
};
```

```bash
pm2 start ecosystem.config.cjs
pm2 save                                # persiste la lista de procesos
pm2 startup                            # genera el servicio systemd (seguí la instrucción que imprime)
pm2 logs novasispay-gateway           # ver logs
curl -s http://127.0.0.1:3010/health  # debe responder OK
```

## 7. Apache — vhost del gateway (reverse proxy)

`/etc/apache2/sites-available/novasispay-gateway.conf`:

```apache
<VirtualHost *:80>
    ServerName novasispay-gateway.novasispy.com
    # Certbot agregará el redirect a HTTPS automáticamente.
</VirtualHost>

<VirtualHost *:443>
    ServerName novasispay-gateway.novasispy.com

    ProxyPreserveHost On
    ProxyRequests Off
    # /docs queda deshabilitado en prod; se proxean todas las rutas de la API.
    ProxyPass        / http://127.0.0.1:3010/
    ProxyPassReverse / http://127.0.0.1:3010/

    # Reenviar el esquema para que la app arme URLs https correctas.
    RequestHeader set X-Forwarded-Proto "https"

    ErrorLog  ${APACHE_LOG_DIR}/novasispay-gateway-error.log
    CustomLog ${APACHE_LOG_DIR}/novasispay-gateway-access.log combined

    # SSLEngine / certificados los completa certbot (ver paso 8).
</VirtualHost>
```

```bash
sudo a2ensite novasispay-gateway
sudo apache2ctl configtest && sudo systemctl reload apache2
```

## 8. TLS (Let's Encrypt)

```bash
sudo certbot --apache -d novasispay-gateway.novasispy.com
sudo certbot renew --dry-run          # verificar renovación automática
```

## 9. Verificación

```bash
curl -s https://novasispay-gateway.novasispy.com/health
curl -s https://novasispay-gateway.novasispy.com/v1/providers | head
```

## 10. Actualizaciones (deploy de una nueva versión)

```bash
cd /var/www/html/novasispay/novasis-pay
git pull
pnpm install --frozen-lockfile
pnpm prisma generate
pnpm prisma migrate deploy            # si hay migraciones nuevas
pnpm build
pm2 reload novasispay-gateway         # reinicio sin downtime
```

## Checklist final

- [ ] Postgres `novasispay` creada; `DATABASE_URL` correcta.
- [ ] Redis corriendo en el puerto de `REDIS_PORT` (dedicado 6381, o 6379 si es la única app). `redis-cli -p <puerto> ping` → PONG.
- [ ] `.env` con secretos generados (`MASTER_ENCRYPTION_KEY` 64 hex) y URLs de los 3 dominios.
- [ ] `PUBLIC_CORS_ORIGINS` incluye checkout y panel.
- [ ] `migrate deploy` + `seed:admin` + `seed:dpago-platforms` corridos.
- [ ] PM2 con `pm2 save` + `pm2 startup` (arranca al bootear).
- [ ] Apache vhost + certbot (HTTPS) OK.
- [ ] `GET /health` y `GET /v1/providers` responden por HTTPS.
