# App de Validación ARCA — Documentación del Sistema

Preparado por UO Solutions

## 1. Propósito deel documento

Este documento explica, en términos generales y técnicos, cómo funciona la aplicación de Validación ARCA dentro de HubSpot.

## 2. Qué hace esta aplicación

En términos generales: un integrante del equipo, trabajando dentro de HubSpot, ingresa el CUIT/CUIL de una persona. La aplicación verifica ese número contra ARCA y contra la base de contactos de HubSpot, y luego ayuda a confirmar el contacto existente o a crear uno nuevo — todo sin salir de HubSpot.

Toda la experiencia vive en una sola pantalla embebida dentro del CRM de HubSpot. No se instala nada en la computadora, y no hay un sitio web ni un login separado.

## 3. Cómo está construido el sistema

La aplicación es una **app privada de HubSpot**. "Privada" significa que fue construida para esta cuenta de HubSpot, y solo existe dentro de ella — no está publicada ni compartida fuera de la organización.

Tiene tres partes, todas alojadas dentro de la infraestructura propia de HubSpot:

1. **La Página (lo que ve el usuario).** Una pantalla embebida directamente en la interfaz del CRM de HubSpot. Solo es accesible abriendo HubSpot y navegando hacia ella — no existe una dirección web pública separada.
2. **Funciones Serverless (la lógica).** Piezas de código backend que hacen el trabajo real: validar un CUIT, buscar un contacto, crear un contacto, actualizar un contacto. Están configuradas como **funciones privadas**, una configuración técnica específica que significa que solo pueden ser invocadas desde dentro de esta misma sesión de la app en HubSpot — no tienen una dirección pública de internet.
3. **La API de CRM de HubSpot.** La interfaz oficial que HubSpot ofrece para leer y escribir registros de contactos. Así es como la app efectivamente crea o actualiza un contacto — pidiéndole a HubSpot que lo haga, usando credenciales limitadas a esta cuenta.

Todo lo descrito arriba — la pantalla, la lógica y la base de contactos — pertenece a HubSpot y corre dentro de HubSpot. El único paso que sale de HubSpot es la verificación del CUIT contra ARCA, descripta en la Sección 5.

## 4. Arquitectura general

```mermaid
flowchart TB
    subgraph HS["Dentro de la cuenta de HubSpot"]
        User["Integrante del equipo,\nlogueado en HubSpot"]
        Page["Página de la App\n(la pantalla dentro del CRM)"]
        Funcs["Funciones Serverless Privadas\n(validar CUIT / buscar / crear / actualizar)"]
        CRM["API de CRM de HubSpot\n(registros de contactos)"]

        User -->|"abre la página dentro de HubSpot"| Page
        Page -->|"llama, usando la sesión\ninterna de la app de HubSpot"| Funcs
        Funcs -->|"lee y escribe contactos"| CRM
    end

    ARCA["Servicio de validación ARCA\n(externo, ARCA)"]
    Funcs -->|"una verificación saliente:\n¿es válido este CUIT?"| ARCA

    style HS fill:#eef4ff,stroke:#5577cc,stroke-width:1px
```

Todo lo que está dentro del recuadro sombreado ocurre en la infraestructura propia de HubSpot. La única flecha que sale del recuadro es la verificación de CUIT, descripta a continuación.

## 5. Cómo viaja la información

Esto es el núcleo de lo que hace seguro al diseño: los datos de contacto se crean, leen y actualizan enteramente a través de los sistemas propios de HubSpot. No se copian ni se procesan en ningún servidor operado por UO Solutions ni por nadie más.

Paso a paso, para un uso típico:

1. Un integrante del equipo, ya logueado en HubSpot, abre la página de la app desde dentro del CRM.
2. Ingresa un CUIT/CUIL. La página le pide a una función privada que lo verifique.
3. Esa función hace una solicitud saliente al servicio de validación de ARCA para confirmar los datos de identidad asociados a ese CUIT (nombre, datos de contacto registrados). Este es el único punto donde una solicitud sale del entorno de HubSpot, y solo envía el número de CUIT que se está consultando.
4. En paralelo, otra función privada le pregunta a la **API de CRM de HubSpot** — no a una base de datos externa — si ya existe un contacto con ese CUIT en esta cuenta.
5. Según el resultado, el integrante del equipo revisa o completa los datos de contacto en pantalla, y luego confirma.
6. Al confirmar, se llama a una función privada que escribe el contacto (creación o actualización) directamente a través de la **API de CRM de HubSpot**.

