# Backend

API Express (ESM) que además **sirve el frontend compilado** (`../dist`), así con solo
levantar el backend se ve toda la web en `http://localhost:3002`. Usa `fetch` nativo
para hablar con Garena (sin Playwright en runtime).

```
backend/
  server.js          cors + json + sirve dist + routers API + fallback SPA + listen
  config.js          PORT, archivos de estado, GARENA{...}, COOKIE_DIR, USER_AGENT
  db.js              "DB" JSON local: readDB / writeDB sobre users.json + orders.json (+ init)
  cookie/            Pool de cookies de Garena (cookie-1.json ...)
  services/
    garena.js        fetchPlayerInfo(id): login + channels DO + paquetes con descuento (rotación automática)
    cookieManager.js gestor del pool de cookies en memoria y rotación transparente
    users.js         getOrRegisterUser(id): siempre consulta Garena y actualiza user con discountedPackages derivado
  routes/
    playerInfo.js    GET  /api/player_info?id=...
    users.js         GET  /api/users/:id
    orders.js        POST/GET /api/orders, PATCH /api/orders/:id/(pay|complete), DELETE /api/orders/:id
    packages.js      GET  /api/packages   -> lee config.json en cada request
  config.json        Catálogo editable: paquetes, diamantes, aliases, precios, descuento y márgenes
  users.json         Usuarios cacheados: { [id]: { nickname, packages, discountedPackages, ... } }
  orders.json        Órdenes: [ ... ]
```

## Claves

- **Descuento (región DO):** hay descuento solo si `currency_amount !== original_currency_amount`.
  Requiere conservar el `session_key` del login (`headers.getSetCookie()`); sin él, Garena
  devuelve precios genéricos sin descuento. `promo` NO es descuento.
- **Catálogo editable:** en `prices.json`. Ahí viven paquetes, diamantes, aliases legacy, `usd`, descuentos, ganancia normal y fees PayPal.
- **Precios:** `usd` es costo/base; bonus = `usd * (1 - discountPercent / 100)`, normal = `usd * (1 + profitPercent / 100)`, PayPal = gross-up sobre el neto elegido.

## Cookie de Garena (`full-cookies.json`)

El backend NO hace login con navegador en runtime: reutiliza la cookie completa
guardada en `full-cookies.json`. Esa cookie se obtiene/regenera con el script
**`test/save-full-cookies.js`**, que abre Garena con Playwright, hace el login
(resolviendo el CAPTCHA a mano si aparece) y guarda todas las cookies de
`pagostore.garena.com` en `backend/full-cookies.json`.

```bash
# Generar / regenerar la cookie (desde la raíz del proyecto)
FF_ID=9922862973 node test/save-full-cookies.js
```

- Duración: dura varias horas (≈10h+ observado). Cuando `/api/player_info` empiece
  a devolver `503 datadome` o no traiga descuentos, regenerarla con el comando de arriba.
- Solo `test/save-full-cookies.js` la genera; ningún otro archivo de `test/` la crea.

## Comandos (desde la raíz del proyecto)

- `node backend/server.js`  Levanta API + frontend en `http://localhost:3002`
- `npm run dev`             Backend (3002) + frontend Vite (5173) en modo desarrollo
- `FF_ID=<id> node test/save-full-cookies.js`  Regenera `full-cookies.json` si caduca
