
     ██████╗  ██████╗██╗    ███╗   ███╗██╗██████╗ ██████╗ ██╗     ███████╗██╗    ██╗ █████╗ ██████╗ ███████╗
    ██╔═══██╗██╔════╝██║    ████╗ ████║██║██╔══██╗██╔══██╗██║     ██╔════╝██║    ██║██╔══██╗██╔══██╗██╔════╝
    ██║   ██║██║     ██║    ██╔████╔██║██║██║  ██║██║  ██║██║     █████╗  ██║ █╗ ██║███████║██████╔╝█████╗  
    ██║   ██║██║     ██║    ██║╚██╔╝██║██║██║  ██║██║  ██║██║     ██╔══╝  ██║███╗██║██╔══██║██╔══██╗██╔══╝  
    ╚██████╔╝╚██████╗██║    ██║ ╚═╝ ██║██║██████╔╝██████╔╝███████╗███████╗╚███╔███╔╝██║  ██║██║  ██║███████╗
     ╚═════╝  ╚═════╝╚═╝    ╚═╝     ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝ ╚══╝╚══╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝
                                                                                                            
# Middleware PunchOut OCI ↔ cXML

## Descripción

Este proyecto implementa un **middleware de compatibilidad PunchOut** que permite conectar clientes SAP que trabajan con **OCI PunchOut** contra una plataforma ecommerce que actualmente soporta **cXML PunchOut**.

El middleware actúa como una capa intermedia entre ambos sistemas:

- Hacia el **cliente SAP**, expone una interfaz compatible con **OCI PunchOut**.
- Hacia el **ecommerce**, consume la integración existente mediante **cXML PunchOut**.
- Durante el proceso, mantiene el estado de la sesión, traduce los mensajes necesarios y devuelve el carrito al sistema de compras del cliente en el formato esperado.

El objetivo es evitar tener que modificar el sistema del cliente o esperar un desarrollo OCI nativo por parte del proveedor del ecommerce.

---

## Problema que resuelve

La empresa venía operando con clientes mediante **OCI PunchOut**.

Luego de una migración urgente de ecommerce, la nueva plataforma disponible soporta **cXML PunchOut**, pero no **OCI PunchOut** de forma nativa.

Esto genera una incompatibilidad:

```text
Cliente SAP espera OCI
Ecommerce soporta cXML
```

El middleware resuelve esa diferencia funcionando como adaptador:

```text
Cliente SAP / OCI  →  Middleware  →  Ecommerce / cXML
Cliente SAP / OCI  ←  Middleware  ←  Ecommerce / cXML
```

---

## Qué hace el middleware

El middleware no solo traduce campos entre OCI y cXML.

También administra el flujo completo de una sesión PunchOut.

Sus responsabilidades principales son:

1. Recibir el inicio de sesión PunchOut desde SAP en formato OCI.
2. Validar credenciales y parámetros recibidos.
3. Crear una sesión interna para conservar datos como `HOOK_URL` y `BuyerCookie`.
4. Generar un `PunchOutSetupRequest` cXML hacia el ecommerce.
5. Procesar la respuesta `PunchOutSetupResponse` del ecommerce.
6. Redirigir al usuario al catálogo online.
7. Recibir el carrito devuelto por el ecommerce como `PunchOutOrderMessage` cXML.
8. Convertir los ítems del carrito al formato OCI esperado por SAP.
9. Devolver el carrito al `HOOK_URL` original del cliente.
10. Registrar logs, errores y trazabilidad del flujo.

---

## Flujo general

```text
Cliente SAP / OCI
        |
        | 1) Envía solicitud OCI con USERNAME, PASSWORD, HOOK_URL, etc.
        v
Middleware OCI ↔ cXML
        |
        | 2) Genera PunchOutSetupRequest cXML
        v
Ecommerce con soporte cXML
        |
        | 3) Devuelve PunchOutSetupResponse con StartPage URL
        v
Middleware
        |
        | 4) Redirige al usuario al catálogo
        v
Usuario selecciona productos
        |
        | 5) Ecommerce devuelve PunchOutOrderMessage cXML
        v
Middleware
        |
        | 6) Convierte carrito cXML a campos OCI
        v
Cliente SAP recibe carrito OCI
```

---

## Endpoints principales

### `POST /oci/setup`

Endpoint público utilizado por el sistema SAP del cliente para iniciar la sesión PunchOut mediante OCI.

Recibe parámetros como:

```text
USERNAME
PASSWORD
HOOK_URL
~TARGET
~OKCODE
~CALLER
```

Responsabilidades:

