Skip to content

Commit

Permalink
Refatoração de Erros e Retornos
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelEstevamReis committed Dec 15, 2023
1 parent fde17e4 commit 13ac0ec
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 23 deletions.
18 changes: 15 additions & 3 deletions Sicoob.Cobranca/SicoobCobranca.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Sicoob.Cobranca;

using Sicoob.Cobranca.Models;
using Sicoob.Shared.Models.Acesso;
using Sicoob.Shared.Models.Geral;
using Simple.API;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -122,10 +123,21 @@ private async Task<RetornoSolicitacaoMovimentacoesCarteira> SolicitarMovimentaca
var retorno = await ExecutaChamadaAsync(() => clientApi.PostAsync<ResponseMovimentacao<RetornoSolicitacaoMovimentacoesCarteira>>("/cobranca-bancaria/v2/boletos/solicitacoes/movimentacao", solicitacao));
return retorno.resultado;
}
public async Task<RetornoConsultaMovimentacoes> ConsultarSituacaoSolicitacao(int numeroContrato, int codigoSolicitacao)
public async Task<RetornoConsultaMovimentacoes?> ConsultarSituacaoSolicitacao(int numeroContrato, int codigoSolicitacao)
{
var retorno = await ExecutaChamadaAsync(() => clientApi.GetAsync<ResponseMovimentacao<RetornoConsultaMovimentacoes>>("/cobranca-bancaria/v2/boletos/solicitacoes/movimentacao", new { numeroContrato, codigoSolicitacao }));
return retorno.resultado;
var result = await clientApi.GetAsync<ResponseMovimentacao<RetornoConsultaMovimentacoes>>("/cobranca-bancaria/v2/boletos/solicitacoes/movimentacao", new { numeroContrato, codigoSolicitacao });

if (result.IsSuccessStatusCode) return result.Data.resultado;

// "{\"mensagens\":[{\"mensagem\":\"Solicitação ainda em processamento.\",\"codigo\":\"5004\"}]}"
if (result.TryParseErrorResponseData(out ErroRequisicao err))
{
if (err.mensagens.Any(o => o.codigo == 5004)) return null;

throw new ErroRequisicaoException(err);
}
result.EnsureSuccessStatusCode(); // Erro comum
return null; // a linha de cima vai arremessar o erro padrão
}
internal async Task<RetornoArquivoMovimentacao> DownloadArquivoMovimentacao(int numeroContrato, int codigoSolicitacao, int idArquivo)
{
Expand Down
62 changes: 48 additions & 14 deletions Sicoob.PIX/SicoobPIX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ protected override void atualizaClients(TokenResponse token)
public async Task<CobrancaImediata> CriarCobrancaAsync(string transactionId, NovaCobrancaImediata cobranca)
{
validaTxID(transactionId);
return await ExecutaChamadaAsync(() => clientApi.PutAsync<CobrancaImediata>($"/pix/api/v2/cob/{transactionId}", cobranca));
return await ExecutaChamadaAsyncPIX(() => clientApi.PutAsync<CobrancaImediata>($"/pix/api/v2/cob/{transactionId}", cobranca));
}
/// <summary>
/// Endpoint para criar uma cobrança imediata, neste caso, o txid deve ser definido pelo PSP.
/// </summary>
/// <param name="cobranca">Dados para geração da cobrança imediata.</param>
/// <returns>Cobrança imediata criada</returns>
public async Task<CobrancaImediata> CriarCobrancaAsync(NovaCobrancaImediata cobranca)
=> await ExecutaChamadaAsync(() => clientApi.PostAsync<CobrancaImediata>($"/pix/api/v2/cob", cobranca));
=> await ExecutaChamadaAsyncPIX(() => clientApi.PostAsync<CobrancaImediata>($"/pix/api/v2/cob", cobranca));
/// <summary>
/// Endpoint para revisar uma cobrança através de um determinado txid.
/// </summary>
Expand All @@ -82,7 +82,7 @@ public async Task<CobrancaImediata> CriarCobrancaAsync(NovaCobrancaImediata cobr
public async Task<CobrancaImediata> RevisarCobrancaAsync(string transactionId, RevisarCobrancaImediata cobranca)
{
validaTxID(transactionId);
return await ExecutaChamadaAsync(() => clientApi.PatchAsync<CobrancaImediata>($"/pix/api/v2/cob/{transactionId}", cobranca));
return await ExecutaChamadaAsyncPIX(() => clientApi.PatchAsync<CobrancaImediata>($"/pix/api/v2/cob/{transactionId}", cobranca));
}

/// <summary>
Expand All @@ -98,15 +98,15 @@ public async Task<CobrancaImediata> ConsultarCobrancaAsync(string transactionId,
string url = $"/pix/api/v2/cob/{transactionId}";
if (revisao.HasValue) url += $"?revisao={revisao.Value}";

return await ExecutaChamadaAsync(() => clientApi.GetAsync<CobrancaImediata>(url));
return await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<CobrancaImediata>(url));
}
/// <summary>
/// Endpoint para consultar cobranças imediatas através de parâmetros como início, fim, cpf, cnpj e status.
/// </summary>
/// <param name="consulta">Dados da consulta</param>
/// <returns>Lista de cobranças imediatas.</returns>
public async Task<ListagemCobrancaImediata> ListarCobrancasAsync(ConsultarCobrancaImediata consulta)
=> await ExecutaChamadaAsync(() => clientApi.GetAsync<ListagemCobrancaImediata>("/pix/api/v2/cob", consulta.ToKVP()));
=> await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<ListagemCobrancaImediata>("/pix/api/v2/cob", consulta.ToKVP()));

