Skip to content

About

Biblioteca para emissão de Nota Fiscal de Serviços em Campinas

Resources

Code of conduct

Contributing

Security policy

Stars

16 stars

Watchers

2 watching

Forks

Repository files navigation

NFSe Campinas v3

Pacote de integração para NFSe Campinas no Padrão Nacional NFS-e / DPS v1.01, conforme implantação da Reforma Tributária do Consumo.

Aviso de Breaking Change

A versão 3.x não usa mais ABRASF 2.03, RPS, SOAP ou WSDL. A API pública passa a trabalhar com DPS e envio REST/JSON municipal.

Para continuar usando ABRASF 2.03, instale a linha anterior:

pnpm add @4success/nfse-campinas@^2

Para DSFNET legacy, use a linha 1.x.

Instalação

pnpm add @4success/nfse-campinas

Status dos Endpoints

Os novos endereços de Campinas são usados por padrão, incluindo cancelamento em produção. O guia oficial atualizado em 18/09/2026 informa a disponibilidade de produção desde 21/09/2026.

  • Base de homologação: https://preprod-nfseapi.ima.sp.gov.br/notafiscal-ws/nfse.
  • Base de produção: https://nfseapi.campinas.sp.gov.br/notafiscal-ws/nfse.
Operação Método e rota
Envio de DPS POST {base}
Consulta por DPS GET {base}/dps/{idDps}
Consulta de NFSe GET {base}/{chaveAcesso}
Cancelamento POST {base}/{chaveAcesso}/eventos

Para configurar endereços próprios, use endpoints.dps, endpoints.consulta, endpoints.consultaDps e endpoints.eventos. A consulta por DPS prioriza consultaDps; se ausente, reutiliza um dps explicitamente configurado para preservar integrações antigas. Sem esses overrides, usa a constante de consulta por DPS do ambiente. Ao sobrescrever o envio com o novo endereço terminado em /nfse, informe também consultaDps terminado em /nfse/dps.

O SDK desabilita redirecionamentos HTTP (maxRedirects: 0). Um 302 gera erro imediato com o status e o cabeçalho Location preservados, evitando que o redirecionamento converta um POST em GET. Consulte o guia de homologação para diagnosticar esses erros.

Exemplo Mínimo

import fs from 'node:fs';
import { assertNfseXmlMatchesDps, decodeNfseXmlGZipB64, NfseCampinas } from '@4success/nfse-campinas';

const nfse = new NfseCampinas({
  environment: 'homologacao',
  certificate: fs.readFileSync('./certificado.pfx'),
  certPassword: process.env.CERTIFICATE_PASSWORD!,
  // O endpoint de homologacao pode levar mais de 30 segundos para responder.
  timeoutMs: 120000,
});

const result = await nfse.enviarDps({
  ambiente: 'homologacao',
  serie: '00001',
  numeroDps: '1',
  dataCompetencia: '2026-06-30',
  dataHoraEmissao: '2026-06-30T21:41:28-03:00',
  tipoEmitente: 1,
  municipioEmissao: '3509502',
  prestador: {
    cnpj: '99999999000199',
    inscricaoMunicipal: '123456',
    regimeTributario: { opcaoSimplesNacional: 1, regimeEspecialTributacao: 0 },
  },
  tomador: {
    cnpj: '99999999000199',
    razaoSocial: 'TOMADOR LTDA',
    endereco: { municipio: '3509502', cep: '13000000', logradouro: 'Rua Exemplo', numero: '100', bairro: 'Centro' },
  },
  servico: {
    municipioPrestacao: '3509502',
    codigoTributacaoNacional: '010601',
    codigoTributacaoMunicipal: '001',
    descricao: 'descricao do servico prestado para fins de homologacao',
    codigoNbs: '115011000',
  },
  valores: {
    valorServico: '26947.27',
    tributacaoMunicipal: { tributacaoIssqn: 1, tipoRetencaoIssqn: 1, aliquota: '5' },
    tributacaoFederal: {
      pisCofins: { cst: '00', tipoRetencaoPisCofins: 0 },
    },
    totalTributos: { indicadorTotalTributos: 0 },
  },
  ibsCbs: {
    finalidadeNfse: 0,
    codigoIndicadorOperacao: '100301',
    indicadorDestinatario: 0,
    cst: '000',
    classificacaoTributaria: '000001',
  },
});

console.log(result.status);
console.log(result.idDps);
console.log(result.rawResponse);

if (result.nfseXmlGZipB64) {
  const nfseXml = decodeNfseXmlGZipB64(result.nfseXmlGZipB64);
  assertNfseXmlMatchesDps(nfseXml, result.idDps);
  console.log(nfseXml);

  const danfseHtml = await nfse.imprimirDanfse({ nfseXmlGZipB64: result.nfseXmlGZipB64 });
  console.log(danfseHtml);
}

rawResponse preserva a resposta original da Prefeitura, parsedResponse expõe o corpo parseado quando possível, e nfseXmlGZipB64 traz o XML autorizado da NFSe compactado em GZip/Base64.

Consulta NFSe

Consulte uma NFSe autorizada pela chave de acesso retornada no envio:

const consulta = await nfse.consultarNfse('NFS35095022215547137000138000000000210026073571802007');

if (consulta.nfseXmlGZipB64) {
  const xml = decodeNfseXmlGZipB64(consulta.nfseXmlGZipB64);
  console.log(xml);
}

Uma NFSe inexistente retorna HTTP 400 com alertas da Prefeitura, preservados em ConsultaHttpError.response.

Cancelamento de NFSe

