NAV Navbar
php ruby python javascript

Introducción

Bienvenido a Sifei Docs! Puedes utilizar nuestros servicios para timbrar, cancelar recuperar tus CFDI ademas de muchas mas otras funciones.

Contamos con ejemplos en Java, C# ,PHP,Ruby, Python, y JavaScript(nodeJs)! Puedes ver el coidigo de ejemplo en el lado derecho y cambiar al langueje de tu interes. Si necesitas algun ejemplo en algun otro lenguaje favor de solicitarlo.

Autenticacion

Para autenticar es necesario que adquieras tus credenciales de ambiente de pruebas.

Aqui hay que describir en una tabla o lista el nombre de usuario, pass y id de equipo

Ademas del proceso para obtener estas

Timbrado

Timbrado(getCFDI):

require 'json'
require 'savon'
require 'iniparse'
require 'time'
require "base64"
require 'zip'

config=IniParse.parse( File.read('config.ini') )

print config
print "hel"

usuario=config['timbrado']['UsuarioSIFEI']
password=config['timbrado']['PasswordSIFEI']
idEquipo=config['timbrado']['IdEquipoGenerado']
usuario=config['timbrado']['UsuarioSIFEI']

#file=File.open('xml_sellado.xml','r');
#cfdi_contenido=file.read
cfdi_contenido=File.read('assets/xml_sellado.xml')

#savon 2 no ofrece una manera directa de acceder al request por lo que deberas de utilizar algun interceptor
client = Savon.client(wsdl: 'http://devcfdi.sifei.com.mx:8080/SIFEI33/SIFEI?wsdl',
    #log: true
    convert_request_keys_to: :none,
    open_timeout: 300,
    read_timeout: 300,
    )
#print client.operations
begin
    #Establecemos el hash(dictionario) con los datos a utilizar!
    soapParams= { 
        Usuario: usuario,
        Password:password ,
        IdEquipo: idEquipo,
        archivoXMLZip: Base64.strict_encode64(cfdi_contenido), # 
        Serie:"" ,
    }

    #print soapParams
    #simulamos request para guardarlo y ver como se envia(omitir en produccion)
    operation=client.operation(:get_cfdi)
    request= operation.build(message:soapParams).pretty
    timestamp=Time.now.to_i.to_s    
    File.open("timbrado_request_#{timestamp}.xml" ,"w"){ |file|file.puts request}
    response = client.call(:get_cfdi, message:soapParams)
    # print response.to_xml
    File.open("timbrado_response_#{timestamp}.xml" ,"w"){ |file|file.puts response.to_xml}
    puts "Escribiendo zip"
    zipName="zip_timbrado_#{timestamp}.zip"
    #se decodifica el resultado de la respuesta(base64) y se escribe a un archivo zip que contiene el xml timbrado
    File.open(zipName ,"w"){ |file|file.puts Base64.strict_decode64(response.body[:get_cfdi_response][:return])}


    #una vez escrito el zip, debemos extraer el XML timbrado
    Zip::File.open(zipName) do |zipfile|
        zipfile.each do |file|
          # do something with file
          file.extract
        end
      end
rescue Savon::SOAPFault => e  
    #print client.request
    puts "Exception:"
    puts e.message    
    puts e.http.code    
    puts e.to_hash#[':fault']#[':faultcode']    
    File.open("timbrado_response_#{timestamp}.xml" ,"w"){ |file|file.puts e.http.body}

   # print e.backtrace

    #print e.as_json
end
 #Ejemplo de ws de timbrado getCFDI(). Para timbrado masivo(con miles de conceptos enviar a )
#Para usar este ejemplo debes instalar Zeep mediante pip
#Comando
#>pip install zeep

from zeep import Client #En este caso de utiliza la libreria Zeep para clientes SOAP. Puede utilizar la libreria de su eleccion
from zeep.exceptions import Fault
from zeep.plugins import HistoryPlugin # con este plugin puedes obtener el historico de peticiones y respuestas

import io
import configparser
from os import path
import os
import time;
from lxml import etree
from zipfile import ZipFile #necesario para extraer el zip donde viene el XML timbrado

