Acessando o web service de Nota Fiscal de Servicos Eletronica (NFS-e Nota Carioca) em Java

Depois de bater bastante cabeça para conseguir dar um primeiro passo na integração com o sistema de Nota Fiscal de Serviços Eletrônica (NFS-e) do município do Rio de Janeiro (Nota Carioca), vou colocar aqui o passo a passo que eu segui para contribuir com outras pessoas que possam estar passando pelo mesmo martírio.

Não encontramos muita informação detalhada por aí. A conclusão que cheguei é que quem conseguiu fazer a integração não quer divulgar como fez, pois provavelmente a implementação faz parte de algum produto comercial sendo vendido, e portanto fornecer estas informações gratuitamente vai contra a empresa.

Como este não é o meu caso, pois eu não vendo este serviço, estou apenas integrando para poder emitir NFS-e automaticamente com o Peladeiro.com, então resolvi contribuir. Aí vai.....


1- Gerar as classes Java que representam os XML consumidos pelo Web Service da prefeitura:

Meu primeiro passo foi gerar as classes Java que representam os arquivos XML definidos pela prefeitura. Para isso, baixei o XML Schema (XSD) da versao que a prefeitura usa. Baixei do site da ABRASF em http://www.abrasf.org.br/

Neste site, procurar na página de "Nota Fiscal de Servicos Eletronica" o XSD correspondente. No meu caso foi o tipos_nfse_v01.xsd

Gerar as classes Java a partir do XSD: (http://docs.oracle.com/javase/6/docs/technotes/tools/share/xjc.html)

Para isso, executei o comando:

 xjc -p com.meusistema.nfse.binding -d C:\Users\Eu\projetos\meunfse\src\java\com\meusistema\nfse\binding C:\Users\Eu\projetos\meunfse\web-app\WEB-INF\tipos_nfse_v01.xsd  

Isso gerou um monte de classes Java, cada uma representando os tipos definidos no XSD. Essas classes serão usadas para preencher com os dados a serem passados para o web service (e retornados tbem). Após usar as classes para preencher com os dados, por exemplo setCpf("34234234");, usei o JAXB para gerar a string XML a ser passada para o web service.... mas vamos chegar lá...

2- Gerar as classes Java de Cliente do Web Service através do WSDL da prefeitura.

O passo seguinte foi gerar as classes Java que representam o Client do web service. A prefeitura fornece o WSDL. No meu caso ficava aqui: https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl

Só que para conseguir acessar este WSDL, foi preciso eu ter o certificado digital. Sem o certificado, nada feito. Então, comprei um certificado da Certisign, do tipo NFS-e A1. É um processo chato, pois você compra no site, mas depois tem que marcar um dia para ir até lá para entregar a documentação da empresa, dos sócios, do representante, etc.... No final, você sai com um número, acesso um site, e com este número faz o download do certificado meucert.pfx (é um certificado com extensão PFX).

Instalei o certificado na minha máquina (windows 7), e através do browser eu consegui acessar o link acima do WSDL. Salvei o WSDL na minha máquina, e gerei as classes Java a partir dele. Para isso, usei o projeto CXF (versão 2.7.8), e com isso usei wsdl2java, No meu DOS executei:

 wsdl2java -p com.meusistema.nfse -frontend jaxws21 -client "C:\Users\Eu\projetos\meunfse\web-app\WEB-INF\notacarioca-nfse.wsdl"  

3- Como conseguir acessar o Web Service através do seu sistema, sua aplicação Java cliente, usando o certificado digital PFX.

Bom, neste ponto eu já conseguia acessar o web service (na verdade o WSDL) pelo browser, mas a minha aplicação sempre recebia um erro 403 - Forbidden: Access is denied.

a) Depois de muito bater cabeça, vi que o meu certificado PFX não possuía toda a cadeia de certificados, ele possuía apenas o certificado da minha empresa. Mas era preciso ter toda a cadeia:
A cadeia na verdade é minha empresa :: autoridade certificadora :: autoridade raiz -> etc

Então, segui um passo a passo que me salvou. Obrigado ao alexegidio em http://www.guj.com.br/java/148620-nfe-erro-4037---forbidden-ao-acessar-webservices-asmx-resolvido

