NFS-e PMSP

Projeto [x]Harbour - Compilador de código aberto compatível com o Clipper.

Moderador: Moderadores

Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Exe que Assina o RPS.

Mas porque precisou disso?
E a rotina de assinatura que está dentro do fonte?

Dá pra eliminar estas funções:

Código: Selecionar todos

METHOD DataToYYYY_MM_DD( dDAT, lTIME ) CLASS NFSE
   LOCAL cRET := AllTrim( Str( Year( dDAT ) ) ) + '-' + StrZero( Month( dDAT ), 2 ) + '-' + StrZero( Day( dDAT ), 2 )
   IF lTIME = NIL
      lTIME := .F.
   ENDIF
   IF lTIME
      cRET += 'T' + Left( Time(), 8 )
   ENDIF
   RETURN( cRET )

METHOD DataToYYYYMMDD( dDAT ) CLASS NFSE
   LOCAL cRET := AllTrim( Str( Year( dDAT ) ) ) + StrZero( Month( dDAT ), 2 ) + StrZero( Day( dDAT ), 2 )
   RETURN( cRET )
Mais fácil usar isto:

Código: Selecionar todos

? Transform( Dtos( dDat ), "@R 9999-99-99" ) + "T" + Left( Time(), 8 )
? Dtos( dDat )
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
eduardomc00
Usuário Nível 1
Usuário Nível 1
Mensagens: 43
Registrado em: 07 Out 2014 18:29
Localização: São Paulo

NFS-e PMSP

Mensagem por eduardomc00 »

Bom dia amigos.
Como eu disse, o código está sujo e feio :D :D

hbnfe.ch

Código: Selecionar todos

#define _RECEPCAO             1
#define _RETRECEPCAO          2
#define _CANCELAMENTO         3
#define _INUTILIZACAO         4
#define _CONSULTAPROTOCOLO    5
#define _STATUSSERVICO        6
#define _CONSULTACADASTRO     7
#define _RECEPCAODEEVENTO     8
#define _EVENTO               9
#define _CONSULTANFEDEST      10
#define _DOWNLOADNFE          11
#define _RECPEVENTO           12

#define _MSXML2_DOMDocument          'MSXML2.DOMDocument.5.0'
#define _MSXML2_MXDigitalSignature   'MSXML2.MXDigitalSignature.5.0'
#define _MSXML2_XMLSchemaCache       'MSXML2.XMLSchemaCache.5.0'
#define _MSXML2_ServerXMLHTTP        'MSXML2.ServerXMLHTTP.5.0'

#define _LOGO_ESQUERDA            1
#define _LOGO_DIREITA             2
#define _LOGO_EXPANDIDO           3
#define _LOGO_TOPO                4

#define HBNFE_MXML            1
#define HBNFE_CURL            2

#define HBNFE_EXIGIDA         .T.
#define HBNFE_NAOEXIGIDA      .F.

#define _CAPICOM_STORE_OPEN_READ_ONLY                 0           // Somente Smart Card em Modo de Leitura

#define _CAPICOM_MEMORY_STORE                         0
#define _CAPICOM_LOCAL_MACHINE_STORE                  1
#define _CAPICOM_CURRENT_USER_STORE                   2
#define _CAPICOM_ACTIVE_DIRECTORY_USER_STORE          3
#define _CAPICOM_SMART_CARD_USER_STORE                4

#define _CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED           2
#define _CAPICOM_CERTIFICATE_FIND_SHA1_HASH           0           // Retorna os Dados Criptografados com Hash SH1
#define _CAPICOM_CERTIFICATE_FIND_EXTENDED_PROPERTY   6
#define _CAPICOM_CERTIFICATE_FIND_TIME_VALID          9           // Retorna Certificados Válidos
#define _CAPICOM_CERTIFICATE_FIND_KEY_USAGE           12          // Retorna Certificados que contém dados.
#define _CAPICOM_DIGITAL_SIGNATURE_KEY_USAGE          0x00000080  // Permitir o uso da Chave Privada para assinatura Digital
#define _CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME 0           // Este atributo contém o tempo em que a assinatura foi criada.
#define _CAPICOM_INFO_SUBJECT_SIMPLE_NAME             0           // Retorna o nome de exibição do certificado.
#define _CAPICOM_ENCODE_BASE64                        0           // Os dados são guardados como uma string base64-codificado.
#define _CAPICOM_E_CANCELLED                          -2138568446 // A operação foi cancelada pelo usuário.
#define _CERT_KEY_SPEC_PROP_ID                        6
#define _CAPICOM_CERT_INFO_ISSUER_EMAIL_NAME          0
#define _SIG_KEYINFO                                  2
#include "wsoft.ch"


#xcommand EXECUTE ;
    FILE <executable> ;
    [ DEFAULT <default> ] ;
    WAIT ;
    [ WHILE <while> ] ;
    [ INTERVAL <msec> ] ;
=> ;
WaitRunTerm ( <executable> , <default> , 5 , <{while}> , <msec> )

#xcommand EXECUTE ;
    FILE <executable> ;
    [ DEFAULT <default> ] ;
    WAIT ;
    [ WHILE <while> ] ;
    [ INTERVAL <msec> ] ;
    MAXIMIZE ;
=> ;
WaitRunTerm ( <executable> , <default> , 3 , <{while}> , <msec> )

#xcommand EXECUTE ;
    FILE <executable> ;
    [ DEFAULT <default> ] ;
    WAIT ;
    [ WHILE <while> ] ;
    [ INTERVAL <msec> ] ;
    MINIMIZE ;
=> ;
WaitRunTerm ( <executable> , <default> , 6 , <{while}> , <msec> )

