PyNFe
PyNFe copied to clipboard
NT 2025.001 QR_CODE VERSÃO 3
Implementa uma nova versão de QR_CODE que elimina o uso de CSC.
Bom dia @juniortada, como está o andamento da implementação, se puder ajudar estou a disposição.
Implementei dessa forma e funcionou a emissão já no novo leiaute:
VERSAO_QRCODE_V3 = 3 class SerializacaoQrcode(object): """Gera e serializa o QR-Code da NFC-e (versão 3 – NT 2025.001)."""
def __init__(self, assinatura_provider=None):
"""
:param assinatura_provider: callable(campos: dict) -> str
Obrigatório em contingência (tpEmis=9). Não deve ser usado online.
"""
self.assinatura_provider = assinatura_provider
def _get_basicos(self, nfe):
ns = {"ns": NAMESPACE_NFE}
chave = nfe[0].attrib["Id"].replace("NFe", "")
tpamb = nfe.xpath("ns:infNFe/ns:ide/ns:tpAmb/text()", namespaces=ns)[0]
cuf = nfe.xpath("ns:infNFe/ns:ide/ns:cUF/text()", namespaces=ns)[0]
uf = [key for key, value in CODIGOS_ESTADOS.items() if value == cuf][0].upper()
dhEmi = nfe.xpath("ns:infNFe/ns:ide/ns:dhEmi/text()", namespaces=ns)[0]
total = nfe.xpath("ns:infNFe/ns:total/ns:ICMSTot/ns:vNF/text()", namespaces=ns)[0]
tpEmis = nfe.xpath("ns:infNFe/ns:ide/ns:tpEmis/text()", namespaces=ns)
tpEmis = tpEmis[0] if tpEmis else "1"
dest_cpf = nfe.xpath("ns:infNFe/ns:dest/ns:CPF/text()", namespaces=ns)
dest_cnpj = nfe.xpath("ns:infNFe/ns:dest/ns:CNPJ/text()", namespaces=ns)
cpf = dest_cpf[0] if dest_cpf else None
cnpj = dest_cnpj[0] if dest_cnpj else None
return chave, tpamb, uf, dhEmi, total, tpEmis, cpf, cnpj
def _extrai_dia(self, dhEmi: str) -> str:
# form. AAAA-MM-DDThh:mm:ssTZD -> captura DD
m = re.search(r"\d{4}-\d{2}-(\d{2})", str(dhEmi))
return m.group(1) if m else "01"
def _urls_por_uf(self, uf: str, tpamb: str):
"""
Retorna (base_qr, url_chave) conforme seu dicionário NFCE,
preservando o comportamento para homologação.
"""
if tpamb == "1": # Produção
base_qr = NFCE[uf]["HTTPS"] + NFCE[uf]["QR"]
url_chave = NFCE[uf]["HTTPS"] + NFCE[uf]["URL"]
else: # Homologação
# PB mantém caminho base e tratamos 'hom?p=' ao montar o ?p=
if uf == "PB":
base_qr = NFCE[uf]["QR"]
url_chave = NFCE[uf]["HOMOLOGACAO"]
else:
base_qr = NFCE[uf]["HOMOLOGACAO"] + NFCE[uf]["QR"]
url_chave = NFCE[uf]["HOMOLOGACAO"] + NFCE[uf]["URL"]
return base_qr.rstrip("?&"), url_chave
def gerar_qrcode(self, xml, return_qr=False):
"""
Gera e insere o QR-Code v3 no XML da NFC-e (grupo ZX01/ZX02/ZX03).
- Online: ?p=<chave>|3|<tpAmb>
- Contingência (tpEmis=9):
?p=<chave>|3|<tpAmb>|<dia>|<vNF>|<tp_idDest>|<idDest>|<assinatura>
"""
nfe = xml
chave, tpamb, uf, dhEmi, total, tpEmis, cpf, cnpj = self._get_basicos(nfe)
base_qr, url_chave = self._urls_por_uf(uf, tpamb)
if tpEmis == "9":
# OFFLINE: inclui destinatário (se houver) e assinatura obrigatória
dia = self._extrai_dia(dhEmi)
tp_idDest, idDest = ("", "")
if cpf:
tp_idDest, idDest = ("1", cpf)
elif cnpj:
tp_idDest, idDest = ("2", cnpj)
campos = {
"chave": chave,
"versao": str(VERSAO_QRCODE_V3),
"tpAmb": tpamb,
"dia": dia,
"vNF": total,
"tp_idDest": tp_idDest,
"idDest": idDest,
}
if not self.assinatura_provider:
raise ValueError("assinatura_provider é obrigatório para QR-Code v3 em contingência (tpEmis=9).")
assinatura = self.assinatura_provider(campos=campos)
p = f"{chave}|{VERSAO_QRCODE_V3}|{tpamb}|{dia}|{total}|{tp_idDest}|{idDest}|{assinatura}"
else:
# ONLINE: sem assinatura (é proibido informar)
p = f"{chave}|{VERSAO_QRCODE_V3}|{tpamb}"
# Montagem final da URL do QR:
# regra geral '?p=', exceção PB/HOMO: 'hom?p='
if uf == "PB" and tpamb != "1":
url_qr = f"{base_qr}hom?p={p}"
else:
url_qr = f"{base_qr}?p={p}"
# Atualiza <infNFeSupl>
info = etree.Element("infNFeSupl")
etree.SubElement(info, "qrCode").text = url_qr.strip()
etree.SubElement(info, "urlChave").text = url_chave
for old in nfe.xpath("infNFeSupl"):
nfe.remove(old)
nfe.insert(1, info)
return (nfe, url_qr.strip()) if return_qr else nfe
chamada passando apenas o xml: xml_com_qrcode = SerializacaoQrcode().gerar_qrcode(xml)