API
Timbrar Con Timbre Fiscal Digital

Timbrar Con Timbre Fiscal Digital

Descripción de la Operación

Esta operación permite timbrar un Comprobante Fiscal Digital por Internet (CFDI) en sus versiones 3.3 o 4.0 y, a diferencia de la operación timbrar, 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í).
xmlCFDI string Contenido del documento XML del CFDI v3.3 o v4.0 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 utilizando el servicio Web.

⚠️
El NoCertificado se puede encontrar en la FIEL como NO. Serie

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

 /// <summary>
 /// Genera un XML de ejemplo para un CFDI 4.0.
 /// En una aplicación real, este XML se construiría dinámicamente.
 /// </summary>
 /// <returns>Un string con el contenido del CFDI.</returns>
 private static string GenerarCfdiParaTimbrar() => """ 
      <?xml version="1.0" encoding="UTF-8"?>
      <cfdi:Comprobante
          xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 cfdv40.xsd" 
          Version="4.0"
          Serie="F"
          Folio="123"
          Fecha="2025-07-02T12:00:00"
          SubTotal="100.00"
          TipoCambio="1.0"
          Serie="A"
          Moneda="MXN"
          Descuento="0.00"
          Total="116.00"
          TipoDeComprobante="I"
          Exportacion="01"
          MetodoPago="PUE"
          CondicionesDePago="CONDICIONES"
          FormaPago="01"
          Confirmacion="A1234"
          NoCertificado="30001000000300023708"
          Certificado=""
          Sello=""
          LugarExpedicion="64000">
          <cfdi:InformacionGlobal Meses="18" Año="2025" Periodicidad="05" />
          <cfdi:CfdiRelacionados TipoRelacion="09">
             <cfdi:CfdiRelacionado UUID="ED1752FE-E865-4FF2-BFE1-0F552E770DC9" />
          </cfdi:CfdiRelacionados>
          <cfdi:Emisor FacAtrAdquirente="0123456789" Rfc="ABC010101XYZ" Nombre="Empresa Ejemplo" RegimenFiscal="601"/>
          <cfdi:Receptor Rfc="XAXX010101000" Nombre="Publico en General" 
                        DomicilioFiscalReceptor="64000" RegimenFiscalReceptor="616" 
                        ResidenciaFiscal="MEX"
                        UsoCFDI="G03"/>
          <cfdi:Conceptos>
              <cfdi:Concepto ObjetoImp="01" ClaveProdServ="01010101" ClaveUnidad="C81" 
                    NoIdentificacion="00001" Cantidad="1.5" 
                      Unidad="TONELADA" Descripcion="ACERO" ValorUnitario="1500000" Importe="2250000">
                  <cfdi:Impuestos>
                      <cfdi:Traslados>
                          <cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" 
                                       TasaOCuota="0.160000" Importe="16.00"/>
                      </cfdi:Traslados>
                      <cfdi:Retenciones>
                         <cfdi:Retencion Base="2250000" Impuesto="001" TipoFactor="Tasa" TasaOCuota="0.300000" Importe="247500"/>
                      </cfdi:Retenciones>
                  </cfdi:Impuestos>
                  <cfdi:CuentaPredial Numero="51888"/>
              </cfdi:Concepto>
          </cfdi:Conceptos>
          <cfdi:Impuestos TotalImpuestosRetenidos="247500"      
                          TotalImpuestosTrasladados="360000">
              <cfdi:Retenciones>
                <cfdi:Retencion Impuesto="001" Importe="247000"/>
                <cfdi:Retencion Impuesto="003" Importe="500"/>
             </cfdi:Retenciones>
             <cfdi:Traslados>
                   <cfdi:Traslado Base="1.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="1.600000" Importe="360000"/>
             </cfdi:Traslados>
          </cfdi:Impuestos>
          <cfdi:Complemento></cfdi:Complemento>
      </cfdi:Comprobante>
      """;

 /// <summary>
 /// Invoca al servicio web para obtener solo el TFD de un CFDI de forma asíncrona.
 /// </summary>
 /// <param name="apiKey">La credencial de acceso al servicio.</param>
 /// <param name="xmlCfdi">El contenido del CFDI a timbrar.</param>
 /// <returns>Un objeto RespuestaTimbradoTFD con el resultado de la operación.</returns>
 public async Task<RespuestaTimbradoTFD> TimbrarTFDAsync(string apiKey, string xmlCfdi)
  {
      using var client = new ServicioTimbradoWSPortTypeClient("ServicioTimbradoWSPort");
      
      try
      {            
          // La operación se llama 'timbrarTFD' en el servicio web
          var response = await client.timbrarTFDAsync(apiKey, xmlCfdi);
                      
          return new RespuestaTimbradoTFD
          {
              Code = response.code,
              Message = response.message,
              Data = response.data // Contiene solo el XML del TFD
          };
      }
      catch (FaultException<RespuestaTimbradoTFD> ex)
      {
          _logger.LogError(ex, "Error SOAP: {Code} - {Message}", ex.Detail.code, ex.Detail.message);
          throw;
      }
      catch (CommunicationException ex)
      {
          _logger.LogError(ex, "Error de comunicación con el servicio");
          throw;
      }
      catch (TimeoutException ex)
      {
          _logger.LogError(ex, "Timeout en la comunicación");
          throw;
      }
  }

 /// <summary>
 /// Define la estructura del objeto de respuesta para mayor claridad.
 /// </summary>
 public class RespuestaTimbradoTFD
 {
    public string? Code { get; set; }
    public string? Message { get; set; }
    public string? Data { get; set; }
 }

 // Ejemplo de uso de TimbrarTFDAsync
 public async Task EjemploUsoTimbrarTFDAsync()
 {
     string apiKey = "TU_API_KEY_AQUI";
     string xmlCfdi = GenerarCfdiParaTimbrar();

     try
     {
         var resultado = await TimbrarTFDAsync(apiKey, xmlCfdi);
         if (resultado.Code == "200")
         {
             Console.WriteLine("¡Timbrado para TFD Exitoso!");
             Console.WriteLine("Mensaje: " + resultado.Message);
             Console.WriteLine("--- XML del Timbre Fiscal Digital (TFD) ---");
             Console.WriteLine(resultado.Data);
         }
         else
         {
             Console.WriteLine("Error durante el timbrado TFD:");
             Console.WriteLine("Código: " + resultado.Code);
             Console.WriteLine("Mensaje: " + resultado.Message);
         }
     }
     catch (Exception ex)
     {
         Console.WriteLine("Ocurrió una excepción: " + ex.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

// --- Archivo: RespuestaTimbradoTFD.java ---
package com.facturaloplus.cliente;

// POJO para encapsular la respuesta de la operación timbrarTFD.
public class RespuestaTimbradoTFD {
  private String code;
  private String message;
  private String data; // Contendrá el XML del TFD

  // Getters y Setters
  public String getCode() { return code; }
  public void setCode(String code) { this.code = code; }
  public String getMessage() { return message; }
  public void setMessage(String message) { this.message = message; }
  public String getData() { return data; }
  public void setData(String data) { this.data = data; }
}

// --- Archivo: TimbradoService.java ---
package com.facturaloplus.cliente;

import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TimbradoService {

  private static final Logger logger = Logger.getLogger(TimbradoService.class.getName());
  private final ExecutorService executor = Executors.newFixedThreadPool(5);

  public String generarXmlCfdiEjemplo() {
      String fechaActual = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
      return """
          <?xml version="1.0" encoding="UTF-8"?>
      <cfdi:Comprobante
          xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 cfdv40.xsd" 
          Version="4.0"
          Serie="F"
          Folio="123"
          Fecha="%s"
          SubTotal="100.00"
          TipoCambio="1.0"
          Serie="A"
          Moneda="MXN"
          Descuento="0.00"
          Total="116.00"
          TipoDeComprobante="I"
          Exportacion="01"
          MetodoPago="PUE"
          CondicionesDePago="CONDICIONES"
          FormaPago="01"
          Confirmacion="A1234"
          NoCertificado="30001000000300023708"
          Certificado=""
          Sello=""
          LugarExpedicion="64000">
          <cfdi:InformacionGlobal Meses="18" Año="2025" Periodicidad="05" />
          <cfdi:CfdiRelacionados TipoRelacion="09">
             <cfdi:CfdiRelacionado UUID="ED1752FE-E865-4FF2-BFE1-0F552E770DC9" />
          </cfdi:CfdiRelacionados>
          <cfdi:Emisor FacAtrAdquirente="0123456789" Rfc="ABC010101XYZ" Nombre="Empresa Ejemplo" RegimenFiscal="601"/>
          <cfdi:Receptor Rfc="XAXX010101000" Nombre="Publico en General" 
                        DomicilioFiscalReceptor="64000" RegimenFiscalReceptor="616" 
                        ResidenciaFiscal="MEX"
                        UsoCFDI="G03"/>
          <cfdi:Conceptos>
              <cfdi:Concepto ObjetoImp="01" ClaveProdServ="01010101" ClaveUnidad="C81" 
                    NoIdentificacion="00001" Cantidad="1.5" 
                      Unidad="TONELADA" Descripcion="ACERO" ValorUnitario="1500000" Importe="2250000">
                  <cfdi:Impuestos>
                      <cfdi:Traslados>
                          <cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" 
                                       TasaOCuota="0.160000" Importe="16.00"/>
                      </cfdi:Traslados>
                      <cfdi:Retenciones>
                         <cfdi:Retencion Base="2250000" Impuesto="001" TipoFactor="Tasa" TasaOCuota="0.300000" Importe="247500"/>
                      </cfdi:Retenciones>
                  </cfdi:Impuestos>
                  <cfdi:CuentaPredial Numero="51888"/>
              </cfdi:Concepto>
          </cfdi:Conceptos>
          <cfdi:Impuestos TotalImpuestosRetenidos="247500"      
                          TotalImpuestosTrasladados="360000">
              <cfdi:Retenciones>
                <cfdi:Retencion Impuesto="001" Importe="247000"/>
                <cfdi:Retencion Impuesto="003" Importe="500"/>
             </cfdi:Retenciones>
             <cfdi:Traslados>
                   <cfdi:Traslado Base="1.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="1.600000" Importe="360000"/>
             </cfdi:Traslados>
          </cfdi:Impuestos>
          <cfdi:Complemento></cfdi:Complemento>
      </cfdi:Comprobante>
          """.formatted(fechaActual);
  }

  public CompletableFuture<RespuestaTimbradoTFD> timbrarTFDAsync(String apiKey, String xmlCfdi) {
      return CompletableFuture.supplyAsync(() -> {
          try {
              ServicioTimbradoWS service = new ServicioTimbradoWS();
              ServicioTimbradoWSPortType port = service.getServicioTimbradoWSPort();

              logger.info("Iniciando timbrado para obtener TFD.");
              // La clase 'Respuesta' es generada por wsimport
              Respuesta response = port.timbrarTFD(apiKey, xmlCfdi); 

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

          } catch (SOAPFaultException ex) {
              logger.log(Level.SEVERE, "Error SOAP: " + ex.getFault().getFaultString(), ex);
              throw new RuntimeException("Error del servicio: " + ex.getFault().getFaultString(), ex);
          } catch (WebServiceException ex) {
              logger.log(Level.SEVERE, "Error de comunicación con el servicio.", ex);
              throw new RuntimeException("Error de comunicación.", ex);
          }
      }, executor);
  }
  
  public void shutdown() {
      executor.shutdown();
  }
}

// --- Archivo: Main.java (Ejemplo de uso) ---
package com.facturaloplus.cliente;

import java.util.concurrent.CompletableFuture;

public class Main {
  public static void main(String[] args) {
      TimbradoService timbradoService = new TimbradoService();
      String miApiKey = "TU_API_KEY_AQUI";
      String xmlParaTimbrar = timbradoService.generarXmlCfdiEjemplo();

      CompletableFuture<RespuestaTimbradoTFD> future = timbradoService.timbrarTFDAsync(miApiKey, xmlParaTimbrar);

      future.whenComplete((resultado, ex) -> {
          if (ex != null) {
              System.err.println("\nOcurrió una excepción: " + ex.getMessage());
          } else {
              if ("200".equals(resultado.getCode())) {
                  System.out.println("\n¡Timbrado para TFD Exitoso!");
                  System.out.println("Mensaje: " + resultado.getMessage());
                  System.out.println("\n--- XML del Timbre Fiscal Digital (TFD) ---");
                  System.out.println(resultado.getData());
              } else {
                  System.err.println("\nError durante el timbrado TFD:");
                  System.err.println("Código: " + resultado.getCode());
                  System.err.println("Mensaje: " + resultado.getMessage());
              }
          }
          timbradoService.shutdown();
      });
      
      future.join(); 
  }
}

Herramienta Zeep

Instala la librería zeep usando pip:

pip install zeep

Implementación

# --- Archivo: timbrado_service.py ---
import asyncio
import logging
from datetime import datetime
from zeep import Client
from zeep.asyncio import AsyncClient
from zeep.exceptions import Fault, TransportError
from dataclasses import dataclass

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

WSDL_URL_DESARROLLO = "https://dev.facturaloplus.com/ws/servicio.do?wsdl"

@dataclass
class RespuestaTimbradoTFD:
  """Clase de datos para encapsular la respuesta del servicio timbrarTFD."""
  code: str = None
  message: str = None
  data: str = None # Contendrá el XML del TFD

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

  def generar_xml_cfdi_ejemplo(self) -> str:
      fecha_actual = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
      return f"""
<?xml version="1.0" encoding="UTF-8"?>
      <cfdi:Comprobante
          xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 cfdv40.xsd" 
          Version="4.0"
          Serie="F"
          Folio="123"
          Fecha="{fecha_actual}"
          SubTotal="100.00"
          TipoCambio="1.0"
          Serie="A"
          Moneda="MXN"
          Descuento="0.00"
          Total="116.00"
          TipoDeComprobante="I"
          Exportacion="01"
          MetodoPago="PUE"
          CondicionesDePago="CONDICIONES"
          FormaPago="01"
          Confirmacion="A1234"
          NoCertificado="30001000000300023708"
          Certificado=""
          Sello=""
          LugarExpedicion="64000">
          <cfdi:InformacionGlobal Meses="18" Año="2025" Periodicidad="05" />
          <cfdi:CfdiRelacionados TipoRelacion="09">
             <cfdi:CfdiRelacionado UUID="ED1752FE-E865-4FF2-BFE1-0F552E770DC9" />
          </cfdi:CfdiRelacionados>
          <cfdi:Emisor FacAtrAdquirente="0123456789" Rfc="ABC010101XYZ" Nombre="Empresa Ejemplo" RegimenFiscal="601"/>
          <cfdi:Receptor Rfc="XAXX010101000" Nombre="Publico en General" 
                        DomicilioFiscalReceptor="64000" RegimenFiscalReceptor="616" 
                        ResidenciaFiscal="MEX"
                        UsoCFDI="G03"/>
          <cfdi:Conceptos>
              <cfdi:Concepto ObjetoImp="01" ClaveProdServ="01010101" ClaveUnidad="C81" 
                    NoIdentificacion="00001" Cantidad="1.5" 
                      Unidad="TONELADA" Descripcion="ACERO" ValorUnitario="1500000" Importe="2250000">
                  <cfdi:Impuestos>
                      <cfdi:Traslados>
                          <cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" 
                                       TasaOCuota="0.160000" Importe="16.00"/>
                      </cfdi:Traslados>
                      <cfdi:Retenciones>
                         <cfdi:Retencion Base="2250000" Impuesto="001" TipoFactor="Tasa" TasaOCuota="0.300000" Importe="247500"/>
                      </cfdi:Retenciones>
                  </cfdi:Impuestos>
                  <cfdi:CuentaPredial Numero="51888"/>
              </cfdi:Concepto>
          </cfdi:Conceptos>
          <cfdi:Impuestos TotalImpuestosRetenidos="247500"      
                          TotalImpuestosTrasladados="360000">
              <cfdi:Retenciones>
                <cfdi:Retencion Impuesto="001" Importe="247000"/>
                <cfdi:Retencion Impuesto="003" Importe="500"/>
             </cfdi:Retenciones>
             <cfdi:Traslados>
                   <cfdi:Traslado Base="1.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="1.600000" Importe="360000"/>
             </cfdi:Traslados>
          </cfdi:Impuestos>
          <cfdi:Complemento></cfdi:Complemento>
      </cfdi:Comprobante>
"""

  async def timbrar_tfd_async(self, api_key: str, xml_cfdi: str) -> RespuestaTimbradoTFD:
      """Invoca al servicio web para obtener solo el TFD de un CFDI."""
      try:
          logging.info("Iniciando timbrado para obtener TFD.")
          response = await self.async_client.service.timbrarTFD(apikey=api_key, xmlCFDI=xml_cfdi)
          
          return RespuestaTimbradoTFD(
              code=response.code,
              message=response.message,
              data=response.data
          )
      except Fault as ex:
          logging.error(f"Error SOAP (Fault): {ex.message}")
          raise ConnectionError(f"Error del servicio: {ex.message}") from ex
      except TransportError as ex:
          logging.error(f"Error de comunicación: {ex}")
          raise

# --- Archivo: main.py (Ejemplo de uso) ---
async def main():
  service = TimbradoService(WSDL_URL_DESARROLLO)
  mi_api_key = "TU_API_KEY_AQUI"
  xml_para_timbrar = service.generar_xml_cfdi_ejemplo()

  try:
      resultado = await service.timbrar_tfd_async(mi_api_key, xml_para_timbrar)
      
      if resultado.code == "200":
          print("\033[92m\n¡Timbrado para TFD Exitoso!\033[0m")
          print(f"Mensaje: {resultado.message}")
          print("\n--- XML del Timbre Fiscal Digital (TFD) ---")
          print(resultado.data)
      else:
          print(f"\033[91m\nError durante el timbrado TFD:\033[0m")
          print(f"Código: {resultado.code}")
          print(f"Mensaje: {resultado.message}")

  except Exception as ex:
      print(f"\033[91m\nOcurrió una excepción: {ex}\033[0m")

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

Herramienta SoapClient

Asegúrate de que la extensión php-soap esté habilitada en tu php.ini.

Implementación

<?php
// --- Archivo: TimbradoTFD.php ---

ini_set('display_errors', 1);
error_reporting(E_ALL);

class RespuestaTimbradoTFD {
  public ?string $code = null;
  public ?string $message = null;
  public ?string $data = null; // Contendrá el XML del TFD
}

class TimbradoService {
  private string $wsdlUrl;

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

  public function generarXmlCfdiEjemplo(): string {
      $fechaActual = gmdate('Y-m-d\TH:i:s');
      return <<<XML
<?xml version="1.0" encoding="UTF-8"?>
      <cfdi:Comprobante
          xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 cfdv40.xsd" 
          Version="4.0"
          Serie="F"
          Folio="123"
          Fecha="{$fechaActual}"
          SubTotal="100.00"
          TipoCambio="1.0"
          Serie="A"
          Moneda="MXN"
          Descuento="0.00"
          Total="116.00"
          TipoDeComprobante="I"
          Exportacion="01"
          MetodoPago="PUE"
          CondicionesDePago="CONDICIONES"
          FormaPago="01"
          Confirmacion="A1234"
          NoCertificado="30001000000300023708"
          Certificado=""
          Sello=""
          LugarExpedicion="64000">
          <cfdi:InformacionGlobal Meses="18" Año="2025" Periodicidad="05" />
          <cfdi:CfdiRelacionados TipoRelacion="09">
             <cfdi:CfdiRelacionado UUID="ED1752FE-E865-4FF2-BFE1-0F552E770DC9" />
          </cfdi:CfdiRelacionados>
          <cfdi:Emisor FacAtrAdquirente="0123456789" Rfc="ABC010101XYZ" Nombre="Empresa Ejemplo" RegimenFiscal="601"/>
          <cfdi:Receptor Rfc="XAXX010101000" Nombre="Publico en General" 
                        DomicilioFiscalReceptor="64000" RegimenFiscalReceptor="616" 
                        ResidenciaFiscal="MEX"
                        UsoCFDI="G03"/>
          <cfdi:Conceptos>
              <cfdi:Concepto ObjetoImp="01" ClaveProdServ="01010101" ClaveUnidad="C81" 
                    NoIdentificacion="00001" Cantidad="1.5" 
                      Unidad="TONELADA" Descripcion="ACERO" ValorUnitario="1500000" Importe="2250000">
                  <cfdi:Impuestos>
                      <cfdi:Traslados>
                          <cfdi:Traslado Base="100.00" Impuesto="002" TipoFactor="Tasa" 
                                       TasaOCuota="0.160000" Importe="16.00"/>
                      </cfdi:Traslados>
                      <cfdi:Retenciones>
                         <cfdi:Retencion Base="2250000" Impuesto="001" TipoFactor="Tasa" TasaOCuota="0.300000" Importe="247500"/>
                      </cfdi:Retenciones>
                  </cfdi:Impuestos>
                  <cfdi:CuentaPredial Numero="51888"/>
              </cfdi:Concepto>
          </cfdi:Conceptos>
          <cfdi:Impuestos TotalImpuestosRetenidos="247500"      
                          TotalImpuestosTrasladados="360000">
              <cfdi:Retenciones>
                <cfdi:Retencion Impuesto="001" Importe="247000"/>
                <cfdi:Retencion Impuesto="003" Importe="500"/>
             </cfdi:Retenciones>
             <cfdi:Traslados>
                   <cfdi:Traslado Base="1.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="1.600000" Importe="360000"/>
             </cfdi:Traslados>
          </cfdi:Impuestos>
          <cfdi:Complemento></cfdi:Complemento>
      </cfdi:Comprobante>
XML;
  }

  public function timbrarTFD(string $apiKey, string $xmlCfdi): RespuestaTimbradoTFD {
      $respuesta = new RespuestaTimbradoTFD();
      
      try {
          $options = [
              'trace' => 1,
              'exceptions' => true,
              'cache_wsdl' => WSDL_CACHE_NONE
          ];

          $soapClient = new SoapClient($this->wsdlUrl, $options);
          
          $params = [
              'apikey'  => $apiKey,
              'xmlCFDI' => $xmlCfdi
          ];
          
          // Llamar a la operación 'timbrarTFD'
          $response = $soapClient->timbrarTFD($params);
          
          $respuesta->code = $response->return->code ?? null;
          $respuesta->message = $response->return->message ?? null;
          $respuesta->data = $response->return->data ?? null;

      } catch (SoapFault $e) {
          error_log("Error SOAP: " . $e->getMessage());
          $respuesta->code = "FAULT";
          $respuesta->message = "Error en la comunicación SOAP: " . $e->getMessage();
      }
      
      return $respuesta;
  }
}

// --- Archivo: index.php (Ejemplo de uso) ---
// require_once 'TimbradoTFD.php';

$wsdlDesarrollo = "https://dev.facturaloplus.com/ws/servicio.do?wsdl";
$timbradoService = new TimbradoService($wsdlDesarrollo);

$miApiKey = getenv('TIMBRADO_API_KEY') ?: "TU_API_KEY_AQUI";
$xmlParaTimbrar = $timbradoService->generarXmlCfdiEjemplo();

header('Content-Type: text/plain; charset=utf-8');

$resultado = $timbradoService->timbrarTFD($miApiKey, $xmlParaTimbrar);

if ($resultado->code === '200') {
  echo "¡Timbrado para TFD Exitoso!\n";
  echo "Mensaje: {$resultado->message}\n";
  echo "\n--- XML del Timbre Fiscal Digital (TFD) ---\n";
  echo $resultado->data;
} else {
  echo "Error durante el timbrado TFD:\n";
  echo "Código: {$resultado->code}\n";
  echo "Mensaje: {$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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:ws_api">
   <SOAP-ENV:Body>
      <ns1:timbrarTFDResponse xmlns:ns1="urn:ws_api">
         <return xsi:type="tns:RespuestaTimbrado">
            <code xsi:type="xsd:string">CÓDIGO</code>
            <message xsi:type="xsd:string">MENSAJE</message>
            <data xsi:type="xsd:string">
              <![CDATA[CFDI TIMBRADO]]>
            </data>
         </return>
      </ns1:timbrarTFDResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Códigos de respuesta

Los códigos de respuesta para el timbrado de CFDI 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 a través de la API.

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