#xcommand EXECUTE ;
    FILE <executable> ;
    [ DEFAULT <default> ] ;
    WAIT ;
    [ WHILE <while> ] ;
    [ INTERVAL <msec> ] ;
    HIDE ;
=> ;
WaitRunTerm ( <executable> , <default> , 0 , <{while}> , <msec> )


#xcommand EXECUTE ;
    [ OPERATION <operation> ] ;
    FILE <file> ;
    [ PARAMETERS <parameters> ] ;
    [ DEFAULT <default> ] ;
=> ;
_Execute ( , <operation> , <file> , <parameters> , <default> , 5 )

#xcommand EXECUTE ;
    [ OPERATION <operation> ] ;
    FILE <file> ;
    [ PARAMETERS <parameters> ] ;
    [ DEFAULT <default> ] ;
    MAXIMIZE ;
=> ;
_Execute ( , <operation> , <file> , <parameters> , <default> , 3 )

#xcommand EXECUTE ;
    [ OPERATION <operation> ] ;
    FILE <file> ;
    [ PARAMETERS <parameters> ] ;
    [ DEFAULT <default> ] ;
    MINIMIZE ;
=> ;
_Execute ( , <operation> , <file> , <parameters> , <default> , 6 )

#xcommand EXECUTE ;
    [ OPERATION <operation> ] ;
    FILE <file> ;
    [ PARAMETERS <parameters> ] ;
    [ DEFAULT <default> ] ;
    HIDE ;
=> ;
_Execute ( , <operation> , <file> , <parameters> , <default> , 0 )
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Ok.

Esta parte é legal, descomplicando um pouco o METHOD de assinatura, que aparentemente não é usada, já que usa programa externo...

Código: Selecionar todos

METHOD Assina_XML() CLASS NFSE

   LOCAL oDOMDoc, oXmldsig, oCert, oStoreMem, dsigKey, signedKey
   LOCAL aRETORNO := Hash()
   LOCAL cXML, cXMLSig
   LOCAL PosIni, PosFim, nP, nResult
   LOCAL nHandle

   aRETORNO[ 'STATUS' ] := .F.
   aRETORNO[ 'MSG' ] := ''

   IF ::Xml = NIL .OR. Empty( ::Xml )
      aRETORNO[ 'MSG' ] := 'Arquivo XML com o registro do cabeçalho e RPS não informado.'
   ENDIF
   cXML := MemoRead( ::Xml )
   FErase( ::Xml )

   cXML := StrTran( cXML, '<Signature></Signature>', '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">' + ;
      '<SignedInfo>' + ;
      '<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />' + ;
      '<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />' + ;
      '<Reference URI="">' + ;
      '<Transforms>' + ;
      '<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />' + ;
      '<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />' + ;
      '</Transforms>' + ;
      '<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />' + ;
      '<DigestValue>' + ;
      '</DigestValue>' + ;
      '</Reference>' + ;
      '</SignedInfo>' + ;
      '<SignatureValue>' + ;
      '</SignatureValue>' + ;
      '<KeyInfo>' + ;
      '<X509Data>' + ;
      '<X509Certificate>' + ;
      '</X509Certificate>' + ;
      '</X509Data>' + ;
      '</KeyInfo>' + ;
      '</Signature>' )

// Inicializa o objeto do DOMDocument
   BEGIN SEQUENCE WITH __BreakBlock()
      oDOMDoc := win_oleCreateObject( _MSXML2_DOMDOCUMENT )
   RECOVER
      aRETORNO[ 'MSG' ] := 'Nao foi possível carregar ' + _MSXML2_DOMDOCUMENT
      RETURN aRETORNO
   ENDSEQUENCE
   oDOMDoc:async = .F.
   oDOMDoc:resolveExternals := .F.
   oDOMDoc:validateOnParse = .T.
   oDOMDoc:preserveWhiteSpace = .T.

// inicializa o objeto do MXDigitalSignature
   BEGIN SEQUENCE WITH __BreakBlock()
      oXmldsig := win_oleCreateObject( _MSXML2_MXDIGITALSIGNATURE )
   RECOVER
      aRETORNO[ 'MSG' ] := 'Nao foi possível carregar ' + _MSXML2_MXDIGITALSIGNATURE
      RETURN aRETORNO
   ENDSEQUENCE

   nHandle := FCreate( ::ohbNFe:pastaEnvRes + "\edunfs.xml" )
   FWrite( nHandle, cXML )
   FClose( nHandle )

// carrega o arquivo XML para o DOM
   oDOMDoc:LoadXML( cXML )
   IF oDOMDoc:parseError:errorCode <> 0
      aRETORNO[ 'MSG' ] := ' Assinar: Não foi possível carregar o documento pois ele não corresponde ao seu Schema' + hb_Eol() + ;
         ' Linha: '       + Str( oDOMDoc:parseError:line ) + hb_eol() + ;
         ' Caractere na linha: ' + Str( oDOMDoc:parseError:linepos ) + hb_eol() + ;
         ' Causa do erro: '   + oDOMDoc:parseError:reason + hb_eol() + ;
         ' code: '       + Str( oDOMDoc:parseError:errorCode )
      RETURN aRETORNO
   ENDIF

// Localiza as assinaturas no XML
   oDOMDoc:SETPROPERTY( 'SelectionNamespaces', "xmlns:ds='http://www.w3.org/2000/09/xmldsig#'" )
   oXmldsig:signature := oDOMDoc:selectSingleNode( './/ds:Signature' )
   IF ( oXmldsig:signature = nil )
      aRETORNO[ 'MSG' ] := 'É preciso carregar o template antes de assinar.'
      RETURN aRETORNO
   ENDIF

// carrega o objeto do certificado digital
   oCert := ::ohbNFe:pegaObjetoCertificado( ::ohbNFe:cSerialCert )

   IF oCert == Nil
      aRETORNO[ 'MSG' ] := 'Certificado não encontrado, Favor revisar a instalação do Certificado'
      RETURN aRETORNO
   ENDIF

// cria o objeto de Store da capicom
   oStoreMem := win_oleCreateObject( 'CAPICOM.Store' )

// Aloca o certificado na memoria
   BEGIN SEQUENCE WITH __BreakBlock()
      oStoreMem:open( _CAPICOM_MEMORY_STORE, 'Memoria', _CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED )
   RECOVER // CATCH oError
      aRETORNO[ 'MSG' ] := 'Falha ao alocar o certificado na memoria ' + hb_eol() + ;
         'Error: '  + Transform( oError:GenCode, nil ) + ';' + hb_eol() + ;
         'SubC: '   + Transform( oError:SubCode, nil ) + ';' + hb_eol() + ;
         'OSCode: '  + Transform( oError:OsCode, nil ) + ';' + hb_eol() + ;
         'SubSystem: ' + Transform( oError:SubSystem, nil ) + ';' + hb_eol() + ;
         'Mensangem: ' + oError:Description
      RETURN aRETORNO
   ENDSEQUENCE

// Aloca o certificado na Capicom
   BEGIN SEQUENCE WITH __BreakBlock()
      oStoreMem:Add( oCert )
   RECOVER // CATCH oError
      aRETORNO[ 'MSG' ] := 'Falha ao aloca o certificado na memoria da Capicom ' + hb_eol() + ;
         'Error: '  + Transform( oError:GenCode, nil ) + ';' + hb_eol() + ;
         'SubC: '   + Transform( oError:SubCode, nil ) + ';' + hb_eol() + ;
         'OSCode: '  + Transform( oError:OsCode, nil ) + ';' + hb_eol() + ;
         'SubSystem: ' + Transform( oError:SubSystem, nil ) + ';' + hb_eol() + ;
         'Mensangem: ' + oError:Description
      RETURN aRETORNO
   ENDSEQUENCE
   oXmldsig:store := oStoreMem

// Cria chave CSP
   BEGIN SEQUENCE WITH __BreakBlock()
      dsigKey := oXmldsig:createKeyFromCSP( oCert:PrivateKey:ProviderType, oCert:PrivateKey:ProviderName, oCert:PrivateKey:ContainerName, 0 )
   RECOVER
      aRETORNO[ 'MSG' ] := 'Erro ao criar a chave do CSP, talvez o certificado não esteja instalado corretamente.'
      RETURN aRETORNO
   ENDSEQUENCE
   IF ( dsigKey = nil )
      aRETORNO[ 'MSG' ] := 'Erro ao criar a chave do CSP.'
      RETURN aRETORNO
   ENDIF

// Assina a chave do CSP
   BEGIN SEQUENCE WITH __BreakBlock()
      signedKey := oXmldsig:Sign( dsigKey, 2 )
   RECOVER
      aRETORNO[ 'MSG' ] := 'Erro ao assinar a chave do CSP, talvez o certificado não esteja instalado corretamente.'
      RETURN aRETORNO
   ENDSEQUENCE
   IF signedKey = NIL
      aRETORNO[ 'MSG' ] := 'Assinatura Falhou.'
      RETURN( aRetorno )
   ENDIF

// Trata o formato da estrutura do XML
   cXMLSig := StrTran( StrTran( oDOMDoc:xml, Chr( 10 ) ), Chr( 13 ) )
   PosIni := At( '<SignatureValue>', cXMLSig ) + Len( '<SignatureValue>' )
   cXMLSig := SUBS( cXMLSig, 1, PosIni - 1 ) + StrTran( SUBS( cXMLSig, PosIni, Len( cXMLSig ) ), ' ', '' )
   PosIni := At( '<X509Certificate>', cXMLSig ) - 1
   nP   := At( '<X509Certificate>', cXMLSig )
   nResult := 0
   DO WHILE nP <> 0
      nResult := nP
      nP = WAT( '<X509Certificate>', cXMLSig, nP + 1 )
   ENDDO
   PosFim := nResult
   cXMLSig := SUBS( cXMLSig, 1, PosIni ) + SUBS( cXMLSig, PosFim, Len( cXMLSig ) )
   cXMLsig := StrTran( cXMLsig, 'Id="rps:1"', '' )

// grava o arquivo no disco
   nHandle := FCreate( ::Xml )
   FWrite( nHandle, cXMLSig )
   FClose( nHandle )

   aRETORNO[ 'STATUS' ] := .T.
   aRETORNO[ 'XML' ] := ::Xml
   aRETORNO[ 'MSG' ] := 'XML assinado com sucesso em ' + ::Xml

   RETURN aRETORNO
Dá pra descomplicar, apenas uma prévia do que está diferente:

Código: Selecionar todos

METHOD AssinaXml() CLASS NFSE
   IF ::Xml = NIL .OR. Empty( ::Xml )
      aRETORNO[ 'MSG' ] := 'Arquivo XML com o registro do cabeçalho e RPS não informado.'
   ENDIF
   cXML := MemoRead( ::Xml )
   FErase( ::Xml )
   nHandle := FCreate( ::ohbNFe:pastaEnvRes + "\edunfs.xml" )
   FWrite( nHandle, cXML )
   FClose( nHandle )
   cRetorno := AssinaXml( @cXml, cCertificado )
// grava o arquivo no disco
   nHandle := FCreate( ::Xml )
   FWrite( nHandle, cXMLSig )
   FClose( nHandle )
Com isso, a classe nem usa mais CAPICOM.
Lógico, quem usa é a rotina de assinatura (igual na SefazClass)

