from lxml import etree
# ================= NAMESPACES =================
NSMAP = {
"cfdi": "http://www.sat.gob.mx/cfd/4",
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
"notariospublicos": "http://www.sat.gob.mx/notariospublicos",
"tfd": "http://www.sat.gob.mx/TimbreFiscalDigital"
}
# ================= ROOT =================
comprobante = etree.Element("{http://www.sat.gob.mx/cfd/4}Comprobante", nsmap=NSMAP)
# ================= ATRIBUTOS =================
comprobante.set("Version", "4.0")
comprobante.set("Serie", "F")
comprobante.set("Folio", "679")
comprobante.set("Fecha", "2024-03-01T00:00:00")
comprobante.set("FormaPago", "01")
comprobante.set("MetodoPago", "PUE")
comprobante.set("LugarExpedicion", "22214")
comprobante.set("Moneda", "MXN")
comprobante.set("SubTotal", "50000.00")
comprobante.set("Total", "58000.00")
comprobante.set("TipoDeComprobante", "I")
comprobante.set("Exportacion", "01")
# Certificado / sello (ejemplo)
comprobante.set("NoCertificado", "30001000000500003416")
comprobante.set("Certificado", "MIIFsDCCA5igAwIBAgIU...")
comprobante.set("Sello", "M1HHmv8EgWU99nXEULpfeH1LTrNmJjTbi1xsYA//WI8S9...")
# SchemaLocation
comprobante.set("{http://www.w3.org/2001/XMLSchema-instance}schemaLocation",
"http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd "
"http://www.sat.gob.mx/notariospublicos http://www.sat.gob.mx/sitio_internet/cfd/notariospublicos/notariospublicos.xsd"
)
# ================= EMISOR =================
emisor = etree.SubElement(comprobante, "{http://www.sat.gob.mx/cfd/4}Emisor")
emisor.set("Rfc", "EKU9003173C9")
emisor.set("Nombre", "ESCUELA KEMPER URGATE")
emisor.set("RegimenFiscal", "601")
# ================= RECEPTOR =================
receptor = etree.SubElement(comprobante, "{http://www.sat.gob.mx/cfd/4}Receptor")
receptor.set("Rfc", "URE180429TM6")
receptor.set("Nombre", "UNIVERSIDAD ROBOTICA ESPAÑOLA")
receptor.set("DomicilioFiscalReceptor", "86991")
receptor.set("RegimenFiscalReceptor", "601")
receptor.set("UsoCFDI", "G01")
# ================= CONCEPTOS =================
conceptos = etree.SubElement(comprobante, "{http://www.sat.gob.mx/cfd/4}Conceptos")
concepto = etree.SubElement(conceptos, "{http://www.sat.gob.mx/cfd/4}Concepto")
concepto.set("Cantidad", "1")
concepto.set("ClaveProdServ", "10201801")
concepto.set("ClaveUnidad", "LM")
concepto.set("Descripcion", "TERRENO 10x8 MTS")
concepto.set("Importe", "50000.00")
concepto.set("NoIdentificacion", "02")
concepto.set("Unidad", "METRO LINEAL")
concepto.set("ValorUnitario", "50000.00")
concepto.set("ObjetoImp", "02")
# Impuestos concepto
impuestos_c = etree.SubElement(concepto, "{http://www.sat.gob.mx/cfd/4}Impuestos")
traslados_c = etree.SubElement(impuestos_c, "{http://www.sat.gob.mx/cfd/4}Traslados")
traslado_c = etree.SubElement(traslados_c, "{http://www.sat.gob.mx/cfd/4}Traslado")
traslado_c.set("Base", "50000.00")
traslado_c.set("Impuesto", "002")
traslado_c.set("TipoFactor", "Tasa")
traslado_c.set("TasaOCuota", "0.160000")
traslado_c.set("Importe", "8000.00")
# ================= IMPUESTOS =================
impuestos = etree.SubElement(comprobante, "{http://www.sat.gob.mx/cfd/4}Impuestos")
impuestos.set("TotalImpuestosTrasladados", "8000.00")
traslados = etree.SubElement(impuestos, "{http://www.sat.gob.mx/cfd/4}Traslados")
traslado = etree.SubElement(traslados, "{http://www.sat.gob.mx/cfd/4}Traslado")
traslado.set("Base", "50000.00")
traslado.set("Importe", "8000.00")
traslado.set("Impuesto", "002")
traslado.set("TasaOCuota", "0.160000")
traslado.set("TipoFactor", "Tasa")
# ================= COMPLEMENTO =================
complemento = etree.SubElement(comprobante, "{http://www.sat.gob.mx/cfd/4}Complemento")
# ===== NOTARIOS =====
notarios = etree.SubElement(complemento, "{http://www.sat.gob.mx/notariospublicos}NotariosPublicos")
notarios.set("Version", "1.0")
desc_inmuebles = etree.SubElement(notarios, "{http://www.sat.gob.mx/notariospublicos}DescInmuebles")
desc = etree.SubElement(desc_inmuebles, "{http://www.sat.gob.mx/notariospublicos}DescInmueble")
desc.set("TipoInmueble", "01")
desc.set("Calle", "8 De Julio")
desc.set("Municipio", "Zapopan")
desc.set("Estado", "01")
desc.set("Pais", "MEX")
desc.set("CodigoPostal", "45236")
datos_op = etree.SubElement(notarios, "{http://www.sat.gob.mx/notariospublicos}DatosOperacion")
datos_op.set("NumInstrumentoNotarial", "1234")
datos_op.set("FechaInstNotarial", "2022-05-10")
datos_op.set("MontoOperacion", "50000")
datos_op.set("Subtotal", "42000")
datos_op.set("IVA", "8000")
datos_notario = etree.SubElement(notarios, "{http://www.sat.gob.mx/notariospublicos}DatosNotario")
datos_notario.set("CURP", "XEXX010101HNEXXXA4")
datos_notario.set("NumNotaria", "123")
datos_notario.set("EntidadFederativa", "01")
enajenante = etree.SubElement(notarios, "{http://www.sat.gob.mx/notariospublicos}DatosEnajenante")
enajenante.set("CoproSocConyugalE", "Si")
enaj = etree.SubElement(enajenante, "{http://www.sat.gob.mx/notariospublicos}DatosUnEnajenante")
enaj.set("Nombre", "KARLA")
enaj.set("ApellidoPaterno", "FUENTE")
enaj.set("RFC", "FUNK671228PH6")
enaj.set("CURP", "XEXX010101HNEXXXA4")
adquiriente = etree.SubElement(notarios, "{http://www.sat.gob.mx/notariospublicos}DatosAdquiriente")
adquiriente.set("CoproSocConyugalE", "Si")
adq = etree.SubElement(adquiriente, "{http://www.sat.gob.mx/notariospublicos}DatosUnAdquiriente")
adq.set("Nombre", "INGRID XODAR JIMENEZ")
adq.set("RFC", "XOJI740919U48")
adq.set("CURP", "XEXX010101HNEXXXA4")
# ===== TIMBRE (ejemplo) =====
timbre = etree.SubElement(complemento, "{http://www.sat.gob.mx/TimbreFiscalDigital}TimbreFiscalDigital")
timbre.set("Version", "1.1")
timbre.set("UUID", "95400488-98b4-46d7-8081-bf1b41d52ffa")
timbre.set("FechaTimbrado", "2024-03-01T16:50:59")
timbre.set("RfcProvCertif", "SPR190613I52")
timbre.set("SelloCFD", "M1HHmv8EgWU99nXEULpfeH1LTrNmJjTbi1xsYA...")
timbre.set("NoCertificadoSAT", "30001000000500003456")
timbre.set("SelloSAT", "Hl1xNzJww0XdRPXDW4pyB4Bdi9tQRSAkU81A6zxdBIiN...")
timbre.set("{http://www.w3.org/2001/XMLSchema-instance}schemaLocation",
"http://www.sat.gob.mx/TimbreFiscalDigital "
"http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigitalv11.xsd"
)
# ================= GUARDAR =================
with open("cfdi_notarios.xml", "wb") as f:
f.write(etree.tostring(comprobante, pretty_print=True, xml_declaration=True, encoding="UTF-8"))
print("XML generado correctamente")
<?php
date_default_timezone_set("America/Mexico_City");
error_reporting(E_ALL);
// ================= RUTAS =================
$rutaCer = "/ruta/certificados/certificado.cer";
$rutaKey = "/ruta/certificados/llave.key.pem";
$rutaXML = "/ruta/xml/cfdi_notarios.xml";
$rutaCadena= "/ruta/xml/cadena.txt";
$rutaXSLT = "/ruta/xslt/cadenaoriginal_4_0.xslt";
// ================= CERTIFICADO =================
$certDer = file_get_contents($rutaCer);
$certificado = base64_encode($certDer);
// Convertir a PEM
$certPem = "-----BEGIN CERTIFICATE-----\n" .
chunk_split(base64_encode($certDer), 64, "\n") .
"-----END CERTIFICATE-----\n";
// Número de certificado
$parsed = openssl_x509_parse($certPem);
$noCertificado = $parsed['serialNumber'];
// ================= XML =================
$xml = new DOMDocument("1.0", "UTF-8");
$xml->formatOutput = true;
// Nodo raíz
$cfdi = $xml->createElementNS("http://www.sat.gob.mx/cfd/4", "cfdi:Comprobante");
$xml->appendChild($cfdi);
// Namespaces
$cfdi->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
$cfdi->setAttribute("xmlns:notariospublicos", "http://www.sat.gob.mx/notariospublicos");
// SchemaLocation
$cfdi->setAttribute("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/notariospublicos http://www.sat.gob.mx/sitio_internet/cfd/notariospublicos/notariospublicos.xsd"
);
// ================= ATRIBUTOS =================
$cfdi->setAttribute("Version", "4.0");
$cfdi->setAttribute("Serie", "A");
$cfdi->setAttribute("Folio", "167ABC");
$cfdi->setAttribute("Fecha", "2025-07-02T16:31:01");
$cfdi->setAttribute("SubTotal", "3500.00");
$cfdi->setAttribute("Total", "4060.00");
$cfdi->setAttribute("Moneda", "MXN");
$cfdi->setAttribute("TipoDeComprobante", "I");
$cfdi->setAttribute("MetodoPago", "PUE");
$cfdi->setAttribute("FormaPago", "01");
$cfdi->setAttribute("LugarExpedicion", "58160");
$cfdi->setAttribute("Exportacion", "01");
$cfdi->setAttribute("CondicionesDePago", "CONDICIONES");
$cfdi->setAttribute("NoCertificado", $noCertificado);
$cfdi->setAttribute("Certificado", $certificado);
// ================= EMISOR =================
$emisor = $xml->createElement("cfdi:Emisor");
$emisor->setAttribute("Rfc", "EKU9003173C9");
$emisor->setAttribute("Nombre", "ESCUELA KEMPER URGATE");
$emisor->setAttribute("RegimenFiscal", "601");
$cfdi->appendChild($emisor);
// ================= RECEPTOR =================
$receptor = $xml->createElement("cfdi:Receptor");
$receptor->setAttribute("Rfc", "CTE950627K46");
$receptor->setAttribute("Nombre", "COMERCIALIZADORA TEODORIKAS");
$receptor->setAttribute("UsoCFDI", "G03");
$receptor->setAttribute("DomicilioFiscalReceptor", "57740");
$receptor->setAttribute("RegimenFiscalReceptor", "601");
$cfdi->appendChild($receptor);
// ================= CONCEPTOS =================
$conceptos = $xml->createElement("cfdi:Conceptos");
$concepto = $xml->createElement("cfdi:Concepto");
$concepto->setAttribute("Cantidad", "1.00");
$concepto->setAttribute("ClaveProdServ", "80121703");
$concepto->setAttribute("ClaveUnidad", "E48");
$concepto->setAttribute("Descripcion", "TRAMITE DE ESCRITURACION");
$concepto->setAttribute("ValorUnitario", "3500.00");
$concepto->setAttribute("Importe", "3500.00");
$concepto->setAttribute("ObjetoImp", "02");
$concepto->setAttribute("Unidad", "SERVICIO");
// Impuestos concepto
$impuestos = $xml->createElement("cfdi:Impuestos");
$traslados = $xml->createElement("cfdi:Traslados");
$traslado = $xml->createElement("cfdi:Traslado");
$traslado->setAttribute("Base", "3500.00");
$traslado->setAttribute("Impuesto", "002");
$traslado->setAttribute("TipoFactor", "Tasa");
$traslado->setAttribute("TasaOCuota", "0.160000");
$traslado->setAttribute("Importe", "560.00");
$traslados->appendChild($traslado);
$impuestos->appendChild($traslados);
$concepto->appendChild($impuestos);
$conceptos->appendChild($concepto);
$cfdi->appendChild($conceptos);
// ================= IMPUESTOS =================
$imp = $xml->createElement("cfdi:Impuestos");
$imp->setAttribute("TotalImpuestosTrasladados", "560.00");
$trs = $xml->createElement("cfdi:Traslados");
$t = $xml->createElement("cfdi:Traslado");
$t->setAttribute("Base", "3500.00");
$t->setAttribute("Impuesto", "002");
$t->setAttribute("TipoFactor", "Tasa");
$t->setAttribute("TasaOCuota", "0.160000");
$t->setAttribute("Importe", "560.00");
$trs->appendChild($t);
$imp->appendChild($trs);
$cfdi->appendChild($imp);
// ================= COMPLEMENTO =================
$complemento = $xml->createElement("cfdi:Complemento");
$notarios = $xml->createElementNS("http://www.sat.gob.mx/notariospublicos", "notariospublicos:NotariosPublicos");
$notarios->setAttribute("Version", "1.0");
// DescInmuebles
$descInm = $xml->createElement("notariospublicos:DescInmuebles");
$inm = $xml->createElement("notariospublicos:DescInmueble");
$inm->setAttribute("Calle", "Camelinas");
$inm->setAttribute("NoExterior", "900");
$inm->setAttribute("NoInterior", "3A");
$inm->setAttribute("Colonia", "Centro");
$inm->setAttribute("Municipio", "Monterrey");
$inm->setAttribute("Estado", "05");
$inm->setAttribute("Pais", "MEX");
$inm->setAttribute("CodigoPostal", "64000");
$inm->setAttribute("TipoInmueble", "04");
$descInm->appendChild($inm);
$notarios->appendChild($descInm);
// DatosOperacion
$datosOp = $xml->createElement("notariospublicos:DatosOperacion");
$datosOp->setAttribute("NumInstrumentoNotarial", "456998");
$datosOp->setAttribute("FechaInstNotarial", "2018-06-10");
$datosOp->setAttribute("MontoOperacion", "4060.00");
$datosOp->setAttribute("Subtotal", "3500.00");
$datosOp->setAttribute("IVA", "560.00");
$notarios->appendChild($datosOp);
// DatosNotario
$datosNot = $xml->createElement("notariospublicos:DatosNotario");
$datosNot->setAttribute("CURP", "TICE700419HCLJND07");
$datosNot->setAttribute("NumNotaria", "321");
$datosNot->setAttribute("EntidadFederativa", "05");
$datosNot->setAttribute("Adscripcion", "Adscripcion del Notario");
$notarios->appendChild($datosNot);
// Enajenante
$enaj = $xml->createElement("notariospublicos:DatosEnajenante");
$enaj->setAttribute("CoproSocConyugalE", "No");
$un = $xml->createElement("notariospublicos:DatosUnEnajenante");
$un->setAttribute("Nombre", "Jose");
$un->setAttribute("ApellidoPaterno", "Hernandez");
$un->setAttribute("ApellidoMaterno", "Solis");
$un->setAttribute("RFC", "AAAA010101AAA");
$un->setAttribute("CURP", "AAAA010101HCLJND07");
$enaj->appendChild($un);
$notarios->appendChild($enaj);
// Adquiriente
$adq = $xml->createElement("notariospublicos:DatosAdquiriente");
$adq->setAttribute("CoproSocConyugalE", "Si");
$cop = $xml->createElement("notariospublicos:DatosAdquirientesCopSC");
$copItem = $xml->createElement("notariospublicos:DatosAdquirienteCopSC");
$copItem->setAttribute("Nombre", "Constructora ABC SA de CV");
$copItem->setAttribute("RFC", "AAA010101AAA");
$copItem->setAttribute("Porcentaje", "50.00");
$cop->appendChild($copItem);
$adq->appendChild($cop);
$notarios->appendChild($adq);
// Agregar complemento
$complemento->appendChild($notarios);
$cfdi->appendChild($complemento);
// ================= CADENA ORIGINAL =================
$xsl = new DOMDocument();
$xsl->load($rutaXSLT);
$proc = new XSLTProcessor();
$proc->importStylesheet($xsl);
$cadena = trim($proc->transformToXML($xml));
file_put_contents($rutaCadena, $cadena);
// ================= SELLO =================
$privKey = openssl_pkey_get_private(file_get_contents($rutaKey));
openssl_sign($cadena, $sello, $privKey, OPENSSL_ALGO_SHA256);
$cfdi->setAttribute("Sello", base64_encode($sello));
// ================= GUARDAR =================
$xml->save($rutaXML);
echo "CFDI Notarios generado correctamente\n";
?>