El complemento permite incorporar la información relativa a los estímulos por la renovación del parque vehicular del autotransporte para el sector de autotransporte federal de carga, de pasajeros y de turismo, así como del servicio público de autotransporte de pasajeros urbano o suburbano, con el objeto de promover la sustitución de vehículos usados con los que se estuviera prestando dicho servicio, por unidades nuevas o seminuevas.
En el siguiente enlace encontrara el estándar y la documentación necesaria para generar el comprobante con complemento de Renovación y sustitución de vehículo.
| Nombre | Documento |
|---|---|
| Estándar del complemento | 📥 Descargar |
| Esquema XSD | 📥 Descargar |
| Utilería XSLT | 📥 Descargar |
| Catálogo | 📥 Descargar |
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
xmlns:decreto="http://www.sat.gob.mx/renovacionysustitucionvehiculos"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd http://www.sat.gob.mx/renovacionysustitucionvehiculos http://www.sat.gob.mx/sitio_internet/cfd/renovacionysustitucionvehiculos/renovacionysustitucionvehiculos.xsd" Certificado="MIIFsDCCA5igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTYwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTE0MzUxWhcNMjcwNTE4MTE0MzUxWjCB1zEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjElMCMGA1UELRMcRUtVOTAwMzE3M0M5IC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmecO6n2GS0zL025gbHGQVxznPDICoXzR2uUngz4DqxVUC/w9cE6FxSiXm2ap8Gcjg7wmcZfm85EBaxCx/0J2u5CqnhzIoGCdhBPuhWQnIh5TLgj/X6uNquwZkKChbNe9aeFirU/JbyN7Egia9oKH9KZUsodiM/pWAH00PCtoKJ9OBcSHMq8Rqa3KKoBcfkg1ZrgueffwRLws9yOcRWLb02sDOPzGIm/jEFicVYt2Hw1qdRE5xmTZ7AGG0UHs+unkGjpCVeJ+BEBn0JPLWVvDKHZAQMj6s5Bku35+d/MyATkpOPsGT/VTnsouxekDfikJD1f7A1ZpJbqDpkJnss3vQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFaUgj5PqgvJigNMgtrdXZnbPfVBbukAbW4OGnUhNrA7SRAAfv2BSGk16PI0nBOr7qF2mItmBnjgEwk+DTv8Zr7w5qp7vleC6dIsZFNJoa6ZndrE/f7KO1CYruLXr5gwEkIyGfJ9NwyIagvHHMszzyHiSZIA850fWtbqtythpAliJ2jF35M5pNS+YTkRB+T6L/c6m00ymN3q9lT1rB03YywxrLreRSFZOSrbwWfg34EJbHfbFXpCSVYdJRfiVdvHnewN0r5fUlPtR9stQHyuqewzdkyb5jTTw02D2cUfL57vlPStBj7SEi3uOWvLrsiDnnCIxRMYJ2UA2ktDKHk+zWnsDmaeleSzonv2CHW42yXYPCvWi88oE1DJNYLNkIjua7MxAnkNZbScNw01A6zbLsZ3y8G6eEYnxSTRfwjd8EP4kdiHNJftm7Z4iRU7HOVh79/lRWB+gd171s3d/mI9kte3MRy6V8MMEMCAnMboGpaooYwgAmwclI2XZCczNWXfhaWe0ZS5PmytD/GDpXzkX0oEgY9K/uYo5V77NdZbGAjmyi8cE2B2ogvyaN2XfIInrZPgEffJ4AB7kFA2mwesdLOCh0BLD9itmCve3A1FGR4+stO2ANUoiI3w3Tv2yQSg4bjeDlJ08lXaaFCLW2peEXMXjQUk7fmpb5MNuOUTW6BE=" CondicionesDePago="CONDICIONES" Exportacion="01" Fecha="2026-03-24T08:48:07" Folio="14789362" FormaPago="01" LugarExpedicion="78963" MetodoPago="PUE" Moneda="MXN" NoCertificado="30001000000500003416" Sello="hKVa1OrB5UiadL3OxRX/zeJ4bCZkyHxuy5MS0orZr/sM4ISUWlSkSfdW6aXp5rL0JQ21ssNf2bTIAKxezu+ehh+qAuLjlPyByHIMGqq5oPOyxxGB8dquXJetFr82FF3Bw5envN7MqglBGrjcpvdF622TmdQ4NZeoR37qcNz5J+UzKQRkOri3QerDFf7toqHPHEyvOmnRxN8RaTtKhYUReFfdXrijEOVydPbcxFlaxs3yathQQiChNXmKiISraP0/wlTMApMqoMs8hpoCx/gI8WB4ZzRDCwNDMyBZcsiJkrEhEwcH7IW3hrpAPGFVScZEHv1Z4iCauhe6dbwR2Fncxg==" Serie="V" SubTotal="50000.00" TipoDeComprobante="I" Total="58000.00" Version="4.0">
<cfdi:Emisor Nombre="ESCUELA KEMPER URGATE" RegimenFiscal="601" Rfc="EKU9003173C9"/>
<cfdi:Receptor DomicilioFiscalReceptor="86400" Nombre="FELIX MANUEL ANDRADE BALLADO" RegimenFiscalReceptor="616" Rfc="AABF800614HI0" UsoCFDI="S01"/>
<cfdi:Conceptos>
<cfdi:Concepto Cantidad="1.00" ClaveProdServ="25101503" ClaveUnidad="H87" Descripcion="AUTO" Importe="50000.00" NoIdentificacion="00001" ObjetoImp="02" Unidad="PIEZA" ValorUnitario="50000.00">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="50000.00" Importe="8000.00" Impuesto="002" TasaOCuota="0.160000" TipoFactor="Tasa"/>
</cfdi:Traslados>
</cfdi:Impuestos>
</cfdi:Concepto>
</cfdi:Conceptos>
<cfdi:Impuestos TotalImpuestosTrasladados="8000.00">
<cfdi:Traslados>
<cfdi:Traslado Base="50000.00" Importe="8000.00" Impuesto="002" TasaOCuota="0.160000" TipoFactor="Tasa"/>
</cfdi:Traslados>
</cfdi:Impuestos>
<cfdi:Complemento>
<decreto:renovacionysustitucionvehiculos TipoDeDecreto="01" Version="1.0">
<decreto:DecretoRenovVehicular VehEnaj="02">
<decreto:VehiculosUsadosEnajenadoPermAlFab PrecioVehUsado="10000.00" TipoVeh="01" Marca="FORD" TipooClase="MUSTANG" Año="1998" NumPlacas="JDA5529" NumFolTarjCir="346521984587656" Foliofiscal="444544DFO4645456" />
<decreto:VehiculoNuvoSemEnajenadoFabAlPerm Año="2011" Modelo="Nissan tsuru" NumPlacas="tkg-24-05" />
</decreto:DecretoRenovVehicular>
</decreto:renovacionysustitucionvehiculos>
</cfdi:Complemento>
</cfdi:Comprobante>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import base64
import hashlib
from datetime import datetime
from lxml import etree
import xml.etree.ElementTree as ET
from M2Crypto.RSA import load_key_string
class CFDI40Generator:
def __init__(self):
self.base_path = "/ruta/CrearCFDI4.0"
self.xml_dir = "/ruta/CrearCFDI4.0/Python/cfdi40 y compl/OtrosEjemplos/XML"
self.xml_path = os.path.join(self.xml_dir, "RenovacionVehiculos.xml")
self.cer_path = os.path.join(self.base_path, "CSD/EKU9003173C9.cer")
self.key_path = os.path.join(self.base_path, "CSD/EKU9003173C9Key.pem")
self.xslt_path = os.path.join(self.base_path, "XSLT/cadenaoriginal_4_0.xslt")
os.makedirs(self.xml_dir, exist_ok=True)
self.generar_xml()
self.cadena_original = self.generar_cadena_original()
self.sello = self.generar_sello(self.cadena_original)
self.agregar_sello()
# =====================================
# CERTIFICADO
# =====================================
def obtener_certificado(self):
with open(self.cer_path, "rb") as f:
cert_data = f.read()
cert_b64 = base64.b64encode(cert_data).decode()
os.system(f'openssl x509 -inform DER -in "{self.cer_path}" -noout -serial > serial.txt')
with open("serial.txt") as f:
serial = f.read()
no_certificado = serial[::2][4:]
return cert_b64, no_certificado
# =====================================
# GENERAR XML
# =====================================
def generar_xml(self):
certificado_base64, no_certificado = self.obtener_certificado()
ET.register_namespace('cfdi', "http://www.sat.gob.mx/cfd/4")
ET.register_namespace('decreto', "http://www.sat.gob.mx/renovacionysustitucionvehiculos")
namespace = {
'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
'xsi:schemaLocation':
"http://www.sat.gob.mx/cfd/4 "
"http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd "
"http://www.sat.gob.mx/renovacionysustitucionvehiculos "
"http://www.sat.gob.mx/sitio_internet/cfd/renovacionysustitucionvehiculos/renovacionysustitucionvehiculos.xsd"
}
Comprobante = ET.Element(
"{http://www.sat.gob.mx/cfd/4}Comprobante",
namespace,
Version="4.0",
Serie="A",
Folio="167ABC",
Fecha=datetime.now().replace(microsecond=0).isoformat('T'),
FormaPago="01",
CondicionesDePago="CONDICIONES",
SubTotal="150.00",
Moneda="MXN",
Total="174.00",
TipoDeComprobante="I",
MetodoPago="PUE",
Exportacion="01",
LugarExpedicion="58000",
NoCertificado=no_certificado,
Certificado=certificado_base64
)
# EMISOR
ET.SubElement(
Comprobante,
"{http://www.sat.gob.mx/cfd/4}Emisor",
Rfc="EKU9003173C9",
Nombre="ESCUELA KEMPER URGATE",
RegimenFiscal="601"
)
# RECEPTOR
ET.SubElement(
Comprobante,
"{http://www.sat.gob.mx/cfd/4}Receptor",
Rfc="CTE950627K46",
Nombre="COMERCIALIZADORA TEODORIKAS",
UsoCFDI="G03",
RegimenFiscalReceptor="601",
DomicilioFiscalReceptor="57740"
)
# CONCEPTOS
conceptos = ET.SubElement(Comprobante, "{http://www.sat.gob.mx/cfd/4}Conceptos")
concepto = ET.SubElement(
conceptos,
"{http://www.sat.gob.mx/cfd/4}Concepto",
ClaveProdServ="10101500",
NoIdentificacion="00001",
Cantidad="1.00",
ClaveUnidad="H87",
Unidad="PIEZA",
Descripcion="PIEZA DE ARTE",
ValorUnitario="150.00",
Importe="150.00",
ObjetoImp="02"
)
# IMPUESTOS CONCEPTO
impuestos_concepto = ET.SubElement(concepto, "{http://www.sat.gob.mx/cfd/4}Impuestos")
traslados_concepto = ET.SubElement(impuestos_concepto, "{http://www.sat.gob.mx/cfd/4}Traslados")
ET.SubElement(
traslados_concepto,
"{http://www.sat.gob.mx/cfd/4}Traslado",
Base="150.00",
Impuesto="002",
TipoFactor="Tasa",
TasaOCuota="0.160000",
Importe="24.00"
)
# IMPUESTOS COMPROBANTE
impuestos = ET.SubElement(
Comprobante,
"{http://www.sat.gob.mx/cfd/4}Impuestos",
TotalImpuestosTrasladados="24.00"
)
traslados = ET.SubElement(impuestos, "{http://www.sat.gob.mx/cfd/4}Traslados")
ET.SubElement(
traslados,
"{http://www.sat.gob.mx/cfd/4}Traslado",
Base="150.00",
Impuesto="002",
TipoFactor="Tasa",
TasaOCuota="0.160000",
Importe="24.00"
)
# =================================
# COMPLEMENTO RENOVACION VEHICULOS
# =================================
complemento = ET.SubElement(
Comprobante,
"{http://www.sat.gob.mx/cfd/4}Complemento"
)
renovacion = ET.SubElement(
complemento,
"{http://www.sat.gob.mx/renovacionysustitucionvehiculos}renovacionysustitucionvehiculos",
TipoDeDecreto="01",
Version="1.0"
)
decreto = ET.SubElement(
renovacion,
"{http://www.sat.gob.mx/renovacionysustitucionvehiculos}DecretoRenovVehicular",
VehEnaj="01"
)
ET.SubElement(
decreto,
"{http://www.sat.gob.mx/renovacionysustitucionvehiculos}VehiculosUsadosEnajenadoPermAlFab",
Marca="Nissan",
TipooClase="Camioneta",
Año="2007",
Modelo="Frontier",
NumSerie="IGIYY2189E5138227",
NumMotor="0445542897692",
NIV="58683",
NumPlacas="354-NYH",
NumFolTarjCir="05413312114",
PrecioVehUsado="90000",
TipoVeh="01",
Foliofiscal="5E7D18FA-BD99-4152-BCDA-1E360A8FBF81"
)
ET.SubElement(
decreto,
"{http://www.sat.gob.mx/renovacionysustitucionvehiculos}VehiculoNuvoSemEnajenadoFabAlPerm",
Año="2014",
Modelo="Nissan tsuru",
NumPlacas="tkg-24-05"
)
tree = ET.ElementTree(Comprobante)
tree.write(self.xml_path, encoding="utf-8", xml_declaration=True)
print("XML generado:")
print(self.xml_path)
# =====================================
# CADENA ORIGINAL
# =====================================
def generar_cadena_original(self):
xml = etree.parse(self.xml_path)
xslt = etree.parse(self.xslt_path)
transform = etree.XSLT(xslt)
cadena = transform(xml)
cadena = str(cadena).replace("\n", "").strip()
print("Cadena original:")
print(cadena)
return cadena
# =====================================
# SELLO
# =====================================
def generar_sello(self, cadena):
with open(self.key_path) as f:
key = f.read()
rsa = load_key_string(key.encode())
digest = hashlib.sha256(cadena.encode()).digest()
firma = rsa.sign(digest, "sha256")
sello = base64.b64encode(firma).decode()
return sello
# =====================================
# INSERTAR SELLO
# =====================================
def agregar_sello(self):
tree = ET.parse(self.xml_path)
root = tree.getroot()
root.set("Sello", self.sello)
tree.write(self.xml_path, encoding="utf-8", xml_declaration=True)
print("XML sellado correctamente")
if __name__ == "__main__":
CFDI40Generator()
<?php
date_default_timezone_set("America/Mexico_City");
error_reporting(E_ALL & ~E_WARNING);
// ================= CONFIG =================
$config = [
"cer" => "/ruta/EKU9003173C9.cer",
"key" => "/ruta/EKU9003173C9Key.pem",
"xml" => "/ruta/decreto.xml",
"cadena"=> "/ruta/CadenaOriginal.txt",
"xslt" => "/ruta/cadenaoriginal_4_0.xslt"
];
// ================= FUNCIONES =================
function crearNodo($doc, $ns, $nombre, $attrs = [])
{
$nodo = $doc->createElementNS($ns, $nombre);
foreach ($attrs as $k => $v) {
$nodo->setAttribute($k, $v);
}
return $nodo;
}
function obtenerCert($ruta)
{
$raw = file_get_contents($ruta);
$pem = "-----BEGIN CERTIFICATE-----\n" .
chunk_split(base64_encode($raw), 64, "\n") .
"-----END CERTIFICATE-----\n";
$data = openssl_x509_parse($pem);
$serial = null;
if ($data && !empty($data['serialNumberHex'])) {
$hex = preg_replace('/\s+/', '', $data['serialNumberHex']);
$serial = strtoupper(hex2bin($hex));
}
return [
"cert" => base64_encode($raw),
"noCert" => $serial
];
}
// ================= CERT =================
$cert = obtenerCert($config["cer"]);
$ns = "http://www.sat.gob.mx/cfd/4";
$nsDec = "http://www.sat.gob.mx/renovacionysustitucionvehiculos";
// ================= XML =================
$xml = new DOMDocument("1.0", "UTF-8");
$xml->formatOutput = true;
// Comprobante
$comp = $xml->createElementNS($ns, "cfdi:Comprobante");
$xml->appendChild($comp);
// Namespaces
$comp->setAttribute("xmlns:cfdi", $ns);
$comp->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
$comp->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:decreto", $nsDec);
$comp->setAttribute("xsi:schemaLocation",
"$ns http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd " .
"$nsDec http://www.sat.gob.mx/sitio_internet/cfd/renovacionysustitucionvehiculos/renovacionysustitucionvehiculos.xsd"
);
// ================= ATRIBUTOS CFDI =================
$attrs = [
"Version"=>"4.0",
"Serie"=>"A",
"Folio"=>"167ABC",
"Fecha"=>date('Y-m-d\TH:i:s'),
"FormaPago"=>"01",
"CondicionesDePago"=>"CONDICIONES",
"SubTotal"=>"150.00",
"Moneda"=>"MXN",
"Total"=>"174.00",
"TipoDeComprobante"=>"I",
"MetodoPago"=>"PUE",
"Exportacion"=>"01",
"LugarExpedicion"=>"58000",
"Certificado"=>$cert["cert"]
];
foreach ($attrs as $k=>$v) $comp->setAttribute($k,$v);
if ($cert["noCert"]) $comp->setAttribute("NoCertificado",$cert["noCert"]);
// ================= EMISOR =================
$comp->appendChild(crearNodo($xml,$ns,"cfdi:Emisor",[
"Rfc"=>"EKU9003173C9",
"Nombre"=>"ESCUELA KEMPER URGATE",
"RegimenFiscal"=>"601"
]));
// ================= RECEPTOR =================
$comp->appendChild(crearNodo($xml,$ns,"cfdi:Receptor",[
"Rfc"=>"CTE950627K46",
"Nombre"=>"COMERCIALIZADORA TEODORIKAS",
"UsoCFDI"=>"G03",
"RegimenFiscalReceptor"=>"601",
"DomicilioFiscalReceptor"=>"57740"
]));
// ================= CONCEPTOS =================
$conceptos = crearNodo($xml,$ns,"cfdi:Conceptos");
$concepto = crearNodo($xml,$ns,"cfdi:Concepto",[
"ClaveProdServ"=>"10101500",
"NoIdentificacion"=>"00001",
"Cantidad"=>"1.00",
"ClaveUnidad"=>"H87",
"Unidad"=>"PIEZA",
"Descripcion"=>"PIEZA DE ARTE",
"ValorUnitario"=>"150.00",
"Importe"=>"150.00",
"ObjetoImp"=>"02"
]);
$imp = crearNodo($xml,$ns,"cfdi:Impuestos");
$tras = crearNodo($xml,$ns,"cfdi:Traslados");
$tras->appendChild(crearNodo($xml,$ns,"cfdi:Traslado",[
"Base"=>"150.00",
"Impuesto"=>"002",
"TipoFactor"=>"Tasa",
"TasaOCuota"=>"0.160000",
"Importe"=>"24.00"
]));
$imp->appendChild($tras);
$concepto->appendChild($imp);
$conceptos->appendChild($concepto);
$comp->appendChild($conceptos);
// ================= IMPUESTOS GLOBALES =================
$impT = crearNodo($xml,$ns,"cfdi:Impuestos",[
"TotalImpuestosTrasladados"=>"24.00"
]);
$trasT = crearNodo($xml,$ns,"cfdi:Traslados");
$trasT->appendChild(crearNodo($xml,$ns,"cfdi:Traslado",[
"Base"=>"150.00",
"Impuesto"=>"002",
"TipoFactor"=>"Tasa",
"TasaOCuota"=>"0.160000",
"Importe"=>"24.00"
]));
$impT->appendChild($trasT);
$comp->appendChild($impT);
// ================= COMPLEMENTO =================
$complemento = $xml->createElementNS($ns, "cfdi:Complemento");
$comp->appendChild($complemento);
// Nodo principal decreto
$decreto = $xml->createElementNS($nsDec, "decreto:renovacionysustitucionvehiculos");
$decreto->setAttribute("Version","1.0");
$decreto->setAttribute("TipoDeDecreto","01");
$complemento->appendChild($decreto);
// Subnodo
$renov = $xml->createElementNS($nsDec, "decreto:DecretoRenovVehicular");
$renov->setAttribute("VehEnaj","01");
$decreto->appendChild($renov);
// Vehículo usado
$vehUsado = crearNodo($xml,$nsDec,"decreto:VehiculosUsadosEnajenadoPermAlFab",[
"Marca"=>"Nissan",
"TipooClase"=>"Camioneta",
"Año"=>"2007",
"Modelo"=>"Frontier",
"NumSerie"=>"IGIYY2189E5138227",
"NumMotor"=>"0445542897692",
"NIV"=>"58683",
"NumPlacas"=>"354-NYH",
"NumFolTarjCir"=>"05413312114",
"PrecioVehUsado"=>"90000",
"TipoVeh"=>"01",
"Foliofiscal"=>"5E7D18FA-BD99-4152-BCDA-1E360A8FBF81"
]);
$renov->appendChild($vehUsado);
// Vehículo nuevo
$vehNuevo = crearNodo($xml,$nsDec,"decreto:VehiculoNuvoSemEnajenadoFabAlPerm",[
"Año"=>"2014",
"Modelo"=>"Nissan tsuru",
"NumPlacas"=>"tkg-24-05"
]);
$renov->appendChild($vehNuevo);
// ================= CADENA =================
$xsl = new DOMDocument();
$xsl->load($config["xslt"]);
$proc = new XSLTProcessor();
$proc->importStylesheet($xsl);
$cadena = trim($proc->transformToXML($xml));
file_put_contents($config["cadena"], $cadena);
// ================= SELLO =================
$key = openssl_pkey_get_private(file_get_contents($config["key"]));
openssl_sign($cadena, $firma, $key, OPENSSL_ALGO_SHA256);
$comp->setAttribute("Sello", base64_encode($firma));
// ================= SAVE =================
$xml->save($config["xml"]);
echo "XML generado:\n{$config["xml"]}\n";
echo "Cadena:\n$cadena\n";