Informe os dados do cancelamento. O SDK monta o pedRegEvento v1.01, gera o identificador do pedido, assina infPedReg com o certificado configurado e transmite o evento 101101:

const cancelamento = await nfse.cancelarNfse({
  chaveAcesso: 'NFS35095022215547137000138000000000210026073571802007',
  autor: { cnpj: '15547137000138' },
  codigoMotivo: 2,
  motivo: 'Serviço não prestado pelo fornecedor conforme acordado',
  dataHoraEvento: '2026-08-18T14:30:00-03:00', // opcional; por padrão, usa o instante atual
});

console.log(cancelamento.alertas);
console.log(cancelamento.rawResponse);

Em homologação, a chamada síncrona usa POST https://preprod-nfseapi.ima.sp.gov.br/notafiscal-ws/nfse/{chaveAcesso}/eventos. O SDK compacta o XML assinado com GZip/Base64 e envia o JSON { pedidoRegistroEventoXmlGZipB64 }. Também é possível fornecer um signedXml externo já pronto; nesse caso, o SDK preserva o XML e não o reassina. Consulte o guia de cancelamento para configurar produção, usar essa opção avançada e tratar a resposta.

O exemplo local de consulta está em exemplos/consultar-nfse.ts; ele usa CERTIFICATE_PATH, CERTIFICATE_PASSWORD e a chave de acesso como primeiro argumento.

Consulta por DPS

Quando o envio tiver resultado incerto — por exemplo, timeout depois de a Prefeitura receber a DPS — consulte o mesmo identificador antes de considerar qualquer reenvio:

const consultaDps = await nfse.consultarDps('DPS350950221234567800019900001000000000000001');

console.log(consultaDps.chaveAcesso);

consultarNfsePorDps é um alias de consultarDps. A consulta usa certificado digital (mTLS), e a chave só é revelada quando o titular do certificado corresponde ao prestador, tomador ou intermediário da NFS-e. Erros HTTP preservam alertas e a resposta bruta em ConsultaDpsHttpError.response.

Por segurança, não associe a chave retornada ao seu documento sem consultar a NFS-e e conferir se o Id de infDPS no XML autorizado é igual ao identificador solicitado. Essa conferência também protege contra uma resposta municipal inconsistente:

if (!consultaDps.chaveAcesso) {
  throw new Error(`Consulta da DPS não retornou chave: ${consultaDps.rawResponse}`);
}

const chaveConsulta = consultaDps.chaveAcesso.startsWith('NFS')
  ? consultaDps.chaveAcesso
  : `NFS${consultaDps.chaveAcesso}`;
const consultaNfse = await nfse.consultarNfse(chaveConsulta);
if (!consultaNfse.nfseXmlGZipB64) {
  throw new Error(`Consulta da NFS-e não retornou XML: ${consultaNfse.rawResponse}`);
}
const xml = decodeNfseXmlGZipB64(consultaNfse.nfseXmlGZipB64);

assertNfseXmlMatchesDps(xml, 'DPS350950221234567800019900001000000000000001');

O exemplo local completo está em exemplos/consultar-dps.ts.

DANFSe

Depois que a Prefeitura autoriza a NFSe, use o XML autorizado para gerar o HTML imprimível do DANFSe:

const html = await nfse.imprimirDanfse({ nfseXmlGZipB64: result.nfseXmlGZipB64! });

Também é possível informar o XML autorizado já descompactado:

const html = await nfse.imprimirDanfse({ xml: nfseXml });

O método retorna HTML. A geração de PDF deve ser feita pelo consumidor a partir desse HTML, usando o renderer apropriado para o ambiente de execução.

A classificação IBS/CBS é lida da DPS incluída no XML autorizado; os valores apurados são lidos do bloco IBS/CBS da NFS-e. XMLs históricos com os dados em apenas um desses blocos continuam aceitos. Não é necessário enriquecer o XML com snapshots do emissor para imprimir esses valores.

O ISS prioriza valores/pAliqAplic da NFS-e e, quando ausente, usa tribMun/pAliq da DPS incorporada. Alíquotas informadas como zero são exibidas como 0,00%; campos ausentes continuam com -. O indicador indDest=0 identifica o próprio tomador como destinatário. Nomes de municípios também podem ser resolvidos pelo par cLocalidadeIncid/xLocalidadeIncid no mesmo XML. Horários, telefones e textos declarados são preservados conforme o XML autorizado.

Opcionalmente, descricaoServico pode recuperar as quebras de linha da descrição original somente na apresentação:

const html = await nfse.imprimirDanfse({ xml: nfseXml, descricaoServico: descricaoOriginal });

O complemento é aceito apenas quando seu texto integral coincide com a descrição do XML após remover exclusivamente CR e LF dos dois lados. Espaços, tabulações e todos os outros caracteres continuam significativos; as entidades XML são decodificadas uma vez para essa comparação. Uma divergência ignora o complemento e mantém a descrição autorizada. Tipos diferentes de string são rejeitados. O XML, sua assinatura e os campos tributários não são modificados. As quebras e os parágrafos vazios são preservados na apresentação, mantendo o escape HTML da descrição.

Segurança

  • Use certificado A1 .pfx/.p12 do prestador.
  • Nunca versione certificados, senhas, XML real de cliente ou respostas com dados pessoais.
  • debug=true imprime XML e resposta brutos para diagnóstico local.

Comunidade

Migração

Consulte docs/v3/migracao-v2-para-v3.md para o mapa entre ABRASF/RPS/SOAP e Padrão Nacional/DPS.

About

Biblioteca para emissão de Nota Fiscal de Serviços em Campinas

Resources

Code of conduct

Contributing

Security policy

Stars

16 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages