diff --git a/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs b/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs index bb252184d..827cd2a1e 100644 --- a/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs +++ b/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs @@ -269,7 +269,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CteStatusServico = @"https://cte.fazenda.mg.gov.br/cte/services/CTeStatusServicoV4", CteRecepcaoOs = @"https://cte.fazenda.mg.gov.br/cte/services/CTeRecepcaoOSV4", CteRecepcaoGtve = @"https://cte.fazenda.mg.gov.br/cte/services/CTeRecepcaoGTVeV4", - QrCode = @"https://cte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml" + QrCode = @"https://portalcte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml" }; } @@ -281,7 +281,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CteInutilizacao = @"https://cte.fazenda.mg.gov.br/cte/services/CteInutilizacao", CteRecepcaoEvento = @"https://cte.fazenda.mg.gov.br/cte/services/RecepcaoEvento", CteConsulta = @"https://cte.fazenda.mg.gov.br/cte/services/CteConsulta", - QrCode = @"https://cte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml", + QrCode = @"https://portalcte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml", CTeDistribuicaoDFe = "https://www1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.PR: @@ -488,7 +488,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) CteStatusServico = @"https://hcte.fazenda.mg.gov.br/cte/services/CTeStatusServicoV4", CteRecepcaoOs = @"https://hcte.fazenda.mg.gov.br/cte/services/CTeRecepcaoOSV4", CteRecepcaoGtve = @"https://hcte.fazenda.mg.gov.br/cte/services/CTeRecepcaoGTVeV4", - QrCode = @"https://cte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml" + QrCode = @"https://portalcte.fazenda.mg.gov.br/portalcte/sistema/qrcode.xhtml" }; } diff --git a/DFe.Utils/Assinatura/AssinaturaDigital.cs b/DFe.Utils/Assinatura/AssinaturaDigital.cs index 3c099e3b7..097acf2fb 100644 --- a/DFe.Utils/Assinatura/AssinaturaDigital.cs +++ b/DFe.Utils/Assinatura/AssinaturaDigital.cs @@ -116,5 +116,18 @@ public static byte[] ObterHashSha1Bytes(byte[] dados) return sha1HashBytes; } } + + /// + /// Obtém a assinatura do certificado digital no formato PKCS#1, baseado em um array de bytes passado como Argumento [value]. + /// + /// + /// + /// + public static byte[] ObterAssinaturaPkcs1(ConfiguracaoCertificado configuracaoCertificado, byte[] value) + { + X509Certificate2 certificado = CertificadoDigital.ObterCertificado(configuracaoCertificado); + using (RSA rsa = certificado.GetRSAPrivateKey()) + return rsa.SignData(value, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + } } } \ No newline at end of file diff --git a/NFe.AppTeste/MainWindow.xaml b/NFe.AppTeste/MainWindow.xaml index 7a793539b..f0250500c 100644 --- a/NFe.AppTeste/MainWindow.xaml +++ b/NFe.AppTeste/MainWindow.xaml @@ -233,8 +233,9 @@ IsEnabled="{Binding CfgServico.ModeloDocumento, ConverterParameter={x:Static flags:ModeloDocumento.NFCe}, Converter={StaticResource EnumParaBool}}"> - - + + + + diff --git a/NFe.AppTeste/MainWindow.xaml.cs b/NFe.AppTeste/MainWindow.xaml.cs index 1409fa640..e12a965ae 100644 --- a/NFe.AppTeste/MainWindow.xaml.cs +++ b/NFe.AppTeste/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -/********************************************************************************/ +/********************************************************************************/ /* Projeto: Biblioteca ZeusNFe */ /* Biblioteca C# para emissão de Nota Fiscal Eletrônica - NFe e Nota Fiscal de */ /* Consumidor Eletrônica - NFC-e (http://www.nfe.fazenda.gov.br) */ @@ -381,7 +381,7 @@ private void BtnInsucessoEntrega_Click(object sender, RoutedEventArgs e) Funcoes.Mensagem(ex.Message, "Erro", MessageBoxButton.OK); } } - + private void BtnCancInsucessoEntrega_Click(object sender, RoutedEventArgs e) { const string titulo = "Cancelar Insucesso Entrega NFe"; @@ -497,7 +497,7 @@ private void BtnComprovanteEntrega_Click(object sender, RoutedEventArgs e) : _configuracoes.Emitente.CNPJ; var retornoComprovante = servicoNFe.RecepcaoEventoComprovanteEntrega(Convert.ToInt32(idlote), - Convert.ToInt16(sequenciaEvento), cpfcnpj, chave, dhEntrega, nDoc, xNome, hashComprovante, + Convert.ToInt16(sequenciaEvento), cpfcnpj, chave, dhEntrega, nDoc, xNome, hashComprovante, dhHashComprovante, latGps, longGps, DFe.Classes.Entidades.Estado.SP); TrataRetorno(retornoComprovante); @@ -769,7 +769,7 @@ private Classes.NFe ObterNfeValidada(VersaoServico versaoServico, ModeloDocument nfe.infNFeSupl = new infNFeSupl(); if (versaoServico == VersaoServico.Versao400) nfe.infNFeSupl.urlChave = nfe.infNFeSupl.ObterUrlConsulta(nfe, _configuracoes.ConfiguracaoDanfeNfce.VersaoQrCode); - nfe.infNFeSupl.qrCode = nfe.infNFeSupl.ObterUrlQrCode(nfe, _configuracoes.ConfiguracaoDanfeNfce.VersaoQrCode, configuracaoCsc.CIdToken, configuracaoCsc.Csc); + nfe.infNFeSupl.qrCode = nfe.infNFeSupl.ObterUrlQrCode(nfe, _configuracoes.ConfiguracaoDanfeNfce.VersaoQrCode, configuracaoCsc.CIdToken, configuracaoCsc.Csc, _configuracoes.CfgServico.Certificado); } nfe.Valida(); @@ -1464,7 +1464,7 @@ protected virtual dest GetDestinatario(VersaoServico versao, ModeloDocumento mod }; dest.xNome = "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL"; //Obrigatório para NFe e opcional para NFCe dest.enderDest = GetEnderecoDestinatario(); //Obrigatório para NFe e opcional para NFCe - + //if (versao == VersaoServico.Versao200) // dest.IE = "ISENTO"; if (versao == VersaoServico.Versao200) return dest; @@ -1836,8 +1836,8 @@ protected virtual List GetPagamento(ICMSTot icmsTot, VersaoServico versao) { detPag = new List { - new detPag {tPag = FormaPagamento.fpCreditoEmLoja, vPag = valorPagto}, - new detPag {tPag = FormaPagamento.fpCreditoEmLoja, vPag = icmsTot.vNF - valorPagto} + new detPag {tPag = FormaPagamento.fpDinheiro, vPag = valorPagto}, + new detPag {tPag = FormaPagamento.fpCheque, vPag = icmsTot.vNF - valorPagto} } } }; diff --git a/NFe.Classes/Informacoes/Detalhe/gCred.cs b/NFe.Classes/Informacoes/Detalhe/gCred.cs index 425aef414..e4945858a 100644 --- a/NFe.Classes/Informacoes/Detalhe/gCred.cs +++ b/NFe.Classes/Informacoes/Detalhe/gCred.cs @@ -30,12 +30,12 @@ public decimal? vCredPresumido public bool ShouldSerializepCredPresumido() { - return _pCredPresumido.HasValue && _pCredPresumido > 0; + return _pCredPresumido.HasValue; } public bool ShouldSerializevCredPresumido() { - return _vCredPresumido.HasValue && _vCredPresumido > 0; + return _vCredPresumido.HasValue; } } } diff --git a/NFe.Danfe.AppTeste.Fast/MainWindow.xaml b/NFe.Danfe.AppTeste.Fast/MainWindow.xaml index d10e6babf..cbd75fdf1 100644 --- a/NFe.Danfe.AppTeste.Fast/MainWindow.xaml +++ b/NFe.Danfe.AppTeste.Fast/MainWindow.xaml @@ -122,12 +122,16 @@ + Margin="10,5,0,0" + IsChecked="{Binding ConfiguracaoDanfeNfce.VersaoQrCode, ConverterParameter={x:Static nfeUtils:VersaoQrCode.QrCodeVersao1}, Converter={StaticResource EnumParaBool}}" Width="90" /> + Margin="110,5,0,0" + IsChecked="{Binding ConfiguracaoDanfeNfce.VersaoQrCode, ConverterParameter={x:Static nfeUtils:VersaoQrCode.QrCodeVersao2}, Converter={StaticResource EnumParaBool}}" Width="90"/> + diff --git a/NFe.Danfe.Base/NFe/NFeRetrato.frx b/NFe.Danfe.Base/NFe/NFeRetrato.frx index e12986475..799aedef4 100644 --- a/NFe.Danfe.Base/NFe/NFeRetrato.frx +++ b/NFe.Danfe.Base/NFe/NFeRetrato.frx @@ -1444,7 +1444,7 @@ namespace FastReport - + diff --git a/NFe.Utils/Enums.cs b/NFe.Utils/Enums.cs index b72e16980..adc5b1f5a 100644 --- a/NFe.Utils/Enums.cs +++ b/NFe.Utils/Enums.cs @@ -50,6 +50,10 @@ public enum VersaoQrCode QrCodeVersao1 = 100, [Description("Versão 2.0 do QR-Code")] - QrCodeVersao2 = 2 + QrCodeVersao2 = 2, + + [Description("Versão 3.0 do QR-Code")] + QrCodeVersao3 = 3, + } } \ No newline at end of file diff --git a/NFe.Utils/Excecoes/ValidacaoSchemaException.cs b/NFe.Utils/Excecoes/ValidacaoSchemaException.cs index e06d36927..a7591ee1b 100644 --- a/NFe.Utils/Excecoes/ValidacaoSchemaException.cs +++ b/NFe.Utils/Excecoes/ValidacaoSchemaException.cs @@ -48,6 +48,12 @@ public class ValidacaoSchemaException : Exception /// Houve erros de validação de schema XSD /// /// - public ValidacaoSchemaException(string message) : base(string.Format("Erros na validação:\n {0}", message)) {} + public ValidacaoSchemaException(string message, string xmlString) : base( + $"Erros na validação:\n {message}") + { + XmlString = xmlString; + } + + public string XmlString { get; private set; } } } diff --git a/NFe.Utils/InformacoesSuplementares/ExtinfNFeSupl.cs b/NFe.Utils/InformacoesSuplementares/ExtinfNFeSupl.cs index b205f8887..3e1506b3a 100644 --- a/NFe.Utils/InformacoesSuplementares/ExtinfNFeSupl.cs +++ b/NFe.Utils/InformacoesSuplementares/ExtinfNFeSupl.cs @@ -37,8 +37,10 @@ using DFe.Classes.Entidades; using DFe.Classes.Flags; using DFe.Utils; +using DFe.Utils.Assinatura; using NFe.Classes; using NFe.Classes.Informacoes.Identificacao.Tipos; +using System.Text; namespace NFe.Utils.InformacoesSuplementares { @@ -127,7 +129,7 @@ private static List CarregarUrls() {Estado.RR, versao3E4, "https://www.sefaz.rr.gov.br/nfce/servlet/qrcode"}, {Estado.MG, versao3E4, "https://portalsped.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml"} }; - adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao1, VersaoQrCode.QrCodeVersao2 }, urlsQrCodeProducaoQrCode1E2); + adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao1, VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsQrCodeProducaoQrCode1E2); var urlsQrCodeProducaoQrCode1 = new TupleList { @@ -149,7 +151,7 @@ private static List CarregarUrls() {Estado.TO, versao4, "http://apps.sefaz.to.gov.br/portal-nfce/qrcodeNFCe"}, {Estado.SC, versao4, "https://sat.sef.sc.gov.br/nfce/consulta"} }; - adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao2 }, urlsQrCodeProducaoQrCode2); + adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsQrCodeProducaoQrCode2); #endregion @@ -181,7 +183,7 @@ private static List CarregarUrls() {Estado.TO, versao3E4, "http://apps.sefaz.to.gov.br/portal-nfce-homologacao/qrcodeNFCe"}, {Estado.MG, versao3E4, "https://portalsped.fazenda.mg.gov.br/portalnfce/sistema/qrcode.xhtml"} }; - adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao1, VersaoQrCode.QrCodeVersao2 }, urlsQrCodeHomologacaoQrCode1E2); + adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao1, VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsQrCodeHomologacaoQrCode1E2); var urlsQrCodeHomologacaoQrCode1 = new TupleList { @@ -199,7 +201,7 @@ private static List CarregarUrls() {Estado.SP, versao4, "https://www.homologacao.nfce.fazenda.sp.gov.br/qrcode"}, {Estado.SC, versao4, "https://hom.sat.sef.sc.gov.br/nfce/consulta"}, }; - adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao2 }, urlsQrCodeHomologacaoQrCode2); + adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlQrCode, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsQrCodeHomologacaoQrCode2); #endregion @@ -306,8 +308,8 @@ private static List CarregarUrls() {Estado.RR, versao3E4, "www.sefaz.rr.gov.br/nfce/consulta"} }; - adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2 }, urlsConsultaHomologacaoEProducao2); - adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2 }, urlsConsultaHomologacaoEProducao2); + adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsConsultaHomologacaoEProducao2); + adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsConsultaHomologacaoEProducao2); #endregion @@ -326,7 +328,7 @@ private static List CarregarUrls() {Estado.SC, versao3E4, "https://sat.sef.sc.gov.br/nfce/consulta" } }; - adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2 }, urlsConsultaProducao2); + adicionarUrls(TipoAmbiente.Producao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsConsultaProducao2); #endregion @@ -345,7 +347,7 @@ private static List CarregarUrls() {Estado.SC, versao3E4, "https://hom.sat.sef.sc.gov.br/nfce/consulta" } }; - adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2 }, urlsConsultaHomologacao2); + adicionarUrls(TipoAmbiente.Homologacao, TipoUrlConsultaPublica.UrlConsulta, new[] { VersaoQrCode.QrCodeVersao2, VersaoQrCode.QrCodeVersao3 }, urlsConsultaHomologacao2); #endregion @@ -388,7 +390,7 @@ public static string ObterUrlConsulta(this infNFeSupl infNFeSupl, Classes.NFe nf /// /// Obtém a URL para montagem do QR-Code /// - public static string ObterUrlQrCode(this infNFeSupl infNFeSupl, Classes.NFe nfe, VersaoQrCode versaoQrCode, string cIdToken, string csc) + public static string ObterUrlQrCode(this infNFeSupl infNFeSupl, Classes.NFe nfe, VersaoQrCode versaoQrCode, string cIdToken, string csc, ConfiguracaoCertificado _cfgCertificado = null) { Func msgErro = parametro => $"O {parametro} não foi informado!"; @@ -405,6 +407,8 @@ public static string ObterUrlQrCode(this infNFeSupl infNFeSupl, Classes.NFe nfe, return ObterUrlQrCode1(infNFeSupl, nfe, cIdToken, csc, versaoServico); case VersaoQrCode.QrCodeVersao2: return ObterUrlQrCode2(infNFeSupl, nfe, cIdToken, csc, versaoServico); + case VersaoQrCode.QrCodeVersao3: + return ObterUrlQrCode3(infNFeSupl, nfe, versaoServico, _cfgCertificado); default: throw new ArgumentOutOfRangeException("versaoQrCode", versaoQrCode, null); } @@ -430,7 +434,7 @@ private static string ObterUrlQrCode1(infNFeSupl infNFeSupl, Classes.NFe nfe, st if (nfe.infNFe.dest != null) cDest = "&cDest=" + nfe.infNFe.dest.CPF + nfe.infNFe.dest.CNPJ + nfe.infNFe.dest.idEstrangeiro; - //Passo 3: Substituir os valores (“dhEmi” e “digVal”) nos parâmetros; + //Passo 3: Substituir os valores ("dhEmi" e "digVal") nos parâmetros; var dadosBase = "chNFe=" + nfe.infNFe.Id.Substring(3) + "&nVersao=100" + @@ -519,5 +523,58 @@ public static string ObterUrlQrCode2ComParametro(this infNFeSupl infNFeSupl, Tip url += parametro; return url; } + + /// + /// Obtém a URL para uso no QR-Code, versão 3.0 - leiaute 4.00+ + /// + public static string ObterUrlQrCode3(this infNFeSupl infNFeSupl, Classes.NFe nfe, VersaoServico versaoServico, ConfiguracaoCertificado cfgCertificado, Encoding encoding = null) + { + if (cfgCertificado == null || string.IsNullOrWhiteSpace(cfgCertificado.Serial)) + throw new ArgumentNullException("CertificadoDigital", "Para gerar a assinatura do QR-Code versão 3.0 EM CONTINGENCIA é necessário informar o certificado digital utilizado na assinatura da NFC-e, verificar Número de Série e Senha."); + + const string pipe = "|"; + + string chave = nfe.infNFe.Id.Substring(3); + int versaoQrCode = 3; + int ambiente = (int)nfe.infNFe.ide.tpAmb; + string dadosBase = string.Concat(chave, pipe, versaoQrCode, pipe, ambiente); + + if (nfe.infNFe.ide.tpEmis == TipoEmissao.teOffLine) + { + string diaEmi = nfe.infNFe.ide.dhEmi.Day.ToString("D2"); + string valorNfce = nfe.infNFe.total.ICMSTot.vNF.ToString("0.00").Replace(',', '.'); + string tp_idDest = string.Empty; + string idDest = string.Empty; + if (nfe.infNFe.dest != null) + { + if (!string.IsNullOrEmpty(nfe.infNFe.dest.idEstrangeiro)) + { + tp_idDest = "3"; + idDest = nfe.infNFe.dest.idEstrangeiro.Trim(); + } + else if (!string.IsNullOrEmpty(nfe.infNFe.dest.CPF) && nfe.infNFe.dest.CPF.Length == 11) + { + tp_idDest = "2"; + idDest = nfe.infNFe.dest.CPF.Replace(".", "").Replace("/", "").Replace("-", "").Trim(); + } + else if (!string.IsNullOrEmpty(nfe.infNFe.dest.CNPJ) && nfe.infNFe.dest.CNPJ.Length == 14) + { + tp_idDest = "1"; + idDest = nfe.infNFe.dest.CNPJ.Replace(".", "").Replace("/", "").Replace("-", "").Trim(); + } + } + + dadosBase = string.Concat(dadosBase, pipe, diaEmi, pipe, valorNfce, pipe, tp_idDest, pipe, idDest); + + if (encoding == null) + encoding = Encoding.UTF8; + + string assinatura = Convert.ToBase64String(AssinaturaDigital.ObterAssinaturaPkcs1(cfgCertificado, encoding.GetBytes(dadosBase))); + dadosBase = string.Concat(dadosBase, pipe, assinatura); + } + + string url = ObterUrlQrCode2ComParametro(infNFeSupl, nfe.infNFe.ide.tpAmb, nfe.infNFe.ide.cUF, versaoServico); + return string.Concat(url, dadosBase); + } } } diff --git a/NFe.Utils/Validacao/Validador.cs b/NFe.Utils/Validacao/Validador.cs index cf30b2ae7..01375c877 100644 --- a/NFe.Utils/Validacao/Validador.cs +++ b/NFe.Utils/Validacao/Validador.cs @@ -206,7 +206,7 @@ public static string[] Valida(ServicoNFe servicoNFe, VersaoServico versaoServico } if (falhas.Length > 0) - throw new ValidacaoSchemaException($"Ocorreu o seguinte erro durante a validação XML: {Environment.NewLine}{falhas}"); + throw new ValidacaoSchemaException($"Ocorreu o seguinte erro durante a validação XML: {Environment.NewLine}{falhas}", stringXml); return falhas.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); }