# INDEX_public.md — public.php
> **Archivo:** `public.php` | 2978 líneas | Portal web administrativo + endpoint público HTTP

---

## Propósito
Archivo dual:
1. **Endpoint REST/webhook** — recibe webhooks de Stripe, solicitudes OXXO, y llamadas AJAX del portal
2. **SPA PHP** — interfaz web administrativa con 4 módulos (Instaladores, Notificaciones, Stripe, OXXO)

---

## Variables globales inicializadas (L.1–65)
| Variable | Descripción |
|---|---|
| `$config` | Config del plugin cargada de `data/config.json` |
| `$ucrmApi` | `UcrmApi` (SDK) con base_uri UCRM y apitoken |
| `$paymentIntentService` | `PaymentIntentService` con stripeApiKey |
| `$admins` | Array de admins UCRM `[id, nombre]` |
| `$defaultStripeAdminId` | ID del admin "stripe" o primero de la lista |
| `$nmsBaseUrl` | `https://{ipserver}/nms/api/v2.1` |
| `$installersData` | JSON de instaladores desde config |

---

## Mapa de Endpoints / Actions

### POST con JSON body (L.22–35) — Webhook delegado a Plugin::run()
| Condición | Acción |
|---|---|
| `isset($jsonData['uuid']) || isset($jsonData['eventName']) || isset($jsonData['type'])` | Instancia `Plugin` via DI container y llama `run()` |

---

### POST `?action=nms_login` (L.80–117) — Login NMS
- **Body:** `{username, password}`
- **Respuesta 200:** `{success, token, user}`
- **Respuesta 201:** `{requires2FA, twoFactorToken}` → sigue con `nms_login_totp`
- **Guarda token en:** `sessionStorage('nms_auth_token')` (frontend)

### POST `?action=nms_login_totp` (L.120–146) — Login TOTP 2FA
- **Body:** `{twoFactorToken, ..., totpCode}`
- **Respuesta 200:** `{success, token, user}`

### GET `?action=nms_verify_session` (L.148–173) — Verificar sesión NMS
- **Header:** `x-auth-token`
- **Llama:** `GET {nmsBaseUrl}/user`

---

### POST `$_POST['action']` (L.176–291)

| action | Línea | Params | Descripción |
|---|---|---|---|
| `save_installers` | 177 | `installers_data` (JSON) | Guarda JSON en config.json |
| `resend_payment` | 191 | `paymentId` | Obtiene pago+cliente UCRM, hace loopback POST a sí mismo simulando webhook |
| `resend_job_notification` | 238 | `jobId` | Simula webhook `job.edit` con status 0→1 y prefijo `[NOTIFICACION-PENDIENTE]` |
| `create_intent` | 443 | `clientId, amount, stripeCustomerId, adminId` | `PaymentIntentService->createPaymentIntent()` |
| `create_oxxo_intent` | 463 | `clientId, amount` | `PluginOxxoNotifierFacade->createOxxoPaymentIntent()` via DI |

---

### GET `?action=...` (L.293–438)

| action | Línea | Params GET | Descripción |
|---|---|---|---|
| `search_clients` | 296 | `q` | UCRM `GET clients?query=q&limit=6` |
| `get_payments` | 307 | `clientId` | UCRM `GET payments?clientId=...&limit=20` + names de métodos |
| `search_stripe` | 323 | `q` | `PaymentIntentService->searchClients(q)` |
| `get_stripe_details` | 329 | `id` (clientId UCRM) | `PaymentIntentService->getClientDetails(id)` |
| `get_stripe_history` | 339 | `stripeCustomerId` | Balance cash + últimos 10 PIs de transferencia |
| `get_oxxo_history` | 355 | `stripeCustomerId` | Últimos 5 PIs OXXO de Stripe |
| `get_installer_jobs` | 365 | `installerId` | UCRM `GET scheduling/jobs?assignedUserId=...&statuses=[0,1]&limit=50` |
| `image` / `get_image` | 413 | `file` o `name` | Sirve imágenes de `/img/`, `/img/webp/`, `/vouchers_oxxo/` |