Esta aqui também:

Código: Selecionar todos

METHOD ctPegaCNCertificado() CLASS NFSE

   LOCAL oStore, oCertificados
   LOCAL cSubjectName := '', cCN := ''
   LOCAL mI

   BEGIN SEQUENCE WITH __BreakBlock()
      oStore := win_oleCreateObject( "CAPICOM.Store" )
   END SEQUENCE

   IF oStore = Nil
      RETURN ''
   ENDIF

   oStore:open( _CAPICOM_CURRENT_USER_STORE, 'My', _CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED )
   oCertificados := oStore:Certificates()
   FOR mI = 1 TO oCertificados:Count()
      IF oCertificados:Item( mI ):SerialNumber = ::ohbNFe:cSerialCert
         cSubjectName := oCertificados:Item( mI ):SubjectName
      ENDIF
   NEXT
   cCN := ''
   FOR mI := At( "CN=", cSubjectName ) + 3 TO Len( cSubjectName )
      IF SUBS( cSubjectName, mI, 1 ) == ","
         EXIT
      ENDIF
      cCN += SUBS( cSubjectName, mI, 1 )
   NEXT
   oCertificados := Nil
   oStore := Nil

   RETURN cCN
Vamos pensar....
Se é pra manter o serial do certificado, pra depois pegar o NOME, então é só trabalhar diretamente com o nome, e não com o serial...
(igual na SefazClass)

É chato dizer isso, mas....
Tudo que usa HBNFE como modelo fica ruim, esqueçam os fontes da HBNFE.
É muito trabalho pra pouca coisa.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Esta parte também, já falei disso aqui no fórum.

Código: Selecionar todos

   BEGIN SEQUENCE WITH __BreakBlock()

      oDOMDoc := win_oleCreateObject( _MSXML2_DOMDOCUMENT )
      oDOMDoc:async = .F.
      oDOMDoc:validateOnParse = .T.
      oDOMDoc:resolveExternals := .F.
      oDOMDoc:preserveWhiteSpace = .T.
      oDOMDoc:LoadXML( cXML )

   RECOVER

      aRETORNO[ 'MSG' ] := 'Não foi possível carregar o documento XML'
      RETURN aRETORNO

   ENDSEQUENCE

   IF oDOMDoc:parseError:errorCode <> 0

      aRETORNO[ 'MSG' ] := 'Não foi possível carregar o documento pois ele não corresponde ao seu Schema' + hb_eol() + ;
         ' Linha: ' + Str( oDOMDoc:parseError:line )                   + hb_eol() + ;
         ' Caractere na linha: ' + Str( oDOMDoc:parseError:linepos )           + hb_eol() + ;
         ' Causa do erro: ' + oDOMDoc:parseError:reason                 + hb_eol() + ;
         ' Code: ' + Str( oDOMDoc:parseError:errorCode )
      RETURN aRETORNO

   ENDIF

   BEGIN SEQUENCE WITH __BreakBlock()

      oServerWS:send( oDOMDoc:xml )

Tudo isso é perda de tempo.
Está pegando o componente DOMDOC pra colocar o XML lá, e depois pegar o XML de volta.

Tudo isso, equivale a isto:
oServerWs:Send( cXml )

Não tem que ficar testando seu XML na rotina de envio.
Tem que gerar o XML certo.
E isso nem sequer é uma validação de XML, é só um teste básico.
Teste básico se faz quando está se criando a rotina de gerar XML pela primeira vez.

Isso sem mencionar que tem esta outra:
METHOD ValidaXML()

Se passou no teste completo, pra que fazer o teste básico depois?
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
eduardomc00
Usuário Nível 1
Usuário Nível 1
Mensagens: 43
Registrado em: 07 Out 2014 18:29
Localização: São Paulo

NFS-e PMSP

Mensagem por eduardomc00 »

josé bom dia!! :D


Há muitas coisas que estão desnecessárias.
O Problema é tempo pra parar e resolver isso.
com a NF-e 4.0 eu pretendo dar uma revisada nesse e no projeto da própria NF-e.
Vamos ver kkkk

Mas se tivr como, me manda a Sefazclass no meu e-mail ? eduardo.mc.00@uol.com.br

Eu ficaria muito feliz kkkk

Obrigado pelos retornos.
eduardomc00
Usuário Nível 1
Usuário Nível 1
Mensagens: 43
Registrado em: 07 Out 2014 18:29
Localização: São Paulo

NFS-e PMSP

Mensagem por eduardomc00 »

Ahhhh, se quiserem.

aqui estão os códigos do EXE em C#:


Program.cs:

Código: Selecionar todos

static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            /*
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
              
            */
            if (System.IO.File.Exists(@"StringParaAssinar.txt"))
            {
                string[] lines = System.IO.File.ReadAllLines(@"StringParaAssinar.txt");
                string[] aDados = new string[2];
                
                aDados[0] = lines[0]; // Numero de Série Certificado;
                aDados[1] = lines[1]; // String a Assinar.
                // MessageBox.Show(aDados[0]);
                // MessageBox.Show(aDados[1]);
                AssinarRPS assina = new AssinarRPS();
                String assinatura = assina.AssinarRPSSP(aDados[0], aDados[1]);
                // MessageBox.Show(assinatura);

                //Declaração do método StreamWriter passando o caminho e nome do arquivo que deve ser salvo
                StreamWriter writer = new StreamWriter(@"StringAssinada.txt");
                //Escrevendo o Arquivo e pulando uma linha
                writer.WriteLine(assinatura);
                //Fechando o arquivo
                writer.Close();
                //Limpando a referencia dele da memória
                writer.Dispose();

                
            }
        }
    }


ASSINARPS.cs:

