Executando verificação de segurança...
1

Como parsear NF-e e NFC-e em JavaScript (e por que é mais complexo do que parece)

Se você já tentou extrair dados de uma nota fiscal eletrônica brasileira
programaticamente, sabe que não é trivial. Neste artigo explico como
funciona o XML da NF-e, como fazer o parse e os problemas que você
vai encontrar no caminho.

O que é a NF-e / NFC-e

A Nota Fiscal Eletrônica (NF-e) e a Nota Fiscal do Consumidor Eletrônica
(NFC-e) são documentos fiscais emitidos em XML, definidos pela SEFAZ
federal. Cada cupom fiscal com QR Code contém uma URL que aponta para
o portal SEFAZ do estado emissor.

O QR Code de um cupom de supermercado em Pernambuco abre algo assim:

https://nfce.sefaz.pe.gov.br/nfce/consulta?p=26260308845439...

Ao acessar essa URL, o portal retorna um XML padronizado — o mesmo
schema para todos os 27 estados.

A estrutura do XML

O XML segue o padrão nacional NFe 4.0. As tags mais importantes:

<nfeProc>
  <NFe>
    <infNFe>
      <emit>
        <xNome>SUPERMERCADO EXEMPLO LTDA</xNome>
        <xFant>SuperExemplo</xFant>
        <CNPJ>12345678000199</CNPJ>
      </emit>
      <det nItem="1">
        <prod>
          <xProd>ARROZ BRANCO TIO JOAO 5KG</xProd>
          <cEAN>7896006716113</cEAN>
          <qCom>2.000</qCom>
          <uCom>KG</uCom>
          <vUnCom>4.9800</vUnCom>
          <vProd>9.96</vProd>
          <vDesc>0.00</vDesc>
        </prod>
      </det>
      <total>
        <ICMSTot>
          <vNF>187.43</vNF>
        </ICMSTot>
      </total>
    </infNFe>
  </NFe>
</nfeProc>
Parser sem dependências externas
Dá para extrair os dados com regex puro — sem XML parser, sem
dependências. O truque é capturar conteúdo entre tags com o flag s
(dotAll) do ES2018:


function extractTag(xml, tag) {
  const m = xml.match(new RegExp(`<${tag}(?:\\s[^>]*)?>(.*?)</${tag}>`, 's'))
  return m ? m[1].trim() : null
}

function parseItems(xml) {
  const items = []
  const detRegex = /<det\s+nItem="(\d+)">(.*?)<\/det>/gs
  let m

  while ((m = detRegex.exec(xml)) !== null) {
    const [, nItem, det] = m
    const prod = extractTag(det, 'prod') ?? ''

    const gross = parseFloat(extractTag(prod, 'vProd') ?? '0')
    const discount = parseFloat(extractTag(prod, 'vDesc') ?? '0')

    items.push({
      n: parseInt(nItem),
      name: extractTag(prod, 'xProd'),
      ean: extractTag(prod, 'cEAN'),
      qty: parseFloat(extractTag(prod, 'qCom') ?? '1'),
      unit: extractTag(prod, 'uCom'),
      unitPrice: parseFloat(extractTag(prod, 'vUnCom') ?? '0'),
      total: Math.max(0, gross - discount),
      discount,
    })
  }

  return items
}
Os problemas que você vai encontrar
1. Cada estado tem um portal diferente

A URL varia por estado. PE usa nfce.sefaz.pe.gov.br, SP usa
www.nfce.fazenda.sp.gov.br, etc. A chave de acesso (44 dígitos no
QR code) tem os dois primeiros dígitos como código UF — dá para
identificar o estado e construir a URL correta.

2. Portais lentos ou instáveis

A SEFAZ de alguns estados pode levar 5-10 segundos para responder,
ou estar fora em horários de pico. Timeout generoso (20s+) é necessário.

3. Headers HTTP específicos

Alguns portais retornam erro 403 sem um User-Agent realista:


const res = await fetch(url, {
  headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
    'Accept': 'text/html,application/xhtml+xml,application/xml',
    'Accept-Language': 'pt-BR,pt;q=0.9',
  }
})
4. XML vs HTML

Em horários de pico, alguns portais retornam uma página HTML de erro
em vez do XML. Vale verificar se o retorno começa com <?xml antes
de parsear.

Se não quiser manter isso você mesmo
Construí a NFParse API para resolver exatamente isso: você manda a URL
da NF-e, recebe JSON estruturado em segundos. Funciona com todos os
27 estados, tem plano gratuito de 100 req/mês e não usa Puppeteer nem
Playwright — parse direto do XML.

→ nfparse.com.br

Dúvidas sobre o parsing ou sobre o padrão XML da NF-e? Comenta aí.
Carregando publicação patrocinada...