API
Timbrar Retenciones Timbre Fiscal Digital

Timbrar Retenciones Timbre Fiscal Digital

Descripción de la Operación

Esta operación permite timbrar un Comprobante Fiscal de Retenciones e Información de Pagos y, a diferencia de la operación timbrarRetencion, retorna únicamente el Timbre Fiscal Digital (TFD) como un XML independiente. Esto es útil cuando se necesita solo el TFD para procesos de validación o almacenamiento por separado.

Parámetros de Entrada (Input)

Parámetro Tipo de Dato Descripción
apikey string Credencial de acceso al servicio (Solicita aquí).
xml string Contenido del documento XML de la constancia de retenciones a timbrar.

Parámetros de Salida (Output) - RespuestaTimbradoTFD

Atributo Tipo de Dato Descripción
code string Código de respuesta de la operación.
message string Mensaje detallado de la respuesta.
data string XML del Timbre Fiscal Digital (TFD) en caso de éxito.

Ejemplo de Código

A continuación se presenta un ejemplo de cómo construir la solicitud y procesar la respuesta.

Solicitud (Request)

Herramienta svcutil

Descarga e instala la herramienta svcutil

Ejecuta el comando siguiente (DESARROLLO)

svcutil.exe https://dev.facturaloplus.com/ws/servicio.do?wsdl /out:ServicioTimbradoClient.cs /config:app.config

Esto genera dos archivos: ServicioTimbradoClient.cs y la configuración en app.config

Implementación

 private static string GetXmlRetencion() => """ 
      <?xml version="1.0" encoding="UTF-8"?>
      <retenciones:Retenciones 
          xmlns:retenciones="http://www.sat.gob.mx/esquemas/retencionpago/2" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://www.sat.gob.mx/esquemas/retencionpago/2 http://www.sat.gob.mx/esquemas/retencionpago/2/retencionpagov2.xsd" 
          Version="2.0" FolioInt="124" FechaExp="2025-07-05T11:00:00" LugarExpRetenc="64000" CveRetenc="14">
          <retenciones:Emisor RfcE="ABC010101XYZ" NomDenRazSocE="Empresa Emisora SA de CV" RegimenFiscalE="601"/>
          <retenciones:Receptor NacionalidadR="Nacional">
              <retenciones:Nacional RfcR="XAXX010101000" NomDenRazSocR="Receptor Nacional SA de CV" DomicilioFiscalR="67890"/>
          </retenciones:Receptor>
          <retenciones:Periodo MesIni="07" MesFin="07" Ejerc="2025"/>
          <retenciones:Totales montoTotOperacion="25000" montoTotGrav="25000" montoTotExent="0" montoTotRet="2500">
              <retenciones:ImpRetenidos BaseRet="25000" Impuesto="01" montoRet="2500" TipoPagoRet="Pago definitivo"/>
          </retenciones:Totales>
      </retenciones:Retenciones>
      """;

 public async Task<RespuestaTimbradoTFD> TimbrarRetencionTFDAsync(string apiKey, string xml)
  {
      using var client = new ServicioTimbradoWSPortTypeClient("ServicioTimbradoWSPort");
      try
      {
          var response = await client.timbrarRetencionTFDAsync(apiKey, xml);
          return new RespuestaTimbradoTFD
          {
              Code = response.code,
              Message = response.message,
              Data = response.data
          };
      }
      catch (Exception ex)
      {
         Console.WriteLine($"Error al timbrar TFD de retención: {ex.Message}");
         throw;
      }
  }

  public class RespuestaTimbradoTFD
 {
    public string? Code { get; set; }
    public string? Message { get; set; }
    public string? Data { get; set; }
 }

 // Ejemplo de uso
 public async Task EjemploUsoTimbrarRetencionTFDAsync()
 {
     string apiKey = "TU_API_KEY_AQUI";
     string xml = GetXmlRetencion();

     var resultado = await TimbrarRetencionTFDAsync(apiKey, xml);

     if (resultado?.Code == "200")
     {
         Console.WriteLine("¡Timbrado de TFD para Retención Exitoso!");
         Console.WriteLine(resultado.Data);
     }
     else
     {
         Console.WriteLine($"Error: {resultado?.Code} - {resultado?.Message}");
     }
 }