Código: Selecionar todos

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace WindowsFormsApplication1
{
    public class AssinarRPS
    {
        public AssinarRPS() { }

        public string AssinarRPSSP(string serial, string original)
        {
            //X509Store store = new X509Store(StoreLocation.LocalMachine); 
            X509Store store = new X509Store();
            store.Open(OpenFlags.ReadOnly);
            string sn = serial, criptografada; //" put here your certificate serial number "; 
            criptografada = "";
            X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindBySerialNumber, serial, true);
            X509Certificate2 cert = null;

            Console.WriteLine(coll.Count);

            if (coll.Count > 0 && coll[0] != null)
            {
                cert = coll[0];
            }
            else return "Erro ao assinar RPS";

            //recebe o certificado e a string a ser assinada 
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

            //pega a chave privada do certificado digital 
            rsa = cert.PrivateKey as RSACryptoServiceProvider;

            //cria o array de bytes e realiza a conversao da string em array de bytes 
            byte[] sAssinaturaByte = enc.GetBytes(original);

            RSAPKCS1SignatureFormatter rsaf = new RSAPKCS1SignatureFormatter(rsa);
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();

            //cria a variavel hash que armazena o resultado do sha1 
            byte[] hash;
            hash = sha1.ComputeHash(sAssinaturaByte);

            //definimos o metodo a ser utilizado na criptografia e assinamos 
            rsaf.SetHashAlgorithm("SHA1");
            sAssinaturaByte = rsaf.CreateSignature(hash);

            //por fim fazemos a conversao do array de bytes para string 
            criptografada = Convert.ToBase64String(sAssinaturaByte);
            return criptografada;

        }
    }
}   

Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Então a rotina de assinatura do fonte que postou realmente não funciona ok?

Só pegar da SefazClass que funciona.

A única informação necessária é o inicio/fim dos blocos a assinar, e aonde vai ser colocada a assinatura.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

com a NF-e 4.0 eu pretendo dar uma revisada nesse e no projeto da própria NF-e.
Vamos ver kkkk
não se esqueça daqui

https://github.com/JoseQuintas/sefazclass
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Aliás... vamos inverter agora....
Deixar o seu fonte como origem de informações e começar um do zero, baseado na SefazClass.
Talvez ajude a todos, a entender tudo...
Fazer "de trás pra frente".
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
eduardomc00
Usuário Nível 1
Usuário Nível 1
Mensagens: 43
Registrado em: 07 Out 2014 18:29
Localização: São Paulo

NFS-e PMSP

Mensagem por eduardomc00 »

Com certeza, inclusive a mim.

Vou migrar o meu Sistema para a SefazClass. Assim falamos a mesma língua.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

começar com uma sefazclass "pelada", ou pelo menos reduzida.

Código: Selecionar todos

/*
ZE_SPEDSEFAZCLASS - Rotinas pra comunicação com SEFAZ
José Quintas
*/

#include "hbclass.ch"

#define WS_NFE_STATUSSERVICO         17

#define WS_AMBIENTE_HOMOLOGACAO      "2"
#define WS_AMBIENTE_PRODUCAO         "1"

#ifndef XML_UTF8
   #define XML_UTF8                     [<?xml version="1.0" encoding="UTF-8"?>]
#endif

CREATE CLASS SefazClass

   /* configuração */
   VAR    cAmbiente      INIT WS_AMBIENTE_PRODUCAO
   VAR    cUF            INIT "SP"                    // Modificada conforme método
   VAR    cCertificado   INIT ""                      // Nome do certificado
   VAR    nTempoEspera   INIT 7                       // intervalo entre envia lote e consulta recibo
   VAR    cUFTimeZone    INIT "SP"                    // Para DateTimeXml() Obrigatório definir UF default
   /* XMLs de cada etapa */
   VAR    cXmlDocumento  INIT ""                      // O documento oficial, com ou sem assinatura, depende do documento
   VAR    cXmlEnvio      INIT ""                      // usado pra criar/complementar XML do documento
   VAR    cXmlSoap       INIT ""                      // XML completo enviado pra Sefaz, incluindo informações do envelope
   VAR    cXmlRetorno    INIT "Erro Desconhecido"     // Retorno do webservice e/ou rotina
   VAR    cXmlProtocolo  INIT ""                      // XML protocolo (obtido no consulta recibo e/ou envio de outros docs)
   VAR    cXmlAutorizado INIT ""                      // XML autorizado, caso tudo ocorra sem problemas
   VAR    cStatus        INIT Space(3)                // Status obtido da resposta final da Fazenda
   /* uso interno */
   VAR    cSoapService   INIT ""                      // webservice Serviço
   VAR    cSoapAction    INIT ""                      // webservice Action
   VAR    cSoapURL       INIT ""                      // webservice Endereço

   METHOD NFeConsultaProtocolo( cChave, cCertificado, cAmbiente )

   /* Uso interno */
   METHOD SetSoapURL( nWsServico )
   METHOD XmlSoapEnvelope()
   METHOD XmlSoapPost()
   METHOD MicrosoftXmlSoapPost()

   METHOD Setup( cUF, cCertificado, cAmbiente, nWsServico )

   ENDCLASS

