The XML generation layer builds complete, unsigned NF-e/NFC-e XML documents according to the MOC 4.00 specification. After generation, the XML is signed with a digital certificate and sent to SEFAZ.
Files: xml-builder.ts, xml-utils.ts, complement.ts
buildInvoiceXml(data: InvoiceBuildData): { xml: string; accessKey: string }- State lookup — Convert UF to IBGE code via
state-codes.ts - Random code — Generate 8-digit
cNFfor the access key - Access key — Build 44-digit key via
AccessKey.build()with mod-11 check digit - Items loop — For each item:
- Call
buildIcmsXml()→ accumulate ICMS totals - Call
buildPisXml(),buildCofinsXml(),buildIpiXml()as needed - Build
<det nItem="N">with<prod>+<imposto>
- Call
- Assemble groups —
ide,emit,dest,det[],total,transp,pag,infAdic - Wrap —
<?xml?><NFe xmlns="..."><infNFe Id="NFe{accessKey}" versao="4.00">...</infNFe></NFe>
<?xml version="1.0" encoding="UTF-8"?>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe35..." versao="4.00">
<ide>...</ide> <!-- identification: model, series, number, dates -->
<emit> <!-- issuer -->
<enderEmit>...</enderEmit>
</emit>
<dest> <!-- recipient (optional for NFC-e) -->
<enderDest>...</enderDest>
</dest>
<det nItem="1"> <!-- item 1 -->
<prod>...</prod>
<imposto>
<ICMS><ICMS00>...</ICMS00></ICMS>
<PIS><PISAliq>...</PISAliq></PIS>
<COFINS><COFINSAliq>...</COFINSAliq></COFINS>
</imposto>
</det>
<total>
<ICMSTot>...</ICMSTot>
</total>
<transp>...</transp> <!-- transport -->
<pag>...</pag> <!-- payment -->
<infAdic>...</infAdic> <!-- additional info -->
</infNFe>
</NFe>The buildAddressFields() helper generates the common address tag sequence (xLgr, nro, xCpl, xBairro, cMun, xMun, UF, CEP, cPais, xPais) used by enderEmit, retirada, and entrega.
function buildAddressFields(a: {
street: string; number: string; complement?: string;
district: string; cityCode: string; cityName: string;
stateCode: string; zipCode?: string; includeCountry?: boolean;
}): string[]- NFref — Referenced invoices (NF-e, NFC-e, ECF, producer note)
- Withdrawal/Delivery — pickup/delivery addresses with TaxId
- autXML — Authorized XML access parties
- retTrib — Tax retention (PIS, COFINS, CSLL, IRRF retained values)
- infAdic — Additional info, contributor/fiscal observations, process references
Shared across the module (xml-builder, convert, complement, contingency, qrcode):
escapeXml(str) // Escape &, <, >, ", '
tag(name, attrs?, children?) // Build <name attr="v">children</name>
extractXmlTagValue(xml, tagName) // Regex extract tag text content- String children → escaped (
tag("xNome", {}, "Foo & Bar")→<xNome>Foo & Bar</xNome>) - Array children → concatenated raw (for nesting pre-built tags)
- No children → empty element (
<tag></tag>)
Post-authorization operations on signed XML:
attachProtocol(requestXml: string, responseXml: string): stringCombines the signed NF-e (<NFe>) with the SEFAZ protocol response (<protNFe>) into the canonical <nfeProc> wrapper:
<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00">
<NFe>...</NFe>
<protNFe versao="4.00">
<infProt>
<cStat>100</cStat> <!-- authorized -->
<nProt>135...</nProt> <!-- protocol number -->
...
</infProt>
</protNFe>
</nfeProc>- Matches
digValbetween request signature and response - Validates
cStatis inVALID_PROTOCOL_STATUSES(100, 150, etc.) - Throws on rejection (110, 205, 301, 302, 303) with descriptive error
Converts legacy SPED TXT format (pipe-delimited) to NF-e XML. Used for batch import from older systems.
- Supports layouts: LOCAL, LOCAL_V12, LOCAL_V13, SEBRAE
- Field definitions in
txt-structures.ts - Validation in
valid-txt.ts - Uses its own
xmlTag()andaddChild()helpers (simpler thantag())
xml-builder.ts re-exports tag and escapeXml from xml-utils.ts for backward compatibility (tests import { tag } from xml-builder).