Herramienta wsimport

Java incluye la herramienta wsimport en el JDK para generar las clases cliente a partir de un WSDL.

Ejecuta el siguiente comando en tu terminal para el ambiente de DESARROLLO:

wsimport -keep -p com.facturaloplus.cliente https://dev.facturaloplus.com/ws/servicio.do?wsdl

-keep: Conserva los archivos fuente .java generados.

-p: Especifica el paquete (package) donde se guardarán las clases.

Implementación

import java.util.concurrent.CompletableFuture;

public class TimbrarRetencionTFDService {

  // ... (Definición de ExecutorService, Logger, etc.)

  public String generarXmlRetencion() {
      return """
      <?xml version="1.0" encoding="UTF-8"?>
      <retenciones:Retenciones 
          xmlns:retenciones="http://www.sat.gob.mx/esquemas/retencionpago/2" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://www.sat.gob.mx/esquemas/retencionpago/2 http://www.sat.gob.mx/esquemas/retencionpago/2/retencionpagov2.xsd" 
          Version="2.0" FolioInt="124" FechaExp="2025-07-05T11:00:00" LugarExpRetenc="64000" CveRetenc="14">
          <retenciones:Emisor RfcE="ABC010101XYZ" NomDenRazSocE="Empresa Emisora SA de CV" RegimenFiscalE="601"/>
          <retenciones:Receptor NacionalidadR="Nacional">
              <retenciones:Nacional RfcR="XAXX010101000" NomDenRazSocR="Receptor Nacional SA de CV" DomicilioFiscalR="67890"/>
          </retenciones:Receptor>
          <retenciones:Periodo MesIni="07" MesFin="07" Ejerc="2025"/>
          <retenciones:Totales montoTotOperacion="25000" montoTotGrav="25000" montoTotExent="0" montoTotRet="2500">
              <retenciones:ImpRetenidos BaseRet="25000" Impuesto="01" montoRet="2500" TipoPagoRet="Pago definitivo"/>
          </retenciones:Totales>
      </retenciones:Retenciones>
      """;
  }

  public CompletableFuture<RespuestaTimbradoTFD> timbrarRetencionTFDAsync(String apiKey, String xml) {
      return CompletableFuture.supplyAsync(() -> {
          try {
              ServicioTimbradoWS service = new ServicioTimbradoWS();
              ServicioTimbradoWSPortType port = service.getServicioTimbradoWSPort();
              
              Respuesta response = port.timbrarRetencionTFD(apiKey, xml);

              RespuestaTimbradoTFD resultado = new RespuestaTimbradoTFD();
              resultado.setCode(response.getCode());
              resultado.setMessage(response.getMessage());
              resultado.setData(response.getData());
              return resultado;

          } catch (Exception ex) {
              throw new RuntimeException("Error al timbrar TFD de retención", ex);
          }
      }, executor);
  }

  // Ejemplo de uso
  public static void main(String[] args) {
      TimbrarRetencionTFDService service = new TimbrarRetencionTFDService();
      String apiKey = "TU_API_KEY_AQUI";
      String xml = service.generarXmlRetencion();

      service.timbrarRetencionTFDAsync(apiKey, xml).whenComplete((resultado, ex) -> {
          if (ex != null) {
              System.err.println("Error: " + ex.getMessage());
          } else if ("200".equals(resultado.getCode())) {
              System.out.println("¡Timbrado de TFD para Retención Exitoso!");
              System.out.println(resultado.getData());
          } else {
              System.err.println("Error: " + resultado.getCode() + " - " + resultado.getMessage());
          }
          service.shutdown();
      }).join();
  }
}