METHOD NFeConsultaProtocolo( cChave, cCertificado, cAmbiente ) CLASS SefazClass

   ::Setup( ::UFSigla( Substr( cChave, 1, 2 ) ), cCertificado, cAmbiente, WS_NFE_STATUSSERVICO )

   ::cSoapVersion := "3.10"
   ::cXmlEnvio    := [<consSitNFe versao="] + ::cSoapVersion + [" xmlns="http://www.portalfiscal.inf.br/nfe">]
   ::cXmlEnvio    +=    XmlTag( "tpAmb", ::cAmbiente )
   ::cXmlEnvio    +=    XmlTag( "xServ", "CONSULTAR" )
   ::cXmlEnvio    +=    XmlTag( "chNFe", cChave )
   ::cXmlEnvio    += [</consSitNFe>]
   IF ! Substr( cChave, 21, 2 ) $ "55,65"
      ::cXmlRetorno := "*ERRO* Chave não se refere a NFE"
   ELSE
      ::XmlSoapPost()
   ENDIF
   ::cStatus := XmlNode( ::cXmlRetorno, "cStat" )
   ::cMotivo := XmlNode( ::cXmlRetorno, "xMotivo" )

   RETURN ::cXmlRetorno

METHOD Setup( cUF, cCertificado, cAmbiente, nWsServico ) CLASS SefazClass

   LOCAL nPos, aSoapList := { ;
      { "**", WS_NFE_STATUSSERVICO,      "nfeConsultaNFDest",    "http://www.portalfiscal.inf.br/nfe/wsdl/NfeConsultaDest/nfeConsultaNFDest" }, ;
      { "BA", WS_NFE_STATUSSERVICO,     "nfeStatusServicoNF",   "http://www.portalfiscal.inf.br/nfe/wsdl/NfeStatusServico" } }

   ::cUF          := iif( cUF == NIL, ::cUF, cUF )
   ::cCertificado := iif( cCertificado == NIL, ::cCertificado, cCertificado )
   ::cAmbiente    := iif( cAmbiente == NIL, ::cAmbiente, cAmbiente )

   IF nWsServico == NIL
      RETURN NIL
   ENDIF
   IF ( nPos := AScan( aSoapList, { | oElement | oElement[ 1 ] $ ::cUF .AND. oElement[ 2 ] == nWsServico } ) ) != 0
      ::cSoapAction  := aSoapList[ nPos, 4 ]
      ::cSoapService := aSoapList[ nPos, 5 ]
   ELSEIF ( nPos := AScan( aSoapList, { | oElement | oElement[ 1 ] == "**" .AND. oElement[ 2 ] == nWsServico } ) ) != 0
      ::cSoapAction  := aSoapList[ nPos, 4 ]
      ::cSoapService := aSoapList[ nPos, 5 ]
   ENDIF
   ::SetSoapURL( nWsServico )

   RETURN NIL

METHOD SetSoapURL( nWsServico ) CLASS SefazClass

   ::cSoapURL := ""
   DO CASE
   CASE nWsServico == WS_NFE_STATUSSERVICO .AND. ::cUF == "ES" ; ::cSoapURL := "https://app.sefaz.es.gov.br/ConsultaCadastroService/CadConsultaCadastro2.asmx"
   ENDCASE
   IF Empty( ::cSoapURL )
      DO CASE
      CASE ::cUF == "SP" ;  ::cSoapURL := SoapURL_SP( ::cAmbiente, nWsServico, ::cVersao )
      ENDCASE
   ENDIF

   RETURN NIL

METHOD XmlSoapPost() CLASS SefazClass

   DO CASE
   CASE Empty( ::cSoapURL )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de webservice"
      RETURN NIL
   CASE Empty( ::cSoapService )
      ::cXmlRetorno := "Erro SOAP: Não há nome do serviço"
      RETURN NIL
   CASE Empty( ::cSoapAction )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de SOAP Action"
      RETURN NIL
   ENDCASE
   ::XmlSoapEnvelope()
   ::MicrosoftXmlSoapPost()
   IF Upper( Left( ::cXmlRetorno, 4 ) )  == "ERRO"
      RETURN NIL
   ENDIF

   RETURN NIL

METHOD XmlSoapEnvelope() CLASS SefazClass

   ::cXmlSoap    := XML_UTF8
   ::cXmlSoap    += [<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ]
   ::cXmlSoap    +=    [xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">]
   ::cXmlSoap    +=    [<soap12:Body>]
   ::cXmlSoap    += ::cXmlEnvio
   ::cXmlSoap    +=    [</] + ::cProjeto + [DadosMsg>]
   ::cXmlSoap    +=    [</soap12:Body>]
   ::cXmlSoap    += [</soap12:Envelope>]

   RETURN NIL

METHOD MicrosoftXmlSoapPost() CLASS SefazClass

   LOCAL oServer, nCont, cRetorno
   LOCAL cSoapAction

   cSoapAction := ::cSoapAction
   BEGIN SEQUENCE WITH __BreakBlock()
      ::cXmlRetorno := "Erro: Criando objeto MSXML2.ServerXMLHTTP"
      oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
      ::cXmlRetorno := "Erro: No uso do objeto MSXML2.ServerXmlHTTP"
      IF ::cCertificado != NIL
         oServer:setOption( 3, "CURRENT_USER\MY\" + ::cCertificado )
      ENDIF
      ::cXmlRetorno := "Erro: Na conexão com webservice " + ::cSoapURL
      oServer:Open( "POST", ::cSoapURL, .F. )
      oServer:SetRequestHeader( "SOAPAction", cSoapAction )
      oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
      oServer:Send( ::cXmlSoap )
      oServer:WaitForResponse( 500 )
      cRetorno := oServer:ResponseBody
      IF ValType( cRetorno ) == "C"
         ::cXmlRetorno := cRetorno
      ELSEIF cRetorno == NIL
         ::cXmlRetorno := "Erro: Sem retorno do webservice"
      ELSE
         ::cXmlRetorno := ""
         FOR nCont = 1 TO Len( cRetorno )
            ::cXmlRetorno += Chr( cRetorno[ nCont ] )
         NEXT
      ENDIF
   ENDSEQUENCE
   IF "<soap:Body>" $ ::cXmlRetorno .AND. "</soap:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soap:Body" ) // hb_UTF8ToStr()
   ELSEIF "<soapenv:Body>" $ ::cXmlRetorno .AND. "</soapenv:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soapenv:Body" ) // hb_UTF8ToStr()
   ELSE
      ::cXmlRetorno := "Erro SOAP: XML retorno não contém soapenv:Body " + ::cXmlRetorno
   ENDIF

   RETURN NIL
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Ponto mais interno: comunicação MicrosoftXMLSOAPPost()

