Skip to content

Issuing an NFC-e

The NFC-e is the retail NF-e: same layout, same signature, same web services. The differences fit in a short list, and this guide is about them.

What changes compared to the NF-e

NF-e (55) NFC-e (65)
Recipient Mandatory Optional — the consumer may stay anonymous
Operation Internal, interstate or foreign Internal only (idDest = 1)
Direction Inbound or outbound Outbound only
End consumer Depends on the operation Always (indFinal = 1)
Departure date Allowed Forbidden
Billing group Allowed Forbidden
Recipient's state registration Allowed Forbidden
Services with ISSQN Allowed Forbidden
QR Code None Mandatory, in the infNFeSupl group

Validar checks all of those points when the model is nfe.ModeloNFCe.

The CSC

The Código de Segurança do Contribuinte is a shared secret between you and your state's SEFAZ. It never travels in the XML: it only enters the hash that authenticates the QR Code, proving the receipt was generated by whoever held the code.

You obtain it from the state SEFAZ portal, and you get two values: an identifier of up to six digits, which is public and appears in the QR Code, and the code itself.

csc := nfce.CSC{
    Id:     os.Getenv("GONFE_CSC_ID"),
    Codigo: os.Getenv("GONFE_CSC"),
}

The CSC is a secret

Whoever holds the CSC can forge QR Codes that pass the consumer's check. Guard it with the same care as the certificate password: out of the source code, out of version control, in a secrets vault.

Building the receipt

n := nfe.Nova(nfe.ModeloNFCe)

ide := &n.InfNFe.Ide
ide.NatOp = "VENDA AO CONSUMIDOR"
ide.Serie = 1
ide.NNF = 1
ide.DhEmi = tipos.AgoraEm(uf.RS.Fuso())
ide.CMunFG = 4314902
ide.TpAmb = nfe.Homologacao
ide.TpImp = nfe.DANFENFCe   // receipt print format
ide.IndFinal = "1"
ide.IndPres = nfe.PresencaPresencial

n.InfNFe.Emit = nfe.Emit{ /* … */ }

// No consumer identification: simply leave the dest group empty.
n.InfNFe.Det = []nfe.Det{ /* … */ }
n.InfNFe.Transp = nfe.Transp{ModFrete: nfe.SemFrete}
n.InfNFe.Pag = &nfe.Pag{DetPag: []nfe.DetPag{{
    TPag: nfe.PagamentoPIXDinamico,
    VPag: total,
}}}

When the consumer asks for their CPF on the receipt, fill in the recipient without a state registration:

n.InfNFe.Dest = &nfe.Dest{
    CPF:       "52998224725",
    IndIEDest: nfe.NaoContribuinte,
}

Order matters: prepare, QR Code, sign

The QR Code depends on the access key, and the infNFeSupl group that holds it enters the document before signing. The sequence is always this:

// 1. Preparar builds the access key.
if err := n.Preparar(); err != nil {
    return err
}

// 2. The QR Code uses that key and fills in infNFeSupl.
if err := nfce.PreencherSuplemento(n, nfce.Opcoes{CSC: csc}); err != nil {
    return err
}

// 3. Validar now requires infNFeSupl.
if err := n.Validar(); err != nil {
    return err
}

// 4. Only then sign.
documento, err := n.XML()
if err != nil {
    return err
}
assinada, err := xmldsig.Assinar(documento, "infNFe", cert)

Swapping steps 2 and 4 produces a receipt whose signature does not check out, because infNFeSupl would have been added after the digest was computed.

The QR Code

PreencherSuplemento produces two values:

fmt.Println(n.InfNFeSupl.QrCode)
// https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx?p=43260…|2|2|000001|f3a1…

fmt.Println(n.InfNFeSupl.UrlChave)
// https://www.sefaz.rs.gov.br/NFCE/NFCE-COM.aspx

The p parameter gathers, separated by vertical bars: the access key, the QR Code version (2), the environment, the CSC identifier, and the SHA-1 hash of the whole set concatenated with the CSC code. The code itself never appears in the URL.

To check a QR Code — your own, while validating the configuration, or one from a receipt you received:

if err := nfce.ConferirQRCode(n.InfNFeSupl.QrCode, csc.Codigo); err != nil {
    return err
}

Rendering the image

The library produces the QR Code text, not the image. That keeps the core free of graphics dependencies and lets you pick the rendering library you already use. With github.com/skip2/go-qrcode, for instance:

png, err := qrcode.Encode(n.InfNFeSupl.QrCode, qrcode.Medium, 256)

Endpoints by state

The library ships a table with the QR Code and lookup addresses for all 27 states. States change these more often than the web services, so check before production:

qr, _ := nfce.URLQRCode(uf.RS, nfe.Producao)
consulta, _ := nfce.URLConsulta(uf.RS, nfe.Producao)

If they differ from what your SEFAZ publishes, override them:

err := nfce.PreencherSuplemento(n, nfce.Opcoes{
    CSC:         csc,
    URLQRCode:   "https://endereco-correto/qrcode",
    URLConsulta: "https://endereco-correto/consulta",
})

A wrong address does not prevent the invoice from being authorised — it produces a receipt whose QR Code the consumer cannot look up.

Offline contingency

The NFC-e allows offline issuing (tpEmis = 9), where the receipt is printed on the spot and transmitted later. The QR Code for that mode uses a different set of parameters, with the issue date, the value and the DigestValue embedded.

PreencherSuplemento refuses invoices with tpEmis = 9 and says why, rather than silently generating a wrong QR Code. To build one, use the low-level function with the parameters in the order given by the current Technical Note:

qr := nfce.MontarQRCode(urlBase, []string{
    chaveAcesso,
    nfce.VersaoQRCode,
    string(ambiente),
    // … the remaining offline contingency parameters
    csc.Id,
}, csc.Codigo)

MontarQRCode handles joining with vertical bars and the hash; the order and content of the parameters are up to you, precisely because that part changes between versions of the Technical Note.

Complete example

exemplos/emitir-nfce builds a snack bar receipt under Simples Nacional, with two items and payment by PIX.

export GONFE_CERT=./certificado.pfx
export GONFE_SENHA=...
export GONFE_CSC_ID=000001
export GONFE_CSC=...
go run ./exemplos/emitir-nfce