#Para evitar colocar accesos directamente en codigo se lee de un archivo.
if not path.exists('config.ini'):
    print ("Archivo de configuracion no existe")
else:      
    config = configparser.ConfigParser()
    config.read('config.ini')

    usuario = config['timbrado']['UsuarioSIFEI']#"Usuario SIFEI"
    password =config['timbrado']['PasswordSIFEI']# "Password SIFEI"
    idEquipo =config['timbrado']['IdEquipoGenerado']# "ID de Equipo SIFEI"

    #directorio relativo a este script compatible con windows y linux(para luego generar el path para leer el XML sellado)
    xmlDIR=os.path.dirname(os.path.abspath(__file__))
    workDir=os.path.dirname(os.path.abspath(__file__))
    timestamp=str(time.time())
    try:
        #Para este ejmplo se envia a timbrar un XML ya sellado  desde una ruta:
        xml_path = os.path.join(xmlDIR,"assets/CFDI33_sellado.xml")
        zipPath = os.path.join(xmlDIR,"assets/CFDI33_timbrado.zip")
        destino_path = os.path.join(xmlDIR,"assets/")

        serie = "" #Serie vacia para usuarios de timbrado    

        #Se lee el archivo xml establecido en la variable xml_path
        file = open(xml_path, 'r')
        readxml = file.read()
        file.close()

        #Se transforma el xml en array de bytes
        xml_bytesArray = bytearray(readxml,'utf-8')
        #Se crea history para cliente SOAP
        history = HistoryPlugin()
        #Se establece el cliente SOAP mediante la clase 'Cliente' de Zeep
        client = Client(
            wsdl="http://devcfdi.sifei.com.mx:8080/SIFEI33/SIFEI?wsdl",
            plugins=[history]  #Plugin para guardar el request y response para la fase de intregracion(podras ver alli los errores arrojados)
        )
        #Se pasan los parametros requeridos para el metodo de timbrado getCFDI()
        result = client.service.getCFDI(usuario, password, xml_bytesArray, serie, idEquipo)
        #Se recibe el xml en caso de ser timbrado(viene dentro de un zip)
        if result != None:

            f = open(zipPath, 'wb')
            writer = io.BufferedWriter(f)
            writer.write(result)
            writer.close()
            #una vez escrito el zip, debemos extraer el XML timbrado
            with ZipFile(zipPath, 'r') as zipObj:
                zipObj.extractall(destino_path)

            print("Comprobante almacenado en su sistema.")

    except Fault as fault:
        #Se extrae el request en caso de excepcion
        detail_decoded = client.wsdl.types.deserialize(fault.detail[0])
    #    timbrado = detail_decoded.xml        #Para extraer xml timbrado en caso de utilizar el metodo getCFDISign()
        print(detail_decoded)
    finally:
         #En ambiente de pruebas mandamos el requets y response  a un archivo respecticamente para inspeccionarlos en caso de error, se asigna un timestamp para identificarlos:
        with open(os.path.join(workDir,"timbrado_request_"+timestamp+".xml"),'w', encoding="utf-8") as request:
            request.write(etree.tostring(history.last_sent["envelope"], encoding="unicode", pretty_print=True))
        with open(os.path.join(workDir,"timbrado_response_"+timestamp+".xml"),'w', encoding="utf-8") as request:
            request.write(etree.tostring(history.last_received["envelope"], encoding="unicode", pretty_print=True)) 



<?php
/**
 * Ejemplo de timbrado PHP del WS SOAP getCFDI() de SIFEI. * 
 * 
 * El CFDI(XML) debe estar sellado correctamente
 * Nota: Para simplificar los ejemplos todas las rutas son relativas y los datos se leen de un archivo config.ini, lo cual no debe de hacerse en un ambiente de produccion.
 * 
 */
$cfdiSelladoPath=__DIR__."/assets/cfdi.xml";//Ruta del xml sellado
$originalName=basename($cfdiSelladoPath);
#Se lee el contenido del XML
$xml = file_get_contents($cfdiSelladoPath);
$tmpDirName = "tmp"; //nombre del directorio temporal
//Para el atributo archivoXMLZip no es necesario aplicar el base64_encode ya que la misma libreria SoapClient lo hace, por eso la mandamos tal cual.