/// <summary>
/// Endpoint para gerar a imagem qrcode de uma cobrança através de um determinado txid.
Expand All @@ -121,7 +121,7 @@ public async Task<byte[]> ConsultarImagemCobrancaAsync(string transactionId, int
validaTxID(transactionId);
string url = $"/pix/api/v2/cob/{transactionId}/imagem";

return await ExecutaChamadaAsync(() => clientApi.GetAsync<byte[]>(url, new { revisao, largura }.ToKVP()));
return await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<byte[]>(url, new { revisao, largura }.ToKVP()));
}

/* COBV */
Expand All @@ -137,14 +137,14 @@ public async Task<byte[]> ConsultarImagemCobrancaAsync(string transactionId, int
/// <param name="consulta">Dados da consulta</param>
/// <returns>Lista dos Pix recebidos de acordo com o critério de busca.</returns>
public async Task<ListagemPixRecebido> ListarPIXAsync(ConsultarPix consulta)
=> await ExecutaChamadaAsync(() => clientApi.GetAsync<ListagemPixRecebido>("/pix/api/v2/pix", consulta.ToKVP()));
=> await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<ListagemPixRecebido>("/pix/api/v2/pix", consulta.ToKVP()));
/// <summary>
/// Endpoint para consultar um Pix através de um e2eid.
/// </summary>
/// <param name="endToEndId">Id fim a fim da transação. Deve ter 32 caracteres.</param>
/// <returns>Dados do Pix efetuado.</returns>
public async Task<PixRecebido> ConsultarPIXAsync(string endToEndId)
=> await ExecutaChamadaAsync(() => clientApi.GetAsync<PixRecebido>($"/pix/api/v2/pix/{endToEndId}"));
=> await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<PixRecebido>($"/pix/api/v2/pix/{endToEndId}"));

