Saltar a contenido

API SOAP

Referencia de los 3 endpoints SOAP que TiFacturaOnlineNext expone (los que consume a AFIP están en Cliente AFIP). El enfoque es contract-first (SPEC-008): el WSDL es la fuente de verdad, y un test bloqueante garantiza que el WSDL servido no se desvíe del baseline de producción. Para el porqué del ciclo, mirá El ciclo de facturación.

Estos endpoints SOAP quedan abiertos (sin auth) a propósito

SecurityConfig deja permitAll explícito sobre /TiFacturaOnlineManagerWS/** (y /ws/**): el POS legacy es máquina-a-máquina y no puede mandar credenciales. Solo el cockpit quedó detrás de autenticación — ver Cockpit. CSRF también está deshabilitado para estas rutas (clientes-máquina sin token CSRF).

Stack

Pieza Valor
Framework SOAP Apache CXF (JAX-WS), cxf.version 4.2.2
Binding SOAP 1.2 (SOAPBinding.SOAP12HTTP_BINDING), style document literal
Transport HTTP (http://schemas.xmlsoap.org/soap/http)
targetNamespace (los 3) http://ws.action.tifacturaonlinemanager.tipre.com/
Path del CXFServlet /TiFacturaOnlineManagerWS (application.yml, cxf.path)
WSDLs fuente src/main/resources/wsdl/{TrxWS,CaeaWS,TiFacturaOnlineManagerWS}.wsdl
WSDLs baseline (gate) baseline/wsdl/ (raíz del repo)

Los 3 servicios

Los publica CxfSoapConfig (com.tipre.tifacturaonlinemanager.config) como tres beans Endpoint. El CXFServlet se monta en cxf.path = /TiFacturaOnlineManagerWS, y cada endpoint se publica en su sub-path, de modo que las URLs resultantes coinciden con las de producción.

Servicio Clase endpoint Sub-path (.publish) URL resultante
TrxWSService TrxWsEndpoint /TrxWS .../TiFacturaOnlineManagerWS/TrxWS
CaeaWSService CaeaWsEndpoint /CaeaWS .../TiFacturaOnlineManagerWS/CaeaWS
TiFacturaOnlineManagerWSService TfomWsEndpoint /TiFacturaOnlineManagerWS .../TiFacturaOnlineManagerWS/TiFacturaOnlineManagerWS

Cada bean fija setWsdlLocation("wsdl/X.wsdl") para servir el WSDL exacto del baseline de producción (no el que CXF regeneraría) — es la vía más segura para que el diff bloqueante dé vacío.

@Bean
public Endpoint trxWsEndpoint(Bus bus, FacturacionService facturacionService) {
    EndpointImpl ep = new EndpointImpl(bus, new TrxWsEndpoint(facturacionService));
    ep.setWsdlLocation("wsdl/TrxWS.wsdl");
    ep.publish("/TrxWS");
    return ep;
}

Cada endpoint implementa una SEI generada por wsdl2java (interfaz endpointInterface) y está anotado @BindingType(SOAPBinding.SOAP12HTTP_BINDING).

El handler de logging legacy NO se porta

SPEC-008 paso 3: a propósito no se registra el WSServerLogHandler legacy. Su logging (TransaccionLog) y su masking estaban muertos en el legacy, y su única función viva (montar el contexto Seam) ya la cubre SeamEventScopeFilter. Portarlo "tal cual" haría crecer el shim de Seam sin beneficio observable.

Operaciones

Las faults estándar de cada operación de negocio son: AfipGeneralException, ParametroRequeridoException, TimeOutException, GeneralException. getVersion y setMockActive no declaran faults; sendEmail y getPdf declaran solo ParametroRequeridoException y GeneralException.

TrxWS (TrxWsEndpoint)

Endpoint de operaciones sobre transacciones (comprobantes) emitidas offline con CAEA.

Operación Estado Qué hace
saveTrx Cableado Persiste una Trx emitida offline con CAEA (delega en FacturacionService.saveTrx).
fecaeaSolicitar Stub Lanza UnsupportedOperationException (pendiente).
setMockActive Stub Lanza UnsupportedOperationException.
getVersion Cableado Devuelve la versión (v20250328).

CaeaWS (CaeaWsEndpoint)

Endpoint del flujo CAEA orientado al POS.

Operación Estado Qué hace
getCaea Stub Lanza UnsupportedOperationException (la variante REST GET /caeaWS/getCaea sí está cableada — ver API REST).
setMockActive Stub Lanza UnsupportedOperationException.
getVersion Stub Lanza UnsupportedOperationException.

TiFacturaOnlineManagerWS (TfomWsEndpoint)

El endpoint principal: emisión de CAE, consultas y puntos de venta.

Operación Estado Qué hace
fecaeSolicitar Cableado Emisión online de CAE — punto de entrada de AMBOS protocolos (V1 clásico y V2 NextGen), discriminados por el dato nroTicketPos que mandó el POS (ver abajo). Idempotencia + AFIP + persistencia vía FacturacionService.
feCompUltimoAutorizado Cableado Último comprobante autorizado en ARCA (usa el TA WSAA + WsfeUltimoComprobanteClient).
feCompConsultar Cableado Consulta un comprobante en AFIP o lo devuelve de la DB (FacturacionService.feCompConsultar).
feCompomprobanteFacturado Cableado Idempotencia de consulta: si la Trx existe la devuelve con CAE; si no, devuelve la del request.
getPv Cableado Puntos de venta CAEA del ente (PtoVentaConsultaService.getPtosVenta).
getErrores Cableado Consulta de un comprobante por su clave de negocio — restauración de contrato (mismo lookup read-only que feCompomprobanteFacturado, rama V1). Valida que venga el Trx (ParametroRequeridoException "Trx es requerido" si falta). Ver nota abajo.
getVersion Cableado Devuelve la versión (v20250328).
setMockActive Stub Lanza UnsupportedOperationException.
sendEmail Stub Lanza UnsupportedOperationException (pendiente — ver Email y PDF).
getPdf Cableado Regenera el PDF del comprobante on-demand y lo devuelve en base64. Recibe el trx.id, re-lee la Trx, delega en PdfFacturaService.generar (nunca persiste el PDF) y devuelve <pdf>{nombre, bytes base64}; el <trx> de la respuesta ecoa el del request. Valida que venga el Trx con id (ParametroRequeridoException "Trx es requerido"). Consumidor: backoffice, no el POS. Ver Email y PDF.

V1 y V2 conviven en fecaeSolicitar — se distinguen por nroTicketPos

Antes existía una operación SOAP aparte, fecaeSolicitarNextGen, para la emisión V2 (numeración automática server-side). Ya no existe: se quitó del WSDL (TiFacturaOnlineManagerWS.wsdl) y ambos protocolos entran por la misma operación fecaeSolicitar. El endpoint discrimina por el dato que mandó el POS (no por un flag, que podría contradecir al dato y re-numerar mal una factura):

  • V1 (clásico): el POS consultó último+1 y trae el comprobante que numeró él; no trae nroTicketPos. Delega en FacturacionService.fecaeSolicitar.
  • V2 (NextGen): el POS trae nroTicketPos y no comprobante; la numeración es potestad del server. Delega en FacturacionService.fecaeSolicitarNextGen (que sigue existiendo como método de servicio Java, no como operación SOAP).

Fail-closed: si vienen AMBOS (nroTicketPos y comprobante), se rechaza con IllegalArgumentException — el server no adivina en una operación de plata. El rechazo de negocio V2 se mapea al fault SOAP tipado GeneralException que fecaeSolicitar ya declara, para no perder el tipo del fault.

getErrores — se restauró al WSDL porque su ausencia rompía TODO el port

getErrores existía en el WSDL de producción, pero la migración la había dejado afuera. El problema no era solo que faltara una operación: los clientes generados del contrato original la declaran en su SEI, y al no encontrarla en el WSDL servido, JAX-WS no podía construir el port y NINGUNA operación funcionaba — fallaba al inicializar, con un engañoso "no se pudo conectar". Restaurarla es lo que devuelve el servicio a un estado usable por esos clientes. El baseline de WsdlDiffGateTest se actualizó para incluirla.

Limitación conocida (follow-up): getErrores todavía no trae la lista de errores

En el legacy, getErrores devolvía el comprobante con su lista de errores en el elemento trxErrores del tipo trx. El tipo trx del WSDL migrado no tiene ese elemento, así que hoy la operación devuelve el comprobante sin esa lista (solo el comprobante + CAE si existe). Restaurar el payload completo obliga a agregar trxErrores al tipo trx — que es compartido por todas las operaciones, así que es una decisión de contrato aparte, no un retoque local.

El gate del WSDL (WsdlDiffGateTest)

SPEC-008 paso 4: un test bloqueante que garantiza que el WSDL servido por CXF no se desvía del contrato de producción. Vive en src/test/java/.../WsdlDiffGateTest.java.

Levanta el app (RANDOM_PORT), baja /TiFacturaOnlineManagerWS/{servicio}?wsdl de cada endpoint y lo compara semánticamente (XMLUnit) contra el baseline congelado en baseline/wsdl/. Normalización aplicada:

  • Ignora whitespace, comentarios y prefijos de namespace (checkForSimilar).
  • Empareja message/operation/etc. por name (el orden no es semántico).
  • Ignora el atributo location de soap:address (host/puerto cambian entre prod y runtime).

Cualquier otra diferencia = test rojo = build rojo. Si hay diff, deja el WSDL servido en target/wsdl-served/ para inspección.

Dos copias de los WSDL — distinto rol

src/main/resources/wsdl/ es la fuente del codegen (wsdl2java) y lo que sirve CXF en runtime (setWsdlLocation). baseline/wsdl/ es el contrato congelado exportado del legacy en vivo (JBoss AS 7.1.1, host EC2AMAZ-01LULGK:8087), contra el que el gate compara. Son archivos distintos a propósito: pueden diferir en serialización (orden de namespaces/atributos) pero deben ser semánticamente idénticos. No editar baseline/wsdl/: es la referencia.

Generación de stubs (cxf-codegen-plugin)

Las SEI y los tipos JAXB se generan en build-time con org.apache.cxf:cxf-codegen-plugin (goal wsdl2java), en la fase generate-sources, hacia target/generated-sources/cxf.

WSDL fuente Paquete generado
src/main/resources/wsdl/TrxWS.wsdl com.tipre.tifacturaonlinemanager.ws.gen.trx
src/main/resources/wsdl/CaeaWS.wsdl com.tipre.tifacturaonlinemanager.ws.gen.caea
src/main/resources/wsdl/TiFacturaOnlineManagerWS.wsdl com.tipre.tifacturaonlinemanager.ws.gen.tfom

Como los 3 WSDL comparten el mismo targetNamespace, se mapea un paquete por servicio (-p ...=...gen.trx, etc.) para que no colisionen las operaciones comunes (getVersion, setMockActive).

# Regenerar las SEI desde los WSDL (corre solo, dentro del build)
mvn generate-sources

# Build completo + el gate bloqueante del WSDL
mvn test

Por dónde seguir