Herramienta Zeep

Para interactuar con servicios SOAP en Python, la librería zeep es una excelente opción. Proporciona una interfaz limpia y moderna.

Instala la librería usando pip:

pip install zeep

Implementación

El siguiente código muestra una implementación robusta utilizando zeep y asyncio para realizar llamadas asíncronas al servicio web.

import asyncio
from zeep.asyncio import AsyncClient

def generar_xml_retencion() -> str:
    return """\
      <?xml version="1.0" encoding="UTF-8"?>
      <retenciones:Retenciones 
          xmlns:retenciones="http://www.sat.gob.mx/esquemas/retencionpago/2" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://www.sat.gob.mx/esquemas/retencionpago/2 http://www.sat.gob.mx/esquemas/retencionpago/2/retencionpagov2.xsd" 
          Version="2.0" FolioInt="124" FechaExp="2025-07-05T11:00:00" LugarExpRetenc="64000" CveRetenc="14">
          <retenciones:Emisor RfcE="ABC010101XYZ" NomDenRazSocE="Empresa Emisora SA de CV" RegimenFiscalE="601"/>
          <retenciones:Receptor NacionalidadR="Nacional">
              <retenciones:Nacional RfcR="XAXX010101000" NomDenRazSocR="Receptor Nacional SA de CV" DomicilioFiscalR="67890"/>
          </retenciones:Receptor>
          <retenciones:Periodo MesIni="07" MesFin="07" Ejerc="2025"/>
          <retenciones:Totales montoTotOperacion="25000" montoTotGrav="25000" montoTotExent="0" montoTotRet="2500">
              <retenciones:ImpRetenidos BaseRet="25000" Impuesto="01" montoRet="2500" TipoPagoRet="Pago definitivo"/>
          </retenciones:Totales>
      </retenciones:Retenciones>
    """

class TimbradoService:
    def __init__(self, wsdl_url: str):
        self.wsdl_url = wsdl_url
        self.async_client = AsyncClient(self.wsdl_url)

    async def timbrar_retencion_tfd_async(self, api_key: str, xml: str) -> RespuestaTimbradoTFD:
        response = await self.async_client.service.timbrarRetencionTFD(
            apikey=api_key,
            xml=xml
        )
        return RespuestaTimbradoTFD(
            code=response.code,
            message=response.message,
            data=response.data
        )

async def main():
    service = TimbradoService("https://dev.facturaloplus.com/ws/servicio.do?wsdl")
    api_key = "TU_API_KEY_AQUI";
    xml = generar_xml_retencion();

    resultado = await service.timbrar_retencion_tfd_async(api_key, xml);

    if resultado.code == "200":
        print("¡Timbrado de TFD para Retención Exitoso!");
        print(resultado.data);
    else:
        print(f"Error: {resultado.code} - {resultado.message}");

if __name__ == "__main__":
    asyncio.run(main());

Herramienta SoapClient

PHP tiene soporte nativo para SOAP a través de la extensión SOAP. Asegúrate de que la extensión php-soap esté habilitada en tu archivo php.ini.

Implementación

El siguiente código muestra una implementación orientada a objetos para consumir el servicio de timbrado.

