Saltar al contenido principal

SIFEN — Descarga de XML y Reenvío de DTEs

Fecha: 2026-04-10 Estado: Production-ready (probado contra SIFEN TEST) Ticket Jira: FAC-72 / Sprint 4.1 — API REST Endpoints añadidos: GET /api/sifen/dtes/{id}/xml, POST /api/sifen/dtes/{id}/reenviar


Qué resuelven

Estos dos endpoints completan el ciclo operativo del DTE en OnnixConnect:

EndpointPropósito
GET /dtes/{id}/xmlDescargar el XML firmado de un DTE para respaldo, auditoría, contabilidad o reimpresión
POST /dtes/{id}/reenviarReencolar un DTE rechazado o con error técnico para volver a enviarlo a SIFEN

Decisión de diseño: solo se usan métodos GET y POST (no PUT/DELETE/PATCH) por:

  1. Compatibilidad con firewalls y proxies corporativos.
  2. Simplicidad para integradores externos.
  3. Coherencia con el modelo SIFEN: todo es "crear" (POST) o "consultar" (GET).

1. Descargar XML firmado

Endpoint

GET /api/sifen/dtes/{id}/xml
Authorization: Bearer <token>

Permiso requerido: sifen:read (roles: admin, operador, lector).

Comportamiento

  • Retorna el XML firmado (XMLDSig) del DTE como descarga binaria.
  • Content-Type: application/xml; charset=UTF-8
  • Content-Disposition: attachment; filename="{CDC}.xml" (o dte_{id}.xml si no tiene CDC)
  • El XML incluye la firma digital, el CDC y todos los campos del DE tal como fueron enviados a SIFEN.

Estados permitidos

El DTE debe tener un xml_signed_path no nulo. Esto se cumple en:

Estado¿Tiene XML firmado?
draftNO (aún no firmado)
signedSI
sentSI
approvedSI
rejectedSI
cancelledSI
errorDepende de cuándo falló

Respuestas

CódigoDescripción
200XML retornado como descarga
401Token ausente o inválido
403Sin permiso sifen:read
404DTE no encontrado o archivo XML no existe en disco
409DTE en estado draft (sin XML firmado todavía)

Ejemplo

curl -O -J \
-H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/sifen/dtes/96/xml

# Resultado:
# Saved as: 01801260060001001000100122026041017345886385.xml

Caso de uso típico

  • Auditoría/respaldo: descarga periódica de todos los XMLs aprobados para almacenamiento off-site.
  • Integración contable: envío del XML al ERP del cliente.
  • Reimpresión del KuDE: el KuDE se regenera del XML.
  • Resolución de incidencias: cuando un cliente reporta un problema con una factura específica.

2. Reenviar un DTE

Endpoint

POST /api/sifen/dtes/{id}/reenviar
Authorization: Bearer <token>

Permiso requerido: sifen:send (roles: admin, operador).

Comportamiento

Reencola el DTE para volver a procesarlo en el pipeline normal. El job despachado depende del estado actual:

Estado actualAcciónJob despachadoEstado tras la acción
draftFirma + envíoBuildAndSignDteJobSendDteJobdraft (los jobs lo avanzan)
signedSolo envíoSendDteJobsigned
rejectedResetea cod/msg, encola envíoSendDteJobsigned
errorIgual que rejectedSendDteJobsigned
sent409 — ya en procesosin cambio
approved409 — no tiene sentidosin cambio
cancelled409 — irreversiblesin cambio

Validaciones

  1. El DTE debe existir (404 si no).
  2. El estado actual debe estar en la lista permitida (409 si no).
  3. Para rejected y error: el reset limpia sifen_result_code y sifen_result_msg para que el reenvío empiece desde cero.

Respuestas

Éxito (200):

{
"message": "DTE 42 reencolado para envío.",
"dte_id": 42,
"estado_anterior": "rejected",
"estado_actual": "signed",
"job": "SendDteJob"
}

Conflicto (409):

{
"message": "El DTE 42 está en estado approved y no puede reenviarse."
}
CódigoDescripción
200DTE reencolado correctamente
401Token ausente o inválido
403Sin permiso sifen:send
404DTE no encontrado
409DTE en estado terminal (approved/cancelled/sent)

Ejemplo

# Reenviar un DTE rechazado
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/sifen/dtes/42/reenviar

# Respuesta:
# {
# "message": "DTE 42 reencolado para envío.",
# "dte_id": 42,
# "estado_anterior": "rejected",
# "estado_actual": "signed",
# "job": "SendDteJob"
# }

Caso de uso típico

  • Recuperación de rechazos: SIFEN rechaza un DTE por validación temporal (ej. RUC del receptor en proceso de actualización), después se corrige y se reenvía.
  • Errores técnicos: caídas de TLS, timeouts del servidor SIFEN, errores intermitentes — se reintentan manualmente.
  • DTEs estancados en signed: si la cola de Horizon estuvo caída cuando se firmó el DTE, se puede empujar manualmente con este endpoint.
  • Re-firma + envío: un DTE en draft que necesita pasar por el pipeline completo nuevamente.

Pipeline interno

POST /api/sifen/dtes/{id}/reenviar
↓ Verifica permiso sifen:send
↓ SifenDte::findOrFail($id)
↓ Validación: estado in [draft, signed, rejected, error]
↓ Si rejected/error: update estado=signed, limpia cod/msg
↓ Despacha job(s):
- draft → BuildAndSignDteJob → chain(SendDteJob)
- signed → SendDteJob
- rejected/error → SendDteJob (con estado ya reseteado)
↓ JsonResponse con detalle del reencolado

Convención GET/POST únicamente

Los endpoints de OnnixConnect siguen una convención estricta:

MétodoUso
GETLectura de datos (consultas, descargas, listados)
POSTAcciones que crean o modifican estado (emitir, cancelar, reenviar, subir cert)

No se usan PUT, PATCH ni DELETE por:

  • Algunos firewalls y proxies corporativos solo permiten GET/POST.
  • En SIFEN no existe el concepto de "eliminar" un DTE — solo cancelar.
  • Simplifica la integración para clientes que usan SDKs HTTP básicos.
  • Coherente con el patrón de SIFEN, que solo expone WS sync/async/eventos/consultas (todos POST).

Cache

El endpoint GET /dtes/{id}/xml no usa cache (FAC-85) porque:

  • Los XMLs firmados se sirven directamente desde disco (Storage::disk('local')).
  • Cachear blobs binarios en Redis es contraproducente (gasta memoria sin acelerar significativamente).
  • El XML firmado es inmutable: una vez firmado, no cambia.

El sistema de archivos de Linux ya cachea los reads frecuentes en memoria (page cache) sin código adicional.


Archivos del módulo

ArchivoCambio
app/Http/Controllers/Api/SifenController.phpMétodos downloadXml() y reenviar() con anotaciones Swagger
routes/api.phpGET /dtes/{id}/xml + POST /dtes/{id}/reenviar con middleware de permisos
storage/api-docs/api-docs.jsonSwagger regenerado

Referencias

  • docs/ROADMAP_EJECUCION_OC.md — Tarea 4.1 (líneas 1463–1502)
  • docs/guias-tecnicas/SIFEN_EVENTOS_FUNCIONAMIENTO.md — Endpoints relacionados
  • Sprint 4 — API REST + KUDE + Observabilidad