Sat.Cfdi.Descarga.Masiva.Verificacion
(sat_cfdi_descarga v1.5.7)
Copy Markdown
Servicio VerificaSolicitudDescarga del WS de Descarga Masiva.
Endpoint: https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/VerificaSolicitudDescargaService.svc.
Consulta el estado de una solicitud previa. Posibles estados:
:aceptada (1), :en_proceso (2), :terminada (3), :error (4),
:rechazada (5), :vencida (6).
Cuando el estado es :terminada, la respuesta incluye los IdsPaquetes
listos para descargar.
Summary
Functions
Endpoint del servicio.
Hace polling llamando a verificar/3 hasta que la solicitud llegue a un
estado terminal: :terminada, :error, :rechazada o :vencida.
SOAPAction.
Verifica el estado de una solicitud por su id_solicitud.
Functions
Endpoint del servicio.
@spec esperar_terminada( Sat.Cfdi.Descarga.Masiva.Types.Token.t(), String.t(), keyword() ) :: {:ok, Sat.Cfdi.Descarga.Masiva.Types.VerificacionResult.t()} | {:error, term()}
Hace polling llamando a verificar/3 hasta que la solicitud llegue a un
estado terminal: :terminada, :error, :rechazada o :vencida.
Opciones extra
:poll_interval_ms(default 30_000) — espera entre intentos.:max_attempts(default 60 — total máximo ~30 minutos).- (más las mismas de
verificar/3::credential,:rfc_solicitante, …).
Retorno
{:ok, %VerificacionResult{}}con unestado_solicitudterminal. Ojo: terminal NO implica éxito —:rechazada/:error/:vencidatambién terminan el polling. Revisaestado_solicitudycodigo_estado_solicitud(p. ej.:rechazada+"5004"= sin datos).{:error, {:timeout, :max_attempts_reached, max}}— se agotaron los intentos y la solicitud seguía en:aceptada/:en_proceso. Reintenta más tarde o sube:max_attempts.- cualquier
{:error, reason}que devuelvaverificar/3.
Ejemplo
case Verificacion.esperar_terminada(token, id, credential: cred, max_attempts: 20) do
{:ok, %{estado_solicitud: :terminada, ids_paquetes: ids}} -> descargar(ids)
{:ok, %{estado_solicitud: :rechazada, codigo_estado_solicitud: "5004"}} -> :sin_datos
{:ok, %{estado_solicitud: estado}} -> {:error, {:no_terminada, estado}}
{:error, {:timeout, _, _}} -> {:error, :sigue_en_proceso}
{:error, reason} -> {:error, reason}
end
SOAPAction.
@spec verificar(Sat.Cfdi.Descarga.Masiva.Types.Token.t(), String.t(), keyword()) :: {:ok, Sat.Cfdi.Descarga.Masiva.Types.VerificacionResult.t()} | {:error, term()}
Verifica el estado de una solicitud por su id_solicitud.
Opciones
:credential(requerido) — FIEL para firmar.:rfc_solicitante— RFC del solicitante (default: el del certificado).:endpoint— override.:timeout— HTTP timeout.
Retorno
{:ok, %VerificacionResult{estado_solicitud, codigo_estado_solicitud, numero_cfdis, mensaje, ids_paquetes, id_solicitud}}.
El campo clave es estado_solicitud (átomo):
| Átomo | EstadoSolicitud | Significado | ¿Terminal? |
|---|---|---|---|
:aceptada | 1 | En espera de procesar. | No, sigue haciendo polling. |
:en_proceso | 2 | Generando paquetes. | No, sigue haciendo polling. |
:terminada | 3 | Lista; ids_paquetes disponibles. | Sí → descargar. |
:error | 4 | Error del SAT. | Sí. |
:rechazada | 5 | Rechazada. | Sí. |
:vencida | 6 | Venció (paquetes viven 72 h). | Sí. |
codigo_estado_solicitud (string) da el detalle, sobre todo si :rechazada:
| Código | Significado |
|---|---|
"5000" | Solicitud recibida con éxito. |
"5002" | Se agotaron las solicitudes de por vida. |
"5003" | Tope máximo de elementos. |
"5004" | No se encontró la información — no hay CFDI con esos criterios. Suele venir con estado_solicitud: :rechazada y numero_cfdis: 0. No es un bug. Causa #1: el rango de fechas no cubre la fecha de emisión de los CFDIs (año/mes equivocado); causa #2: los CFDIs están cancelados y filtraste :vigente. Revisa el <des:solicitud FechaInicial=... FechaFinal=...> enviado vs. el portal del SAT. |
"5005" | Solicitud duplicada. |
ids_paquetes solo trae elementos cuando estado_solicitud == :terminada.
Errores ({:error, reason})
{:missing_option, :credential}, {:http_error, status, body},
{:network_error, reason}, {:soap_fault, code, string},
{:parse_error, :missing_fields, body}.
Respuesta cruda del SAT (HTTP 200) y mapeo a VerificacionResult
El cuerpo relevante es el elemento <VerificaSolicitudDescargaResult>. Cada
atributo/hijo se mapea así:
| XML | Campo del struct | Nota |
|---|---|---|
EstadoSolicitud="3" | estado_solicitud | Se convierte al átomo (3 → :terminada). |
CodigoEstadoSolicitud="5000" | codigo_estado_solicitud | String tal cual. |
NumeroCFDIs="35" | numero_cfdis | Entero. |
Mensaje="..." | mensaje | String. |
CodEstatus="5000" | — | Estatus de la petición de verificación (no de la solicitud). |
<IdsPaquetes>...</IdsPaquetes> | ids_paquetes | Lista; solo llega si :terminada. |
Ejemplo TERMINADA (hay paquetes para descargar)
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<VerificaSolicitudDescargaResponse xmlns="http://DescargaMasivaTerceros.sat.gob.mx">
<VerificaSolicitudDescargaResult CodEstatus="5000" EstadoSolicitud="3"
CodigoEstadoSolicitud="5000" NumeroCFDIs="35" Mensaje="Solicitud Aceptada">
<IdsPaquetes>790040c0-1135-4a30-bf03-9cb25f863396_01</IdsPaquetes>
</VerificaSolicitudDescargaResult>
</VerificaSolicitudDescargaResponse>
</s:Body>
</s:Envelope>→ %VerificacionResult{estado_solicitud: :terminada, codigo_estado_solicitud: "5000", numero_cfdis: 35, ids_paquetes: ["790040c0-...-_01"], mensaje: "Solicitud Aceptada"}
Ejemplo RECHAZADA sin datos (respuesta real del SAT)
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<VerificaSolicitudDescargaResponse xmlns="http://DescargaMasivaTerceros.sat.gob.mx">
<VerificaSolicitudDescargaResult CodEstatus="5000" EstadoSolicitud="5"
CodigoEstadoSolicitud="5004" NumeroCFDIs="0" Mensaje="Solicitud Aceptada"/>
</VerificaSolicitudDescargaResponse>
</s:Body>
</s:Envelope>→ %VerificacionResult{estado_solicitud: :rechazada, codigo_estado_solicitud: "5004", numero_cfdis: 0, ids_paquetes: [], mensaje: "Solicitud Aceptada"}
Nota: CodEstatus="5000" + Mensaje="Solicitud Aceptada" se refieren a que la
consulta de verificación fue procesada, NO a que la solicitud tenga datos.
El estado real de la solicitud está en estado_solicitud +
codigo_estado_solicitud (aquí :rechazada / "5004" = sin CFDIs).
Ejemplo
case Verificacion.verificar(token, id, credential: cred) do
{:ok, %{estado_solicitud: :terminada, ids_paquetes: ids}} -> {:ok, ids}
{:ok, %{estado_solicitud: e}} when e in [:aceptada, :en_proceso] -> :en_proceso
{:ok, %{estado_solicitud: :rechazada, codigo_estado_solicitud: "5004"}} -> {:ok, []}
{:ok, %{codigo_estado_solicitud: cod}} -> {:error, {:rechazada, cod}}
{:error, reason} -> {:error, reason}
end