$array_ini =parse_ini_file(__DIR__."/config.ini");
$parametros=array(
    //accesos
    'Usuario'=>$array_ini['UsuarioSIFEI'], 
    'Password'=>$array_ini['PasswordSIFEI'],
    'IdEquipo'=>$array_ini['IdEquipoGenerado'],
    //archivo XML
    'archivoXMLZip'=>$xml,  
    'Serie'=>''

);

//url de pruebas
$wsdl ="http://devcfdi.sifei.com.mx:8080/SIFEI33/SIFEI?wsdl";
$client = new SoapClient($wsdl, 
    array(
        'soap_version' => SOAP_1_1,
        'trace' => true,
    )
); 

try {
    $res = $client->__soapCall('getCFDI', array($parametros ));
    $fileTmpZip = "timbrado.zip";  //nombre del zip
    //mandamos en un zip el xml timbrado en caso de exito
    file_put_contents( $fileTmpZip, $res->return);
    $zipXml = new ZipArchive();//En caso de not found ZipArchive, asegurate de tener instalada la extension

    if ($zipXml->open($fileTmpZip) === TRUE) {          
        $zipXml->extractTo( $tmpDirName );
        $zipXml->close();
    }


} catch (SoapFault $e) {
    #En caso de un error inspeccionar la excepcion:
    var_dump( $e->faultcode, $e->faultstring, $e->detail );
} finally{
    $time=time();
    #En ambiente de pruebas mandamos el requets y response  a un archivo respecticamente para inspeccionarlos en caso de error, se asigna un timestamp para identificarlos:
    //mandamos en un XML el mensaje soap del request


    $doc=new DOMDocument();
    $doc->loadXML($client->__getLastRequest());
    $doc->formatOutput=true;
    $doc->save(__DIR__."/workfiles/timbrado_getCFDI_request_{$time}.xml");

    //mandamos en un XML el response del xml timbrado

    $doc=new DOMDocument();
    $doc->loadXML($client->__getLastResponse());
    $doc->formatOutput=true;
    $doc->save(__DIR__."/workfiles/timbrado_getCFDI_response_{$time}.xml");
}
?>
/**
 * Ejemplo de timbrado PHP del WS SOAP getCFDI() de SIFEI. * 
 * 
 * El CFDI(XML) debe estar sellado correctamente
 * Nota: Para simplificar los ejemplos todas las rutas son relativas y los datos se leen de un archivo config.ini, lo cual no debe de hacerse en un ambiente de produccion.
 * Los datos de pruebas y produccion son distintos, deberas solicitar primero tus datos de pruebas y una vez finalizadas tus pruebas deberas reemplazar tus accesos y URL de Ws service.
 */
var soap = require('soap');
var fs = require('fs');
var ini = require('ini')
var unzipper= require('unzipper')
var path = require('path');


//cargamos los accesos desde un almacenamiento seguro, en este caso para facilitar el ejemplo se lee desde un archivo .ini
var config=ini.parse(fs.readFileSync('./config.ini','utf-8'));
var usuario=config.timbrado.UsuarioSIFEI;
var password=config.timbrado.PasswordSIFEI;
var idEquipo=config.timbrado.IdEquipoGenerado;



//leemos el XML desde un archivo local,nota, la library propuesta no detecta que tipo requerido para archivoXMLZip es un binario y no lo codifica a base 64 
//por lo que se debe de realizar
cfdi= fs.readFileSync('./assets/cfdi.xml','utf-8');
let cfdiBase64=( Buffer.from(cfdi,'utf-8')).toString('base64');
//console.log(cfdi)
//URL de pruebas
var url = 'http://devcfdi.sifei.com.mx:8080/SIFEI33/SIFEI?wsdl';