O passo a passo é:


       1- Importe o certificado .PFX para o IE >>> FERRAMENTAS/OPÇÕES DA INTERNET/ ABA CONTEÚDO / BOTÃO CERTIFICADOS.   
      2- Selecione a aba "Pessoal".   
      3- Clique em importar.   
      4- O Sistema vai abrir um assistente. Selecione o Certificado e clique em avançar.   
      5- Na próxima tela MARQUE O CHECBOX "Marcar esta chave como exportável. Isso possibilitará o backup e transporte das chaves posteriormente". Como o texto diz se você não marcar esta opção o certificado não poderá ser exportado   
      6- Após ser importado seu certificado vai para a aba Pessoal. Clique sobre ele e clique em "exportar"   
      7- Clique em avançar   
      8- Na próxima tela marque a opção: "Sim, exportar a chave privada". Clique em avançar.   
      9- Na janela seguinte marque o checkbox "Incluir todos os certificados no caminho de certificação, se possível". <<ESTA OPÇÃO É CRUCIAL NÃO ESQUEÇA DELA !!!   
      10- Informe a senha e a seguir o local onde o arquivo será salvo.   
      PRONTO Feito isso está pronto você já pode utilizar o certificado no seu projeto.  


b) Teve um outro pulo do gato que foi juntar este meu certificado, agora completo com toda a cadeia de certificados, com os demais certificados aceitos para acesso HTTPS. O java da sua máquina vem com um arquivo cacerts que dentro possui as autoridades certificadoras confiáveis. É assim que sua aplicação java pode acessar um web service em HTTPS. Mas agora, além de aceitar os certificados do HTTPS, nossa aplicação tem que passar um certificado privado, como forma de autenticação. Então, eu fiz o import do meu certificado PFX pra dentro de um keystore existente do java instalado na minha máquina. Para isso, copiei o cacerts original da Sun em C:\Program Files\Java\jdk1.6.0_30\jre\lib\security\cacerts para outro diretório para não quebrar o arquivo original. E então executei o seguinte comando neste arquivo cópia:

 keytool -importkeystore -srckeystore meu-certificado.pfx -destkeystore cacerts -srcstoretype PKCS12 -deststoretype JKS  

A senha do cacerts original da Sun é "changeit".

Com isso, agora eu tenho um keystore que é o arquivo cacerts, que possui também o meu próprio certificado, além dos certificados aceitos para HTTPS.

Pronto. Agora é só executar....


package com.peladeiro.nfse;

import java.io.File;
import java.io.StringWriter;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;

import com.peladeiro.factory.DadosNfseFactory;
import com.peladeiro.nfse.binding.EnviarLoteRpsEnvio;


public class ClienteTeste {
  
  private static final QName SERVICE_NAME = new QName("http://notacarioca.rio.gov.br/", "Nfse");

