Case 01Fiscal e ERP
Steuer
Notas fiscais que chegam sozinhas e entram conferidas no Linx.
Plataforma fiscal multiempresa que capta toda NF-e, CT-e e NFS-e emitida contra os CNPJs do cliente, guarda XML e PDF prontos para consulta e lança a entrada conferida direto no banco do ERP Linx, dentro da rede da loja. O time fiscal para de redigitar nota e passa a tratar só as exceções que o sistema aponta.
- Cliente
- Redes de varejo de moda com ERP Linx · matriz e filiais
- Período
- 2024 — 2026
- Equipe
- Squad de 5 pessoas
- Papel
- Engenheiro full stack · da captura fiscal à entrada no Linx
- Stack
- Bun · Fastify · TypeScript · Zod · Prisma · MySQL
- Confidencialidade
- Dados de clientes preservados
Resultados em números
- 12 milnotas fiscais processadas por mêsNF-e, CT-e e NFS-e somando os clientes
- 90%das entradas no Linx sem digitaçãoo time fiscal atua só nas exceções
- 42tabelas do Linx integradas pelo gatewayentradas, itens, impostos, estoque, pedidos e cadastros
- 60 sentre verificações de saúde de cada túnel3 falhas seguidas disparam a reconexão automática
O contexto do negócio
- Cliente
- Redes de varejo de moda com ERP Linx · matriz e filiais
- Período
- 2024 — 2026
Uma rede de moda recebe nota fiscal o dia inteiro: mercadoria de fornecedores, romaneios vindos do centro de distribuição, frete de transportadoras e serviços contratados por cada filial. Cada documento precisa ser localizado, atribuído ao CNPJ certo, conferido contra o pedido de compra e lançado no Linx, que roda em SQL Server no servidor local de cada cliente. Quando essa entrada depende de alguém baixando XML e redigitando item por item, o estoque atrasa, o fechamento fiscal aperta e cada vínculo errado vira retrabalho contábil.
O desafio
O desafio tinha três frentes. A captura dependia de uma API paginada por cursor, e qualquer interrupção no meio de um período podia deixar notas para trás. O grupo tem matriz e filiais com acessos diferentes, então cada documento e cada tela precisavam respeitar a empresa certa. E o Linx não está na nuvem: fica atrás do firewall de cada rede, sem endereço público, e só aceita a entrada quando pedido, fornecedor, filial e impostos batem.
Captura que não perde nota
O provedor pagina por cursor dentro de uma janela de datas. Se a execução cai no meio e a janela avança, documentos somem sem ninguém perceber — e o primeiro sincronismo de um cliente cobre mais de um ano de notas.
Matriz, filiais e acessos distintos
Uma mesma rede tem dezenas de CNPJs, usuários que só enxergam a própria filial e perfis com permissões diferentes por tela. Um dado cruzando de uma empresa para outra seria inaceitável.
ERP dentro da rede do cliente
O banco do Linx roda on-premise. Alcançá-lo sem abrir porta para a internet e gravar a entrada sem duplicar nota nem deixar registro pela metade era a parte mais delicada.
O que entreguei
Dividi o produto em quatro serviços com fronteiras claras. O backend central em Bun e Fastify capta as notas por empresa, registra cada cabeçalho como pendente e publica o lote no RabbitMQ, com o n8n orquestrando agenda e consumo. O painel em Laravel concentra login, permissões e a operação fiscal. O gateway Linx valida e grava a entrada no banco do ERP, alcançado por um agente Windows em .NET 8 que mantém um túnel WireGuard aberto de dentro para fora.
Captura com retomada
NF-e, CT-e e NFS-e por CNPJ, com histórico desde janeiro do ano anterior e retomada exata do cursor após qualquer queda.
Entrada automática no Linx
Prévia validada, conferência nota × pedido item a item e gravação atômica pelas procedures do próprio Linx.
Steuer Connector
Agente Windows com instalador MSI, túnel WireGuard, heartbeat e reconexão automática — o ERP segue sem porta exposta.
XML e PDF sob demanda
XML arquivado por empresa no S3, download por URL assinada e DANFE gerado na hora, com acesso limitado ao tenant.
Como funciona
Siga o trabalho atravessando o sistema, etapa por etapa, ou clique em qualquer componente do quadro para ver o que ele faz.
arraste para ver o fluxo
Legenda
- Síncrono
- Assíncrono
- Canal seguro
- Integração externa
Descrição textual do fluxo
- De SEFAZ e prefeituras para Provedores fiscais — Distribuição DF-e (integração externa).
- De Provedores fiscais para Backend central — Cabeçalhos por cursor (integração externa).
- De Orquestrador para Backend central — Dispara captura (síncrono).
- De Backend central para Fila de importação — Publica o lote (assíncrono).
- De Backend central para Base fiscal — Registra pendência (síncrono).
- De Fila de importação para Base fiscal — Grava nota e itens (assíncrono).
- De Fila de importação para Arquivo de XML — Arquiva o XML (assíncrono).
- De Base fiscal para Painel Steuer — Consulta por empresa (síncrono).
- De Arquivo de XML para Painel Steuer — URL assinada (síncrono).
- De Painel Steuer para Gateway Linx — Prévia e confirmação (síncrono).
- De Gateway Linx para Steuer Connector — Túnel WireGuard (canal seguro).
- De Instalador para Steuer Connector — Provisiona (síncrono).
- De Steuer Connector para ERP Linx — Proxy restrito (canal seguro).
Percorra o fluxo
←→trocam de etapa
6 etapas
Do começo ao fim
Escolha uma etapa ou pressione Reproduzir para acompanhar o fluxo inteiro. Enquanto isso, o quadro destaca por onde o trabalho passa.
Componentes do sistema
Captura incremental por empresa
- O que resolve
- Trazer todas as notas emitidas contra cada CNPJ, inclusive o histórico, sem nunca pular uma página.
- Como funciona
- Para cada empresa ativa, o backend lê a última execução: se ela parou no meio, retoma com o mesmo cursor e a mesma janela; se terminou, monta a próxima janela mensal. No primeiro acesso, sincroniza desde janeiro do ano anterior, mês a mês, até alcançar o dia atual. Cada página grava cursor, período e totais, e o laço só termina quando a janela chega a hoje com a página vazia.
Bun · Fastify · Prisma · MySQL
Fila de importação e arquivo fiscal
- O que resolve
- Separar a descoberta das notas do trabalho pesado de baixar, arquivar e interpretar cada XML.
- Como funciona
- Os cabeçalhos entram como pendentes, com a chave de acesso única no banco, e o lote vai para uma fila durável no RabbitMQ com mensagens persistentes. O n8n consome a fila, o backend baixa o XML, arquiva no S3 por empresa e grava cabeçalho e itens conforme a situação — autorizada, cancelada, denegada ou em contingência. Uma falha marca a nota com erro e ela volta na execução seguinte.
RabbitMQ · n8n · AWS S3 · TypeScript
Painel fiscal multiempresa
- O que resolve
- Dar a cada pessoa do grupo a visão exata da sua empresa, com o nível de acesso que o seu papel exige.
- Como funciona
- Um escopo global do Laravel filtra cada consulta pela empresa do usuário logado, de modo que quem é de filial enxerga só a própria filial e a matriz enxerga o grupo. Permissões por menu definem se a pessoa não acessa, consulta ou opera cada tela. Ali ficam consulta e download de XML e PDF, DANFE, apuração, relatórios por CFOP e de entradas × saídas e a gestão de NFS-e.
Laravel · PHP 8.2 · MySQL
Prévia assinada da entrada
- O que resolve
- Mostrar ao operador exatamente o que vai entrar no Linx e impedir que isso mude até a confirmação.
- Como funciona
- O gateway resolve fornecedor, filial, natureza de operação, série e pedido de compra, localiza o romaneio de origem na loja ou no CD e compara quantidade e valor item a item, com tolerância de um centavo. A prévia volta com um snapshot assinado em HMAC-SHA256; na confirmação o hash é recalculado e qualquer alteração é recusada. Para NFS-e, 26 códigos de diagnóstico apontam o que falta no cadastro do Linx.
Fastify · Zod · Prisma · SQL Server
Gravação atômica e idempotente
- O que resolve
- Garantir que cada nota entre no ERP uma única vez, inteira ou não entre.
- Como funciona
- A confirmação adquire uma chave de idempotência em duas camadas — 5 minutos em memória e 30 minutos no banco — e checa se a nota já existe no Linx. Numa única transação, insere em ENTRADAS e executa LX_ORQUESTRA_ENTRADA, que gera romaneio e impostos. O código de transação sai da combinação entre tipo fiscal e origem: pedido novo, CD ou loja. Se a procedure falha, o rollback não deixa registro órfão.
TypeScript · Prisma · SQL Server · Linx
Steuer Connector
- O que resolve
- Ligar o banco do Linx à plataforma sem expor nada da rede do cliente.
- Como funciona
- Serviço Windows em .NET 8 com cinco estados explícitos. Ativa com um token de uso único, guarda a configuração cifrada com DPAPI, sobe o túnel WireGuard e um proxy TCP que só encaminha para o host e a porta do banco autorizados. Heartbeat a cada 60 s, reconexão após 3 falhas, watchdog de 2 minutos e backoff exponencial com jitter. Um painel local fala com o serviço por Named Pipe, e o MSI verifica o ambiente antes de liberar a ativação.
C# · .NET 8 · WireGuard · WiX
Decisões que sustentam o sistema
Log de execução, não cursor solto
Cada página capturada grava cursor, janela de datas e totais. Uma queda vira uma retomada exata, e o sincronismo histórico de um cliente novo avança mês a mês sem sobrecarregar o provedor nem perder documento.
Persistir antes de distribuir
Toda nota vira um registro pendente, com chave de acesso única, antes de entrar na fila. Se um consumidor cai, o estado está no banco e o reprocessamento não duplica nada — a fila acelera, mas nunca é a única cópia da verdade.
Prévia assinada, confirmação idempotente
Separar prévia e confirmação deixa o operador no controle, e o hash HMAC garante que o que ele aprovou é o que será gravado. A chave de idempotência protege contra duplo clique, timeout e reenvio: a segunda chamada recebe a mesma resposta, sem nova entrada.
Túnel de dentro para fora, proxy restrito
Nenhuma rede de varejo abriria porta para o ERP. O agente inicia o WireGuard com keepalive de 25 s, o hub só permite tráfego do peer para o host e a porta do banco, e as credenciais do banco nunca são enviadas ao agente.
Resultado para o negócio
O lançamento de uma compra no Linx caiu de cerca de 6 minutos para menos de 1 minuto por nota, sem redigitar nenhum item.
Nenhuma nota fica para trás: cada página capturada grava janela e cursor, e a execução seguinte retoma exatamente de onde a anterior parou.
Zero entrada duplicada ou pela metade no ERP: chave de idempotência, checagem de nota já lançada e uma única transação com as procedures do Linx.
Um novo cliente entra em produção no mesmo dia: o instalador prepara WireGuard, serviço e firewall, a ativação usa um código de uso único e o ERP continua sem porta aberta para a internet.
Meu papel
- Papel
- Engenheiro full stack · da captura fiscal à entrada no Linx
- Equipe
- Squad de 5 pessoas
Construí o Steuer de ponta a ponta, do primeiro serviço à versão que hoje processa milhares de notas por mês. Desenhei a captura incremental e o modelo de filas, escrevi o gateway que conversa direto com o banco do Linx — prévia, conferência, idempotência e as procedures de entrada —, levei o isolamento por empresa do painel até o middleware e criei o Steuer Connector, o agente que liga cada cliente à plataforma. Também conduzi a implantação e a operação em produção com cada rede.
Pelo que respondi
- Projetei a captura incremental em Bun, Fastify e Prisma: log de execução por empresa, janela mensal de datas e retomada pelo último cursor.
- Modelei a fila durável de importação no RabbitMQ e os fluxos do n8n que agendam a captura e consomem cada lote.
- Escrevi o gateway Linx: 27 rotas, 42 tabelas do ERP mapeadas, prévia assinada com HMAC e confirmação idempotente.
- Implementei o isolamento por empresa ponta a ponta: escopo global no Laravel, contexto por requisição no gateway e modo estrito nas rotas de escrita.
- Desenvolvi o Steuer Connector em C#/.NET 8: máquina de estados, túnel WireGuard, proxy TCP restrito e controle local por Named Pipe.
- Automatizei o build do instalador MSI no GitHub Actions, versionado a cada execução e publicado no S3; os serviços web rodam em Docker.
Stack por camada
Backend e gateway
- Bun
- Fastify
- TypeScript
- Zod
- Prisma
Dados e filas
- MySQL
- SQL Server
- RabbitMQ
- n8n
- AWS S3
Painel
- Laravel
- PHP 8.2
- Blade
Agente e entrega
- C#
- .NET 8
- WireGuard
- WiX
- GitHub Actions
- Docker
O que levo deste projeto
Quando o ERP mora na rede do cliente, a conectividade é parte do produto, não um detalhe de implantação. Tratá-la como software, com estados, testes e telemetria, evitou retrabalho em cada nova rede.
Estado explícito vence inferência: quando o agente passou a ser dono do próprio ciclo de vida, com máquina de estados e IPC, o falso “conectando” desapareceu e o suporte passou a ler o status real.