/// <summary>
/// Endpoint para solicitar uma devolução através de um e2eid do Pix e do ID da devolução.
Expand All @@ -159,7 +159,7 @@ public async Task<PixDevolucao> SolicitarDevlucaoPixAsync(string endToEndId, str
{
validaIDDevolucao(idDevolucao);
string url = $"/pix/api/v2/pix/{endToEndId}/devolucao/{idDevolucao}";
return await ExecutaChamadaAsync(() => clientApi.PutAsync<PixDevolucao>(url, new { valor = valor.ToString("N2", CultureInfo.InvariantCulture) }));
return await ExecutaChamadaAsyncPIX(() => clientApi.PutAsync<PixDevolucao>(url, new { valor = valor.ToString("N2", CultureInfo.InvariantCulture) }));
}
/// <summary>
/// Endpoint para consultar uma devolução através de um EndToEndID do Pix e do ID da devolução
Expand All @@ -170,7 +170,7 @@ public async Task<PixDevolucao> SolicitarDevlucaoPixAsync(string endToEndId, str
public async Task<PixDevolucao> ConsultarDevlucaoPixAsync(string endToEndId, string idDevolucao)
{
string url = $"/pix/api/v2/pix/{endToEndId}/devolucao/{idDevolucao}";
return await ExecutaChamadaAsync(() => clientApi.GetAsync<PixDevolucao>(url));
return await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<PixDevolucao>(url));
}

/* Webhook */
Expand All @@ -182,23 +182,23 @@ public async Task<PixDevolucao> ConsultarDevlucaoPixAsync(string endToEndId, str
/// <param name="url">Url a ser chamada com POST. Será concatenado `/pix` ao final.</param>
public async Task CriarWebHookAsync(string chave, string url)
{
await ExecutaChamadaAsync(() => clientApi.PutAsync($"/pix/api/v2/webhook/{chave}", new { webhookUrl = url }));
await ExecutaChamadaAsyncPIX(() => clientApi.PutAsync($"/pix/api/v2/webhook/{chave}", new { webhookUrl = url }));
}
/// <summary>
/// Endpoint para consultar Webhooks cadastrados
/// </summary>
public async Task<ListagemWebhookAtivo> ConsultarWebHooksAsync()
=> await ExecutaChamadaAsync(() => clientApi.GetAsync<ListagemWebhookAtivo>("/pix/api/v2/webhook"));
=> await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<ListagemWebhookAtivo>("/pix/api/v2/webhook"));
/// <summary>
/// Endpoint para recuperação de informações sobre o Webhook Pix.
/// </summary>
public async Task<WebhookAtivo> ConsultarWebHookAsync(string chave)
=> await ExecutaChamadaAsync(() => clientApi.GetAsync<WebhookAtivo>($"/pix/api/v2/webhook/{chave}"));
=> await ExecutaChamadaAsyncPIX(() => clientApi.GetAsync<WebhookAtivo>($"/pix/api/v2/webhook/{chave}"));
/// <summary>
/// Endpoint para cancelamento do webhook. Não é a única forma pela qual um webhook pode ser removido.
/// </summary>
public async Task CancelarWebHookAsync(string chave)
=> await ExecutaChamadaAsync(() => clientApi.DeleteAsync($"/pix/api/v2/webhook/{chave}"));
=> await ExecutaChamadaAsyncPIX(() => clientApi.DeleteAsync($"/pix/api/v2/webhook/{chave}"));

/* Validação de IDs */
private static void validaTxID(string transactionId)
Expand Down Expand Up @@ -258,4 +258,38 @@ public Task<ConsultaLotesCobranca> ListarLoteCobrancaVencimentoAsync(Consulta co
{
throw new NotImplementedException();
}


/* Helpers*/
private async Task<T> ExecutaChamadaAsyncPIX<T>(Func<Task<Response<T>>> func)
{
await VerificaAtualizaCredenciaisAsync();
Response<T> response = await func();

if (!response.IsSuccessStatusCode)
{
if (response.TryParseErrorResponseData(out CS.BCB.PIX.Models.ErroRequisicao err))
{
throw new CS.BCB.PIX.Excecoes.ErroRequisicaoException(err);
}
}
response.EnsureSuccessStatusCode();

return response.Data;
}
private async Task ExecutaChamadaAsyncPIX(Func<Task<Response>> func)
{
await VerificaAtualizaCredenciaisAsync();
Response response = await func();

// Processa manualmente para não envelopar demais
if (response.IsSuccessStatusCode) return;
if (response.TryParseErrorResponseData(out CS.BCB.PIX.Models.ErroRequisicao err))
{
throw new CS.BCB.PIX.Excecoes.ErroRequisicaoException(err);
}
// Se não era um ErroRequisição, usar o erro comum
response.EnsureSuccessStatusCode();
}

}
36 changes: 36 additions & 0 deletions Sicoob.Shared/Models/Geral/ResponseErro.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Sicoob.Shared.Models.Geral;

using System;
using System.Linq;

public class ErroRequisicao
{
public Mensagens[] mensagens { get; set; }

Check warning on line 8 in Sicoob.Shared/Models/Geral/ResponseErro.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'mensagens' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public string ObterMensagemErro()
{
return string.Join("; ", mensagens.Select(m => m.mensagem));
}

public class Mensagens
{
public string mensagem { get; set; }

Check warning on line 17 in Sicoob.Shared/Models/Geral/ResponseErro.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'mensagem' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public int codigo { get; set; }
}
}
public class ErroRequisicaoException : Exception
{
public ErroRequisicao DadosErro { get; }

public ErroRequisicaoException(ErroRequisicao erro)
: base(erro.ObterMensagemErro())
{
DadosErro = erro;
}

public ErroRequisicaoException(ErroRequisicao erro, Exception innerException)
: base(erro.ObterMensagemErro(), innerException)
{
DadosErro = erro;
}
}
1 change: 0 additions & 1 deletion Sicoob.Shared/Sicoob.Shared.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CS.BCB.PIX" Version="0.9.2" />
<PackageReference Include="Simple.API" Version="1.7.1.2" />
</ItemGroup>

Expand Down
11 changes: 6 additions & 5 deletions Sicoob.Shared/Sicoob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
\**************************************/
namespace Sicoob.Shared;

using global::Sicoob.Shared.Models.Geral;
using Simple.API;
using System;
using System.Net.Http;
Expand Down Expand Up @@ -113,9 +114,9 @@ protected async Task<T> ExecutaChamadaAsync<T>(Func<Task<Response<T>>> func)

if (!response.IsSuccessStatusCode)
{
if (response.TryParseErrorResponseData(out CS.BCB.PIX.Models.ErroRequisicao err))
if (response.TryParseErrorResponseData(out ErroRequisicao err))
{
throw new CS.BCB.PIX.Excecoes.ErroRequisicaoException(err);
throw new ErroRequisicaoException(err);
}
}
response.EnsureSuccessStatusCode();
Expand All @@ -126,12 +127,12 @@ protected async Task ExecutaChamadaAsync(Func<Task<Response>> func)
{
await VerificaAtualizaCredenciaisAsync();
Response response = await func();

// Processa manualmente para não envelopar demais
if (response.IsSuccessStatusCode) return;
if (response.TryParseErrorResponseData(out CS.BCB.PIX.Models.ErroRequisicao err))
if (response.TryParseErrorResponseData(out ErroRequisicao err))
{
throw new CS.BCB.PIX.Excecoes.ErroRequisicaoException(err);
throw new ErroRequisicaoException(err);
}
// Se não era um ErroRequisição, usar o erro comum
response.EnsureSuccessStatusCode();
Expand Down

0 comments on commit 13ac0ec

Please sign in to comment.