  public static void main(String[] args) throws JAXBException {
    URL wsdlURL = Nfse.WSDL_LOCATION;
        if (args.length > 0 && args[0] != null && !"".equals(args[0])) { 
            File wsdlFile = new File(args[0]);
            try {
                if (wsdlFile.exists()) {
                    wsdlURL = wsdlFile.toURI().toURL();
                } else {
                    wsdlURL = new URL(args[0]);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        
    ClienteTeste c = new ClienteTeste();
    c.autentica();
    c.chamaWebService(wsdlURL);

  }
  
  private void autentica() {

    String caminhoDoCertificadoDoCliente = "C:\\Users\\Eu\\meu-certificado-final.pfx";
    String senhaDoCertificadoDoCliente = "minhaSenhaPrivada";
    String caminhoDoKeyStore = "C:\\Users\\Eu\\cacerts";
    String senhaDoKeyStore = "changeit";

    System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
    
    System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
    System.setProperty("javax.net.ssl.keyStore", caminhoDoCertificadoDoCliente);
    System.setProperty("javax.net.ssl.keyStorePassword", senhaDoCertificadoDoCliente);

    System.setProperty("javax.net.ssl.trustStoreType", "JKS");
    System.setProperty("javax.net.ssl.trustStore", caminhoDoKeyStore);
    System.setProperty("javax.net.ssl.trustStorePassword", senhaDoKeyStore);
    
  }

  public void chamaWebService(URL wsdlURL ) throws JAXBException{
    
      
        Nfse ss = new Nfse(wsdlURL, SERVICE_NAME);
        NfseSoap port = ss.getNfseSoap();  
        
        // Esta Factory instancia os objetos com os dados a serem passados para o web service
        /*
        Exemplo:
        TcIdentificacaoRps idrps = new TcIdentificacaoRps();
    idrps.setNumero(new BigInteger("4321"));
    idrps.setSerie("1");
    idrps.setTipo(TIPO_RPS);
         * */
        EnviarLoteRpsEnvio envio = DadosNfseFactory.criaEnviarLoteRpsEnvio();
        
        JAXBContext jc = JAXBContext.newInstance(EnviarLoteRpsEnvio.class);
    Marshaller m = jc.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        
        StringWriter sw = new StringWriter();
    m.marshal(envio, sw);
        
        RecepcionarLoteRpsRequest recepcionarLoteRpsRequest = new RecepcionarLoteRpsRequest();
    recepcionarLoteRpsRequest.setInputXML(sw.toString());
    RecepcionarLoteRpsResponse recepcionarLoteRpsResponse = port.recepcionarLoteRps(recepcionarLoteRpsRequest);
    System.out.println("recepcionarLoteRps.result=" + recepcionarLoteRpsResponse.outputXML);
  }

}


E é isso!
Abcs



14 comentários:

Patrick 30 de abril de 2014 23:53

Também preciso automatizar a emissão de notas fiscais no meu site, mas não estou conseguindo encontrar uma implementação em Java nem muita informação a respeito. Agradeço por compartilhar.

Anônimo 21 de abril de 2015 15:44

Felipe, muito obrigado por postar teu exemplo, baseado nele consegui montar uma rotina para enviar e receber notas de servico.
Valeu mesmo!!!!

Marcelo

Anônimo 14 de maio de 2015 17:46

Felipe,
Muito obrigado por postar esse tutorial. Definitivamente o material mais completo pra realizar a integração com a Nota Carioca. Com ele finalmente consegui fazer a integração com sucesso.

Estou com um problema que talvez você possa ter encontrado: tentando utilizar o serviço ConsultaNfse, o elemento listaNfse da resposta está sempre vazio. Seja consultando por um número de nota válido como consultando por um período de emissão que existem notas emitidas. Testei tanto em homologação como em produção. Chegou a passar por isso?

Obrigado,
Tiago.

Carlos Andrade 13 de julho de 2016 12:50

Oi felipe.

Estou tambem com dificuldades.
Porem nao pude usar o exemplo porque tem arquivos que nao entendi de ondem vieram

Nfse, EnviarLoteRpsEnvio, DadosNfseFactory

Se puder me enviar, agradeço

casdea@gmail.com

Felipe Nascimento 15 de julho de 2016 17:06

Olá Carlos

Nfse é uma classe que foi gerada pelo comando abaixo
wsdl2java -p com.meusistema.nfse -frontend jaxws21 -client "C:\Users\Eu\projetos\meunfse\web-app\WEB-INF\notacarioca-nfse.wsdl"


EnviarLoteRpsEnvio idem, também foi gerada pelo comando acima.

Já a DadosNfseFactory, foi uma pequena classe minha que eu fiz apenas para preencher as instâncias dos objetos que representam o XML. É apenas para ter dados pra testar. Não precisa dessa classe, pode fazer diretamente. Veja que ela retorna um EnviarLoteRpsEnvio. Basta você instanciar esta classe com
new EnviarLoteRpsEnvio()
E sair preenchendo seus atributos. Só isso.

Abcs
Felipe

Carlos Andrade 16 de julho de 2016 00:13

Obrigado pela resposta. Vou tentar fazer conforme você orientou.

Leonardo Hermes 1 de agosto de 2016 11:34

Muito bom o material Felipe, graças a ele consegui gerar meu programa de consulta a nfse. Muito obrigado e parabéns!

#SSG# 24 de agosto de 2016 22:03

To implementando pra NFSe em .Net c# consegui fazer a primeira parte da tradução da classe.
Mas quando chegou na parte do WSDL não consigo realizar o processo.
Alguem pode ajudar?

Carlos Andrade 18 de outubro de 2016 09:24

Bom dia felipe !

você pode mandar pra mim o DadosNfseFactory ?

Carlos Andrade 19 de outubro de 2016 16:00

Fiz como você diz, porem a classe Nfse não é gerada. Me ajuda

Anônimo 16 de novembro de 2016 20:41

Amigo, eu jamais conseguiria sem a sua ajuda... Funcionou perfeitamente e está muito bem explicado!
Tive de incluir informações de proxy e utilizar o Unmarshaller, mas isso fica para o próximo tutorial.
Muito obrigado!

Felipe Nascimento 16 de novembro de 2016 22:13

Que bom Anônimo... Abcs
Carlos Andrade, tem bastante tempo que fiz isso.... Nem tenho mais esse código de exemplo. Mas a Dados NfseFactory era uma classe muito simples que apenas instanciada as classes de dados e preenchia os dados. Abcs

Anônimo 24 de novembro de 2016 10:45

Excelente tutorial!

Alguém teria um exemplo de como montar o SignatureType?

Obrigado.
Rafael.

Felipe Henrique 7 de dezembro de 2016 10:00

consegue disponibilizar o codigo gonte?