- Validar los datos recibidos.
- Crear una sesión interna.
- Guardar el `HOOK_URL` original.
- Generar un `BuyerCookie`.
- Construir el `PunchOutSetupRequest` cXML.
- Enviar el request al ecommerce.
- Recibir el `PunchOutSetupResponse`.
- Redirigir al usuario al catálogo del ecommerce.

---

### `POST /cxml/return`

Endpoint utilizado por el ecommerce para devolver el carrito seleccionado en formato cXML.

Recibe un documento:

```text
PunchOutOrderMessage
```

Responsabilidades:

- Leer y validar el XML recibido.
- Obtener el `BuyerCookie`.
- Recuperar la sesión interna correspondiente.
- Extraer los productos del carrito.
- Convertir cada ítem al formato OCI.
- Generar el formulario de retorno hacia SAP.
- Enviar el carrito al `HOOK_URL` original.

---

### `GET /health`

Endpoint simple de monitoreo.

Respuesta esperada:

```json
{
  "status": "ok"
}
```

Se usa para verificar que el servicio esté activo.

---

## Estado de sesión

El middleware necesita conservar estado entre el inicio OCI y el retorno posterior del carrito cXML.

Información mínima a persistir:

```text
session_id
buyer_cookie
oci_username
oci_hook_url
oci_target
oci_okcode
oci_caller
status
created_at
expires_at
completed_at
```

El `BuyerCookie` permite asociar el carrito cXML devuelto por el ecommerce con la sesión OCI original iniciada por el cliente SAP.

---

## Traducción OCI → cXML

Cuando SAP inicia el flujo OCI, el middleware recibe datos como:

```text
USERNAME
PASSWORD
HOOK_URL
~TARGET
~OKCODE
~CALLER
```

Con esos datos genera un `PunchOutSetupRequest` cXML hacia el ecommerce.

El request cXML debe incluir información como:

```text
From
To
Sender
SharedSecret
BuyerCookie
BrowserFormPost URL
```

El `BrowserFormPost URL` debe apuntar al middleware:

```text
https://tudominio.com/cxml/return
```

De esa forma, cuando el usuario finalice el carrito, el ecommerce no vuelve directo al cliente SAP, sino al middleware, para que este pueda traducir el carrito de cXML a OCI.

---

## Traducción cXML → OCI

Cuando el ecommerce devuelve el carrito, el middleware recibe un `PunchOutOrderMessage` cXML.

De cada producto se extraen campos como:

```text
SupplierPartID
Description
Quantity
UnitPrice
Currency
UnitOfMeasure
Classification
```

Luego se convierten a campos OCI:

```text
NEW_ITEM-DESCRIPTION[n]
NEW_ITEM-QUANTITY[n]
NEW_ITEM-UNIT[n]
NEW_ITEM-PRICE[n]
NEW_ITEM-CURRENCY[n]
NEW_ITEM-VENDORMAT[n]
NEW_ITEM-MATGROUP[n]
```

Ejemplo para un producto:

```text
NEW_ITEM-DESCRIPTION[1]=Producto ejemplo
NEW_ITEM-QUANTITY[1]=2
NEW_ITEM-UNIT[1]=UN
NEW_ITEM-PRICE[1]=100.00
NEW_ITEM-CURRENCY[1]=ARS
NEW_ITEM-VENDORMAT[1]=SKU123
NEW_ITEM-MATGROUP[1]=12345678
```

---

## Mapeo básico de campos

| cXML | OCI | Descripción |
|---|---|---|
| `SupplierPartID` | `NEW_ITEM-VENDORMAT[n]` | Código del producto del proveedor |
| `Description` | `NEW_ITEM-DESCRIPTION[n]` | Descripción del producto |
| `quantity` | `NEW_ITEM-QUANTITY[n]` | Cantidad seleccionada |
| `UnitPrice/Money` | `NEW_ITEM-PRICE[n]` | Precio unitario |
| `Money/@currency` | `NEW_ITEM-CURRENCY[n]` | Moneda |
| `UnitOfMeasure` | `NEW_ITEM-UNIT[n]` | Unidad de medida |
| `Classification` | `NEW_ITEM-MATGROUP[n]` | Grupo de material / categoría / UNSPSC |

Este mapeo puede ajustarse según los campos obligatorios del SAP del cliente.

---

## Retorno al sistema SAP

El retorno al `HOOK_URL` del cliente normalmente debe realizarse mediante un formulario HTML con campos ocultos y auto-submit.

Ejemplo conceptual:

```html
<form method="POST" action="HOOK_URL_ORIGINAL">
  <input type="hidden" name="NEW_ITEM-DESCRIPTION[1]" value="Producto ejemplo">
  <input type="hidden" name="NEW_ITEM-QUANTITY[1]" value="2">
  <input type="hidden" name="NEW_ITEM-UNIT[1]" value="UN">
  <input type="hidden" name="NEW_ITEM-PRICE[1]" value="100.00">
  <input type="hidden" name="NEW_ITEM-CURRENCY[1]" value="ARS">
  <input type="hidden" name="NEW_ITEM-VENDORMAT[1]" value="SKU123">
  <input type="hidden" name="NEW_ITEM-MATGROUP[1]" value="12345678">
</form>

<script>
  document.forms[0].submit();
</script>
```

Este comportamiento debe validarse con el sistema SAP del cliente o con un OCI RoundTrip Tester.

---

## Configuración esperada

El middleware debería permitir configuración por ambiente.

Variables típicas:

```env
APP_ENV=local
APP_URL=https://middleware.ejemplo.com

ECOMMERCE_CXML_ENDPOINT=https://segufer.ar/punchout/setup
ECOMMERCE_CXML_FROM_ID=
ECOMMERCE_CXML_TO_ID=
ECOMMERCE_CXML_SENDER_ID=
ECOMMERCE_CXML_SHARED_SECRET=

OCI_USERNAME=
OCI_PASSWORD=

SESSION_TTL_MINUTES=60
MAX_XML_SIZE_MB=2
LOG_LEVEL=info
```

---

## Seguridad

Requisitos mínimos de seguridad:

1. Usar HTTPS en producción.
2. Validar `USERNAME` y `PASSWORD` del flujo OCI.
3. No registrar passwords ni shared secrets en logs.
4. Expirar sesiones automáticamente.
5. Validar tamaño máximo del XML recibido.
6. Deshabilitar entidades externas XML para evitar XXE.
7. Sanitizar valores antes de generar HTML de retorno.
8. Registrar errores con un identificador de request.
9. Separar credenciales de testing y producción.
10. Permitir configuración por cliente en caso de múltiples integraciones.

---

## Logs y trazabilidad

El middleware debe registrar eventos clave del flujo:

```text
Inicio de sesión OCI recibido
Sesión creada
PunchOutSetupRequest enviado al ecommerce
PunchOutSetupResponse recibido
Redirección al catálogo
PunchOutOrderMessage recibido
Carrito convertido a OCI
Retorno enviado al HOOK_URL
Sesión completada
Error de validación
Error de comunicación con ecommerce
Error de retorno al cliente
```

Los logs deben permitir reconstruir el flujo completo sin exponer datos sensibles.

---

## Casos de error esperados

El middleware debe manejar, como mínimo:

| Caso | Respuesta esperada |
|---|---|
| Falta `HOOK_URL` | Rechazar solicitud OCI |
| Credenciales inválidas | Rechazar solicitud OCI |
| Ecommerce no responde | Mostrar error controlado |
| cXML inválido | Registrar error y rechazar retorno |
| `BuyerCookie` inexistente | No se puede asociar la sesión |
| Sesión expirada | Rechazar retorno |
| Carrito sin ítems | Rechazar o devolver error controlado |
| Error al postear al `HOOK_URL` | Registrar y mostrar mensaje controlado |

---

## Validación mínima del proyecto

Antes de pasar a producción, validar:

1. Que el ecommerce acepte un `PunchOutSetupRequest` generado por el middleware.
2. Que devuelva un `PunchOutSetupResponse` válido.
3. Que la URL de catálogo permita navegar y seleccionar productos.
4. Que el ecommerce devuelva el carrito al `BrowserFormPost` del middleware.
5. Que el `BuyerCookie` permita recuperar la sesión.
6. Que el carrito se convierta correctamente a OCI.
7. Que SAP o el tester OCI reciba correctamente los campos `NEW_ITEM-*`.
8. Que los logs permitan seguir toda la operación.

---

## Limitaciones

Este middleware no reemplaza al ecommerce ni modifica su lógica comercial.

No administra:

- Stock.
- Precios.
- Usuarios finales del ecommerce.
- Catálogo de productos.
- Reglas comerciales.
- Autorizaciones internas del cliente SAP.

Su función es exclusivamente actuar como capa de compatibilidad PunchOut entre OCI y cXML.

---

## Resumen

Este middleware permite que un cliente SAP continúe operando con **OCI PunchOut**, aunque la plataforma ecommerce actual solo soporte **cXML PunchOut**.

Funciona como adaptador de protocolo y administrador de sesión:

```text
OCI hacia el cliente
cXML hacia el ecommerce
```

Con esta solución se evita pedir cambios urgentes al cliente o al proveedor del ecommerce, manteniendo el control de la integración en una capa propia.
