type: skill name: Fiscalzen SEFAZ description: "Integrações SEFAZ (NF-e/CT-e/MDF-e) no padrão FiscalZen: DistDFe, Manifestação e eventos — com certificados A1, SOAP, retry/backoff e persistência no produto." skillSlug: fiscalzen-sefaz phases: [E, V] generated: 2026-01-28 status: filled scaffoldVersion: "2.0.0"
🏗️ Contexto do Projeto (como o FiscalZen é estruturado)
- Monorepo com pnpm-workspace + Turborepo
- Código em TypeScript (ESM
"type": "module") - Backend: Fastify 4 (apps/api)
- Jobs: BullMQ (workers + scheduler)
- Persistência: Drizzle ORM + PostgreSQL
- Cache/queues: Redis
- Logging: Pino
- Pacote fiscal principal:
@fiscalzen/sefaz-client(NF-e/CT-e/MDF-e) - Parser fiscal:
@fiscalzen/xml-parser(usado pelo sefaz-client e api)
✅ Regra: alterações de SEFAZ geralmente tocam packages/ (cliente) e/ou apps/api/ (orquestração + persistência).
🎯 Objetivo (o que esta skill resolve)
Aplicar mudanças relacionadas a SEFAZ mantendo consistência com a codebase:
- Preferir abstrações internas (
@fiscalzen/sefaz-client) em vez de reimplementar SOAP/assinatura/certificado - Validar certificado e permissões no backend antes de chamar SEFAZ
- Respeitar retry/backoff e throttling
- Persistir eventos e atualizar documentos do jeito do produto
- Garantir validação (unit/integration + homolog)
🧭 Onde mexer (mapa rápido)
Cliente SEFAZ (packages/sefaz-client)
packages/sefaz-client/src/client.ts→ política de timeout/retry/backoff,https.Agent,dispose(), loggingpackages/sefaz-client/src/soap-client.ts+src/soap/envelope.ts→ SOAP + envelopepackages/sefaz-client/src/services/*→ implementações por domínio:nfe-distdfe.ts(DistDFe NF-e)cte-distdfe.ts,mdfe-distdfe.ts(DistDFe CT-e/MDF-e)manifestacao.ts(eventos do destinatário)event-query.ts(consulta eventos)cte-events.ts(eventos CT-e)
packages/sefaz-client/src/types.ts→ contratos + erros tipados (SefazError,TimeoutError,CertificadoError, etc.)packages/sefaz-client/src/certificate.ts→ A1 (PFX), cache/invalidação, validaçõespackages/sefaz-client/src/signature.ts→ digest/assinatura (helpers)
Produto (apps/api)
apps/api/src/modules/manifestacao/service.ts→ fluxo real de manifestação + persistênciaapps/api/src/modules/certificates/*→ validação de certificados (gate antes de SEFAZ)apps/api/src/modules/documents/*→ documento + status/manifestaçõesapps/api/src/jobs/queues.ts→ criação/agenda de jobs SEFAZapps/api/src/modules/nsu/*+packages/database/src/schema/nsu-control.ts→ controle de NSU/sync
✅ Regras obrigatórias (gates)
1) Sempre validar certificado antes de chamar SEFAZ (no produto)
Se a chamada vier do apps/api, não chame SEFAZ sem passar pelo gate:
- empresa existe
company.certificateecompany.certificatePasswordexistem- certificado não expirou
- (se aplicável) tenantId correto (multi-tenant)
2) Não vazar segredo (PFX/senha)
- nunca logar senha do certificado
- nunca logar buffer do PFX
- ao logar erro, sanitize campos sensíveis
3) TLS estrito
- não desabilitar validação TLS (
rejectUnauthorized)
4) Erros externos padronizados
No apps/api, problemas da SEFAZ devem virar erro de serviço externo (padrão do projeto), mantendo mensagem útil.
🔁 Retry/Backoff e Throttling (padrão FiscalZen)
Ponto único de política: packages/sefaz-client/src/client.ts
✅ Regra: se você precisar mudar retry/backoff/timeout, faça no client (não espalhe retry pelos services).
Throttling (ex.: cStat 656)
Recomendação de implementação consistente:
- tratar como erro recuperável
- aplicar retry com backoff exponencial + jitter
- evitar tempestade: preferir job/queue para processar em série por empresa/UF
📦 DistDFe (Golden Path) — como fazer no FiscalZen
Cenários típicos
- Consulta por último NSU (sync contínuo)
- Consulta por NSU específico
- Consulta por chave (pontual)
Padrão de implementação
- Validar parâmetros (CNPJ, chave, nsu/ultNSU)
- Montar request XML conforme tipo de consulta
- Enviar via
SoapClient/SefazClient(abstração interna) - Parsear retorno (cStat/xMotivo/ultNSU/maxNSU)
- Extrair
docZipe:- decodificar
- detectar schema
- derivar tipo/chave usando
@fiscalzen/xml-parser
- Retornar
DistDFeResponseconsistente - Se estiver no produto (apps/api): persistir e atualizar controle de NSU
Regras de robustez
nsueultNSUsempre como string com 15 dígitos (padStart(15,'0'))- docZip inválido não deve derrubar o lote inteiro (registrar erro e seguir)
- ao tratar throttling, preferir política central no client
🧾 Manifestação do Destinatário (Golden Path) — padrão do produto
Passo a passo (apps/api)
- obter empresa com certificado (gate)
- buscar documento por chave com tenantId
- montar
CertificadoA1no formato do@fiscalzen/sefaz-client - usar
env.SEFAZ_AMBIENTE - chamar o client do pacote
- se
sucesso=false: lançarExternalServiceError('SEFAZ', xMotivo) - persistir em
documentEvents+ atualizardocuments.manifestacao
Regras específicas
- eventos devem ser idempotentes na camada do produto (não duplicar o mesmo evento sem necessidade)
- manter rastreabilidade (documentId/chave/tipoEvento/protocolo/dataRegistro)
🧪 Validação (Fase V) — mínimo obrigatório
Unit / pacote (sefaz-client)
- validar
validateParams(CNPJ/chave) - validar montagem de request por ultNSU/nsu/chave
- validar parsing de resposta (cStat e campos)
- validar docZip: sucesso e falha isolada
Integration / produto (apps/api)
- empresa sem certificado → erro de validação
- certificado expirado → erro de validação
- documento inexistente → NotFound
- retorno
sucesso=falseda SEFAZ → ExternalServiceError - persistência: criou event + atualizou documento
Homologação
- testar ao menos:
- DistDFe por ultNSU
- Manifestação (um evento)
- cenário de throttling (se possível reproduzir)
🧰 Prompt templates (como invocar a skill)
Manifestação (produto)
“Aplique a skill
fiscalzen-sefazpara ajustar o fluxo de manifestação emapps/api.
Preserve o gate de certificado + multi-tenant e mapeie falhas SEFAZ para erro externo padrão. Atualize persistência emdocumentEventsedocuments.manifestacao.”
DistDFe (pacote)
“Aplique a skill
fiscalzen-sefazpara modificarpackages/sefaz-client/src/services/nfe-distdfe.ts.
Preserve validações, padStart(15) e parsing docZip com@fiscalzen/xml-parser. Se tratar 656, preferir política central no client.”