---

## HTML/UI — Estructura (L.481–2064)

### Variables PHP→JS inyectadas (L.2066–2075)
```javascript
NEEDS_LOGIN = true/false       // Si sesión UCRM activa
SYSTEM_USER_ID = null|int      // ID usuario actual
store.installers = [...]        // Array de instaladores desde config
store.crmUrl = 'https://...'   // URL base API UCRM
store.publicUrl = 'https://...'// URL pública CRM
store.defaultStripeAdminId = '' // ID admin Stripe por defecto
```

### Módulos de la UI (tabs)
| Tab ID | Sección HTML | Descripción |
|---|---|---|
| `instaladores` | `#section-instaladores` | Tabla instaladores + jobs activos del instalador |
| `notificaciones` | `#section-notificaciones` | Búsqueda cliente + tabla pagos + botón reenviar |
| `pagos-spei` | `#section-pagos-spei` (en `views/stripe.php`) | Generador Stripe + historial |
| `pagos-oxxo` | `#section-pagos-oxxo` (en `views/oxxo.php`) | Generador OXXO + historial |

### Funciones JS principales (L.2065+)
| Función | Descripción |
|---|---|
| `toggleTheme()` | Cambia light/dark en `data-theme` + localStorage |
| `showDashboard()` | Muestra menú principal, oculta módulos |
| `showModule(id)` | Activa módulo por ID (mapea 'stripe'→'pagos-spei', etc.) |
| `switchTab(tabName)` | Cambia tab activa y sección visible |
| `showToast(msg, err)` | Toast temporal 3s |
| `setupSearch(inputId, resultId, action, onSelect)` | Search debounced 300ms → fetch `?action=...` |
| `renderRichSearchResults(container, data, query, cb)` | Renderiza resultados de búsqueda con highlight |
| `highlightMatch(text, query)` | Resalta coincidencias en texto |
| `loadPayments(id)` | Fetch `?action=get_payments&clientId=id` → tabla |
| `refreshClientPayments()` | Recarga pagos del cliente actual |
| `resendPayment(id)` | POST `action=resend_payment&paymentId=id` |
| `loadOxxoHistory(stripeCustomerId)` | Fetch `?action=get_oxxo_history` → tabla |
| `handleLogin()` | POST `?action=nms_login` → guarda token en sessionStorage |
| `handleTotpLogin()` | POST `?action=nms_login_totp` |
| `handleLogout()` | Limpia sessionStorage + reload |

### Funciones JS Instaladores
| Función | Descripción |
|---|---|
| `openInstallerModal()` | Abre modal de nuevo instalador |
| `fillInstallerData(select)` | Rellena campos desde select de admin |
| `loadInstallerJobs(installerId)` | Fetch `?action=get_installer_jobs&installerId=...` |
| `resendJobNotification(jobId)` | POST `action=resend_job_notification` |
| `renderInstallersTable()` | Renderiza tabla desde `store.installers` |

---

## Includes externos (L.1992–1993)
```php
include __DIR__ . '/views/stripe.php';  // Sección y JS del módulo Stripe
include __DIR__ . '/views/oxxo.php';    // Sección y JS del módulo OXXO
```

---

## Archivos que sirve el endpoint `image`
- `/img/*.{png,webp,jpg}`
- `/img/webp/*.{webp}`
- `/vouchers_oxxo/*.{png,webp,jpg}`

## Login overlay (L.1711–1750)
- Se muestra si `NEEDS_LOGIN = true` (usuario sin sesión UCRM activa)
- Pide credenciales NMS (UISP), no UCRM
- Soporta 2FA TOTP
- Token guardado en `sessionStorage('nms_auth_token')`

---

## Notas de implementación
- `resend_payment` hace un **loopback cURL** a sí mismo simulando webhook UCRM
- `create_intent` usa `PaymentIntentService` que llama a Stripe SDK directo
- El CSS (~1200 líneas) está inline en el archivo, soporta dark/light mode vía `data-theme`
- Las vistas `stripe.php` y `oxxo.php` tienen su propio JS, leer esos archivos para modificar esos módulos
