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+1y trae elcomprobanteque numeró él; no traenroTicketPos. Delega enFacturacionService.fecaeSolicitar. - V2 (NextGen): el POS trae
nroTicketPosy nocomprobante; la numeración es potestad del server. Delega enFacturacionService.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. porname(el orden no es semántico). - Ignora el atributo
locationdesoap: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¶
- Cliente AFIP — los servicios SOAP que este backend consume.
- API REST — los endpoints REST (incluida la variante REST de
getCaea). - El ciclo de facturación — cuándo entra cada operación.