<?php
function generarXmlRetencion(): string {
    return <<<'XML'
      <?xml version="1.0" encoding="UTF-8"?>
      <retenciones:Retenciones 
          xmlns:retenciones="http://www.sat.gob.mx/esquemas/retencionpago/2" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://www.sat.gob.mx/esquemas/retencionpago/2 http://www.sat.gob.mx/esquemas/retencionpago/2/retencionpagov2.xsd" 
          Version="2.0" FolioInt="124" FechaExp="2025-07-05T11:00:00" LugarExpRetenc="64000" CveRetenc="14">
          <retenciones:Emisor RfcE="ABC010101XYZ" NomDenRazSocE="Empresa Emisora SA de CV" RegimenFiscalE="601"/>
          <retenciones:Receptor NacionalidadR="Nacional">
              <retenciones:Nacional RfcR="XAXX010101000" NomDenRazSocR="Receptor Nacional SA de CV" DomicilioFiscalR="67890"/>
          </retenciones:Receptor>
          <retenciones:Periodo MesIni="07" MesFin="07" Ejerc="2025"/>
          <retenciones:Totales montoTotOperacion="25000" montoTotGrav="25000" montoTotExent="0" montoTotRet="2500">
              <retenciones:ImpRetenidos BaseRet="25000" Impuesto="01" montoRet="2500" TipoPagoRet="Pago definitivo"/>
          </retenciones:Totales>
      </retenciones:Retenciones>
XML;
}

class TimbradoService {
    private string $wsdlUrl;

    public function __construct(string $wsdlUrl) {
        $this->wsdlUrl = $wsdlUrl;
    }

    public function timbrarRetencionTFD(string $apiKey, string $xml): RespuestaTimbradoTFD {
        $respuesta = new RespuestaTimbradoTFD();
        try {
            $soapClient = new SoapClient($this->wsdlUrl, ['trace' => 1, 'exceptions' => true]);
            $params = [
                'apikey'  => $apiKey,
                'xml' => $xml
            ];

            $response = $soapClient->timbrarRetencionTFD($params);
            
            $respuesta->code = $response->return->code ?? null;
            $respuesta->message = $response->return->message ?? null;
            $respuesta->data = $response->return->data ?? null;

        } catch (Exception $e) {
            $respuesta->code = "CLIENT_ERROR";
            $respuesta->message = $e->getMessage();
        }
        return $respuesta;
    }
}

// Ejemplo de uso
$service = new TimbradoService("https://dev.facturaloplus.com/ws/servicio.do?wsdl");
$apiKey = "TU_API_KEY_AQUI";
$xml = generarXmlRetencion();

$resultado = $service->timbrarRetencionTFD($apiKey, $xml);

header('Content-Type: text/plain');
if ($resultado->code === '200') {
    echo "¡Timbrado de TFD para Retención Exitoso!\n";
    echo $resultado->data;
} else {
    echo "Error: {$resultado->code} - {$resultado->message}\n";
}
?>

Respuesta (Response)

El campo data contendrá únicamente el XML del Timbre Fiscal Digital (TFD).

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope ...>
   <SOAP-ENV:Body>
      <ns1:timbrarRetencionTFDResponse xmlns:ns1="urn:ws_api">
         <return xsi:type="tns:RespuestaTimbradoTFD">
            <code xsi:type="xsd:string">CÓDIGO</code>
            <message xsi:type="xsd:string">MENSAJE</message>
            <data xsi:type="xsd:string"><![CDATA[<tfd:TimbreFiscalDigital ... />]]></data>
         </return>
      </ns1:timbrarRetencionTFDResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Códigos de respuesta

Los códigos de respuesta para el timbrado de CFDI de retenciones son importantes para entender el resultado de la solicitud. A continuación se detallan los códigos más comunes que puedes recibir al realizar una solicitud de timbrado de retenciones a través de la API.

Código Descripción
200 Solicitud procesada con éxito
300 API KEY Inválida o inexistente
T301 XML mal formado
T302 El sello del emisor no es válido
T303 El RFC del CSD del emisor no corresponde al RFC del Emisor
T304 CSD del Emisor ha sido revobado
T305 La fecha de emisión no está dentro de la vigencia del CSD del Emisor
T306 La llave utilizada para sellar debe ser un CSD
T307 El CFDI contiene un timbre previo
T308 El CSD del emisor no ha sido firmado por uno de los Certificados de autoridad del SAT
T401 El rango de la fecha de generación no debe de ser mayor a 72 horas para la emisión del timbre
T402 RFC del emisor no se encuentra en el régimen de contribuyentes (Lista de validación de régimen) LCO