Código: Selecionar todos

METHOD MicrosoftXmlSoapPost() CLASS SefazClass

   LOCAL oServer, nCont, cRetorno
   LOCAL cSoapAction

   cSoapAction := ::cSoapAction
   BEGIN SEQUENCE WITH __BreakBlock()
      ::cXmlRetorno := "Erro: Criando objeto MSXML2.ServerXMLHTTP"
      oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
      ::cXmlRetorno := "Erro: No uso do objeto MSXML2.ServerXmlHTTP"
      IF ::cCertificado != NIL
         oServer:setOption( 3, "CURRENT_USER\MY\" + ::cCertificado )
      ENDIF
      ::cXmlRetorno := "Erro: Na conexão com webservice " + ::cSoapURL
      oServer:Open( "POST", ::cSoapURL, .F. )
      oServer:SetRequestHeader( "SOAPAction", cSoapAction )
      oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
      oServer:Send( ::cXmlSoap )
      oServer:WaitForResponse( 500 )
      cRetorno := oServer:ResponseBody
      IF ValType( cRetorno ) == "C"
         ::cXmlRetorno := cRetorno
      ELSEIF cRetorno == NIL
         ::cXmlRetorno := "Erro: Sem retorno do webservice"
      ELSE
         ::cXmlRetorno := ""
         FOR nCont = 1 TO Len( cRetorno )
            ::cXmlRetorno += Chr( cRetorno[ nCont ] )
         NEXT
      ENDIF
   ENDSEQUENCE
   IF "<soap:Body>" $ ::cXmlRetorno .AND. "</soap:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soap:Body" ) // hb_UTF8ToStr()
   ELSEIF "<soapenv:Body>" $ ::cXmlRetorno .AND. "</soapenv:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soapenv:Body" ) // hb_UTF8ToStr()
   ELSE
      ::cXmlRetorno := "Erro SOAP: XML retorno não contém soapenv:Body " + ::cXmlRetorno
   ENDIF

   RETURN NIL
Temos ali SOAPUrl e SOAPAction

O da NFSE

Código: Selecionar todos

      oServerWS:open( 'POST', cUrlWS, .F. )
      IF .F. // WSet( LPK_B_PC_TEC ) // NÆo est funcionando
         oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/testeenvio' )
      ELSE
         IF cMethod == 'cancelar'
            oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/cancelamentoNFe' )
         ELSE
            oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/envioLoteRPS' )
         ENDIF
      ENDIF
Então primeira coisa, definir esses dois serviços. WS_CANCELAMENTONFE e WS_ENVIOLOTERPS

E dá pra definir o envelope em XMLSoapEnvelope()
que seria esta parte

Código: Selecionar todos

   cXML := '<?xml version="1.0" encoding="utf-8"?>' + ;
      '<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + ;
      'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' + ;
      'xmlns:soap12="http://schemas.xmlsoap.org/soap/envelope/">' + ;
      '<soap12:Body>' + ;
      '<' + cNameSpace + ' xmlns="http://www.prefeitura.sp.gov.br/nfe">' + ;
      '<VersaoSchema>1</VersaoSchema>' + ;
      '<MensagemXML> ' + ;
      '<![CDATA[ ' + cXML + ' ]]>' + ;
      '</MensagemXML>' + ;
      '</' + cNameSpace + '>' + ;
      '</soap12:Body>' + ;
      '</soap12:Envelope>'
Aqui já notei uma pequena diferença, que pode fazer diferença.
cXML := '<?xml version="1.0" encoding="utf-8"?>' + ;
UTF-8 está em minúsculas. Tem UF que rejeita isso, pode ser igual em prefeituras.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

acho que encontrei um dos seus problemas

Código: Selecionar todos

      IF .F. // WSet( LPK_B_PC_TEC ) // NÆo est funcionando
         oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/testeenvio' )
      ELSE
         IF cMethod == 'cancelar'
            oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/cancelamentoNFe' )
         ELSE
            oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/envioLoteRPS' )
         ENDIF
      ENDIF
A SOAP Action é uma "divisão" da URL (endereço).
Se altera um, altera o outro.
Name space idem.

E mais outro:

Código: Selecionar todos

      cNameSpace := "CancelamentoNFeRequest"
         IF cMethod == 'cancelar'
            oServerWS:setRequestHeader( 'SOAPAction', 'http://www.prefeitura.sp.gov.br/nfe/ws/cancelamentoNFe' )
maiúscula/minúscula
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

NFS-e PMSP

Mensagem por JoséQuintas »

Um primeiro protótipo.

https://github.com/JoseQuintas/nfse-sp- ... EClass.prg

No passo a passo:

Configura, assina, envia e mostra o retorno - destaque que vai configurar pra WS_ENVIOLOTERPS

Código: Selecionar todos

METHOD EnvioLoteRPS( cXml, cCertificado, cAmbiente ) CLASS SefazClass

   ::Setup( cCertificado, cAmbiente, WS_ENVIOLOTERPS )
   ::cXmlEnvio := ::AssinaXml( cXml )
   ::XmlSoapPost()

   RETURN ::cXmlRetorno
Configuração, pega na lista referente a WS_ENVIOLOTERPS