//preparamos los parametros de timbrado
var parametrosDeTimbrado = {
    Usuario: usuario,   //usuario de sifei
    Password:password , //contraseña
    IdEquipo: idEquipo, //id de equipo
    archivoXMLZip: cfdiBase64, //archivo CFDI
    Serie:"" ,
};
soap.createClient(url, function(err, client) {

   console.log(JSON.stringify(client.describe()))//con esta linea averguaguamos los metodos
    //invamos metodo de timbrado:
   client.getCFDI(parametrosDeTimbrado,function(err,res,rawSoapResponse,soapResponseHeader,rawSoapRequest){
        //#En ambiente de pruebas mandamos el requets y response  a un archivo respecticamente para inspeccionarlos en caso de error, se asigna un timestamp para identificarlos:
        let timestamp=(new Date()).getTime()

        fs.writeFileSync(`./tmp/timbrado_response_${timestamp}.xml`,rawSoapResponse)
        fs.writeFileSync(`./tmp/timbrado_request_${timestamp}.xml`,rawSoapRequest)

        //ahora continuamos con el flujo normal
        if(err){
           //ocurrio un error , se debe leer la excepcion de sifei para ver cual es el error en el XML            
           console.error("error")
           //mensaje de error
           console.error(err.message)
           //excepcion completa(es un JSON tranformado):
            /**
             * {
                    codigo: 'XX',
                    error: 'XXXX',
                    message: 'XXXX'
                }
             */
           console.error(err.root.Envelope.Body.Fault.detail.SifeiException)
        }else{
            console.debug("ok")
           //Podemos extraer el cfdi timbrado  (viene dentro de un zip) 
          // console.log(res)
            let zipP='./tmp/timbrado.zip';
            let timbradoDir=path.dirname(zipP).split(path.sep).pop()
            //la respuesta vieene en el return
            fs.writeFileSync(zipP,res.return,{
              encoding:'base64'  
            })  
            //ahora debemos leer el zip y extraer los archivos(solo es el XML timbrado)
            fs.createReadStream(zipP)
                .pipe(unzipper.Extract({
                    path:timbradoDir
                }))
            console.log("Archivos extraidos")
        }
   })
});

Solictud generada:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://MApeados/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
    <tns:getCFDI>
      <Usuario>TCM....</Usuario>
      <Password>123456..</Password>
      <IdEquipo>123dsa..</IdEquipo>
      <archivoXMLZip>PD94bWwgd..</archivoXMLZip>
      <Serie/>
    </tns:getCFDI>
  </env:Body>
</env:Envelope>

Respuesta retornada:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body>
<ns2:getCFDIResponse xmlns:ns2="http://MApeados/">
<return>UEsDBBQACAAIAI5...</return>
</ns2:getCFDIResponse></S:Body>
</S:Envelope>

Este metodo se encarga de timbrar un CFDI, por tanto es el metodo mas importante.

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body>
<ns2:getCFDIResponse xmlns:ns2="http://MApeados/">
<return>UEsDBBQACAAIAI5...</return>
</ns2:getCFDIResponse></S:Body>
</S:Envelope>

URL de timbrado

La siguiente es la URL de pruebas, con ella podras consumir y acceder a todos los metodos disponibles

http://devcfdi.sifei.com.mx:8080/SIFEI33/SIFEI

Parametros de la solicitud

A continuacion se muestran los parametros necesarios para consumir el ws

Parametro Tipo Descripcion
Usuario String Usuario sifei
Password String If setset to false, the result will include kittens that have already been adopted.
IdEquipo String If setset to false, the result will include kittens that have already been adopted.
archivoXMLZip Binario(base64) XML a timbrar
Serie Binario(base64) XML a timbrar

Excepción

Cuando se produce un error en el servicio la respuesta SOAP genera una excepcion llamada SifeiException:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>S:Server</faultcode>
<faultstring>Parámetros incompletos</faultstring>
<detail>
<ns2:SifeiException xmlns:ns2="http://service.sifei.cancelacion/">
<codigo>1003</codigo>
<detalle>Parámetros incompletos</detalle>
<error>Parámetros incompletos</error>
</ns2:SifeiException>
</detail>
</S:Fault>
</S:Body>
</S:Envelope>

Codigos de error

Es importante mencionar ...

Codigo de error Significado
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- The kitten requested is hidden for administrators only.
404 Not Found -- The specified kitten could not be found.
405 Method Not Allowed -- You tried to access a kitten with an invalid method.
406 Not Acceptable -- You requested a format that isn't json.
410 Gone -- The kitten requested has been removed from our servers.
418 I'm a teapot.
429 Too Many Requests -- You're requesting too many kittens! Slow down!
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.