```mermaid
sequenceDiagram
    participant U as Integrante del equipo (en HubSpot)
    participant P as Página de la App
    participant F as Funciones Privadas
    participant H as API de CRM de HubSpot
    participant A as ARCA (externo)

    U->>P: Ingresa CUIT y confirma
    P->>F: Validar CUIT
    F->>A: Verificar CUIT (saliente, solo el CUIT)
    A-->>F: Datos de identidad
    F->>H: Buscar contacto existente
    H-->>F: Contacto encontrado / no encontrado
    F-->>P: Muestra el resultado al integrante del equipo
    U->>P: Confirma y envía los datos de contacto
    P->>F: Crear o actualizar contacto
    F->>H: Escribe el registro de contacto
    H-->>F: Confirmación
    F-->>P: Éxito
```

Salvo por la única verificación de CUIT contra ARCA, cada flecha de este diagrama permanece dentro de la red propia de HubSpot. El registro de contacto en sí — nombres, emails, teléfonos — solo se escribe y se lee desde la base de contactos propia de HubSpot.

## 6. App de HubSpot - Detalles

- **No hay infraestructura que asegurar ni mantener.** No hay un servidor separado con su propio sistema operativo ni dependencias que mantener.
- **Sin copia independiente de los datos.** No hay una segunda base de datos que guarde una copia de la información de contacto. Los datos tienen un solo hogar: HubSpot.
- **Menor superficie de ataque.** Las funciones serverless privadas no tienen ninguna dirección pública de internet — solo pueden ser llamadas desde una sesión autenticada de HubSpot en esta cuenta. No hay nada expuesto en internet para que un atacante externo lo encuentre o lo ataque.
- **El control de acceso se hereda de HubSpot.** Como la app no tiene un sistema de login propio, depende enteramente de la autenticación de HubSpot.

## 7. Gobierno y acceso a los datos

Las únicas personas que pueden abrir esta app, y las únicas que pueden ver o modificar los datos con los que trabaja, son personas que ya están logueadas en esta cuenta específica de HubSpot y que tienen los permisos otorgados allí.

- La página de la app solo existe dentro de la interfaz del CRM de HubSpot. No hay una dirección web separada a la que alguien pueda entrar desde afuera de HubSpot. Si alguien no está logueado en HubSpot, la página directamente no existe para esa persona.
- Las funciones que hacen el trabajo real — validar un CUIT, buscar contactos, crear o actualizar un contacto — están configuradas como **funciones privadas**. Esto significa que HubSpot no las expone como direcciones accesibles desde internet. Solo pueden ser invocadas desde una sesión activa y autenticada de esta app, en esta cuenta.
- Cada escritura a un registro de contacto (creación o actualización) se realiza usando credenciales ligadas a la instalación de la app privada en esta cuenta de HubSpot, emitidas y gestionadas por HubSpot.
- El propio sistema de permisos de la cuenta de HubSpot (el mismo que se usa en el resto del CRM) decide quién puede ver o editar los registros de contacto. Esta app no evita ni duplica ese sistema — lo utiliza.


## 8. Validar consultas de Hubspot.

Hubspot permite validar que las peticiones salen de un servidor de Hubspot y no se trata de otra persona o ente externo. Para esto, se puede utilizar la siguiente lógica:

```
/**
 * Validates the HubSpot v3 request signature.
 *
 * @param Request $request
 * @return bool
 */
protected function isValidHubspotSignature(Request $request): bool
{
    $clientSecret = config('services.hubspot.client_secret');

    $signature  = $request->header('X-HubSpot-Signature-v3');
    $timestamp  = $request->header('X-HubSpot-Request-Timestamp');

    if (! $signature || ! $timestamp) {
        return false;
    }

    // Reject requests older than 5 minutes (timestamp is in milliseconds)
    $nowMs = (int) round(microtime(true) * 1000);
    if (abs($nowMs - (int) $timestamp) > 5 * 60 * 1000) {
        return false;
    }

    // Must exactly match the original request: method + full URI + raw body + timestamp
    $method = $request->method();
    $uri    = $request->fullUrl(); // includes query string
    $body   = $request->getContent(); // raw body, NOT $request->all()

    $sourceString = $method . $uri . $body . $timestamp;

    $computedHash = base64_encode(
        hash_hmac('sha256', $sourceString, $clientSecret, true)
    );

    return hash_equals($computedHash, $signature);
}

```

También se puede utilizar otros métodos de autenticación y utilizar la lógica que Hubspot ofrece. [Documentación](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/authentication/request-validation#php).