Código: Selecionar todos

   LOCAL nPos, aSoapList := { ;
      { WS_CANCELAMENTONFE,  "CancelamentoNFe", "http://www.prefeitura.sp.gov.br/nfe/ws/cancelamentoNFe", "https://naosei" }, ;
      { WS_ENVIOLOTERPS,     "EnvioLoteRPS",    "http://www.prefeitura.sp.gov.br/nfe/ws/envioLoteRPS", "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx" } }

   IF ( nPos := AScan( aSoapList, { | oElement | oElement[ 1 ] == nWsServico } ) ) != 0
      ::cSoapService := aSoapList[ nPos, 2 ]
      ::cSoapAction  := aSoapList[ nPos, 3 ]
      ::cSoapURL     := aSoapList[ nPos, 4 ]
   ENDIF
A comunicação, além de alguns testes preliminares, coloca no envelope e envia.

Código: Selecionar todos

METHOD XmlSoapPost() CLASS SefazClass

   DO CASE
   CASE Empty( ::cSoapURL )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de webservice"
      RETURN NIL
   CASE Empty( ::cSoapService )
      ::cXmlRetorno := "Erro SOAP: Não há nome do serviço"
      RETURN NIL
   CASE Empty( ::cSoapAction )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de SOAP Action"
      RETURN NIL
   ENDCASE
   ::XmlSoapEnvelope()
   ::MicrosoftXmlSoapPost()
   IF Upper( Left( ::cXmlRetorno, 4 ) )  == "ERRO"
      RETURN NIL
   ENDIF

   RETURN NIL

O envelope, usando as informações obtidas no setup

Código: Selecionar todos

METHOD XmlSoapEnvelope() CLASS SefazClass

   ::cXmlSoap    := XML_UTF8
   ::cXmlSoap    += [<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ]
   ::cXmlSoap    +=       [xmlns:xsd="http://www.w3.org/2001/XMLSchema" ]
   ::cXmlSoap    +=       [xmlns:soap12="http://schemas.xmlsoap.org/soap/envelope/">]
   ::cXmlSoap    +=    [<soap12:Body>]
   ::cXmlSoap    +=       [<] + ::cSoapService + [Request xmlns="http://www.prefeitura.sp.gov.br/nfe">]
   ::cXmlSoap    +=          [<VersaoSchema>1</VersaoSchema>]
   ::cXmlSoap    +=          [<MensagemXML>]
   ::cXmlSoap    +=             "<![CDATA[ " + ::cXmlEnvio + " ]]>"
   ::cXmlSoap    +=          [</MensagemXML>]
   ::cXmlSoap    +=       [</] + ::cSoapService + [Request>]
   ::cXmlSoap    +=   [</soap12:Body>]
   ::cXmlSoap    += [</soap12:Envelope>]

   RETURN NIL
E finalmente a comunicação propriamente dita, exatamente a mesma rotina da SefazClass.
Apenas faz uso da URL, Certificado, SOAP Action e entrega o XML.
E deixa o retorno disponível em ::cXmlRetorno

Código: Selecionar todos

METHOD MicrosoftXmlSoapPost() CLASS SefazClass

   LOCAL oServer, nCont, cRetorno
   LOCAL cSoapAction

   cSoapAction := ::cSoapAction
   BEGIN SEQUENCE WITH __BreakBlock()
      ::cXmlRetorno := "Erro: Criando objeto MSXML2.ServerXMLHTTP"
      oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
      ::cXmlRetorno := "Erro: No uso do objeto MSXML2.ServerXmlHTTP"
      IF ::cCertificado != NIL
         oServer:setOption( 3, "CURRENT_USER\MY\" + ::cCertificado )
      ENDIF
      ::cXmlRetorno := "Erro: Na conexão com webservice " + ::cSoapURL
      oServer:Open( "POST", ::cSoapURL, .F. )
      oServer:SetRequestHeader( "SOAPAction", cSoapAction )
      oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
      oServer:Send( ::cXmlSoap )
      oServer:WaitForResponse( 500 )
      cRetorno := oServer:ResponseBody
      IF ValType( cRetorno ) == "C"
         ::cXmlRetorno := cRetorno
      ELSEIF cRetorno == NIL
         ::cXmlRetorno := "Erro: Sem retorno do webservice"
      ELSE
         ::cXmlRetorno := ""
         FOR nCont = 1 TO Len( cRetorno )
            ::cXmlRetorno += Chr( cRetorno[ nCont ] )
         NEXT
      ENDIF
   ENDSEQUENCE
   IF "<soap:Body>" $ ::cXmlRetorno .AND. "</soap:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soap:Body" ) // hb_UTF8ToStr()
   ELSEIF "<soapenv:Body>" $ ::cXmlRetorno .AND. "</soapenv:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soapenv:Body" ) // hb_UTF8ToStr()
   ELSE
      ::cXmlRetorno := "Erro SOAP: XML retorno não contém soapenv:Body " + ::cXmlRetorno
   ENDIF

   RETURN NIL
Falta: assinatura
O detalhe da assinatura, é identificar os blocos e colocar na lista em ze_SpedAssinaXml da SefazClass.
Se for do tipo que assina CADA RPS e mais o XML final, é assinar um RPS de cada vez, e só depois assinar o final.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
eduardomc00
Usuário Nível 1
Usuário Nível 1
Mensagens: 43
Registrado em: 07 Out 2014 18:29
Localização: São Paulo

NFS-e PMSP

Mensagem por eduardomc00 »

Tem vezes que estamos tão perdido que uma pessoa de fora enxerga coisas óbvias que não vemos. kkkkkkk :D :D

Eu fiz um teste com o meu código do jeito que ta, e funcionou no A3 tbm ! na máquina do financeiro :-o :-o :-o


está bem mais bonito o código assim !
Responder