ajustes sobre verifactu y el banco
This commit is contained in:
parent
62bc070115
commit
e0ab16f82d
|
|
@ -0,0 +1,90 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using DoliMiddlewareApi.Dtos.VeriFactu;
|
||||||
|
using DoliMiddlewareApi.Services.VeriFactu;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace DoliMiddlewareApi.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class VeriFactuController(VeriFactuApiClient verifactuApiClient) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet("health")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> Health()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await verifactuApiClient.GetHealthAsync();
|
||||||
|
return Content(result.Content ?? "{\"status\":\"ok\"}", result.ContentType ?? "application/json");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException)
|
||||||
|
{
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
status = "down",
|
||||||
|
error = $"Connection refused ({verifactuApiClient.BaseUrl})",
|
||||||
|
upstream = verifactuApiClient.BaseUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("public-key")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> PublicKey()
|
||||||
|
{
|
||||||
|
return await Proxy(verifactuApiClient.GetPublicKeyAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("formats")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> Formats()
|
||||||
|
{
|
||||||
|
return await Proxy(verifactuApiClient.GetFormatsAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("certificates/register")]
|
||||||
|
public async Task<IActionResult> RegisterCertificate([FromBody] RegisterCertificateDto dto)
|
||||||
|
{
|
||||||
|
var logger = HttpContext.RequestServices.GetRequiredService<ILogger<VeriFactuController>>();
|
||||||
|
logger.LogInformation("[VeriFactuController] RegisterCertificate called. CertName={CertName}, CertFile length={CertFileLen}, Password length={PasswordLen}",
|
||||||
|
dto.CertName, dto.CertFile?.Length ?? 0, dto.Password?.Length ?? 0);
|
||||||
|
return await Proxy(verifactuApiClient.RegisterCertificateAsync(dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("facturas")]
|
||||||
|
public async Task<IActionResult> SendInvoice([FromBody] JsonElement payload)
|
||||||
|
{
|
||||||
|
return await Proxy(verifactuApiClient.SendInvoiceAsync(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("facturas/anular")]
|
||||||
|
public async Task<IActionResult> CancelInvoice([FromBody] JsonElement payload)
|
||||||
|
{
|
||||||
|
return await Proxy(verifactuApiClient.CancelInvoiceAsync(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IActionResult> Proxy(Task<ProxyResponse> task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await task;
|
||||||
|
return new ContentResult
|
||||||
|
{
|
||||||
|
StatusCode = result.StatusCode,
|
||||||
|
Content = result.Content,
|
||||||
|
ContentType = result.ContentType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (HttpRequestException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status503ServiceUnavailable, new
|
||||||
|
{
|
||||||
|
title = "VeriFactu unavailable",
|
||||||
|
status = 503,
|
||||||
|
detail = $"No se puede conectar con VeriFactu en {verifactuApiClient.BaseUrl}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace DoliMiddlewareApi.Dtos.VeriFactu;
|
||||||
|
|
||||||
|
public record ProxyResponse(int StatusCode, string? Content, string? ContentType);
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace DoliMiddlewareApi.Dtos.VeriFactu;
|
||||||
|
|
||||||
|
public class RegisterCertificateDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[StringLength(100)]
|
||||||
|
[JsonPropertyName("cert_name")]
|
||||||
|
public string CertName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[JsonPropertyName("cert_file")]
|
||||||
|
public string CertFile { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(1024)]
|
||||||
|
[JsonPropertyName("password")]
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ public class InvoiceDetailDto
|
||||||
public DateTime? Date { get; set; }
|
public DateTime? Date { get; set; }
|
||||||
public DateTime? ExpireDate { get; set; }
|
public DateTime? ExpireDate { get; set; }
|
||||||
public int ClientId { get; set; }
|
public int ClientId { get; set; }
|
||||||
|
public string? ClientName { get; set; }
|
||||||
public decimal? TotalHt { get; set; }
|
public decimal? TotalHt { get; set; }
|
||||||
public decimal? TotalTax { get; set; }
|
public decimal? TotalTax { get; set; }
|
||||||
public decimal? Total { get; set; }
|
public decimal? Total { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using DoliMiddlewareApi.Services;
|
||||||
using DoliMiddlewareApi.Services.Auth;
|
using DoliMiddlewareApi.Services.Auth;
|
||||||
using DoliMiddlewareApi.Services.Clients;
|
using DoliMiddlewareApi.Services.Clients;
|
||||||
using DoliMiddlewareApi.Services.Notifications;
|
using DoliMiddlewareApi.Services.Notifications;
|
||||||
|
using DoliMiddlewareApi.Services.VeriFactu;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Diagnostics;
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
@ -136,12 +137,21 @@ builder.Services.AddHttpClient("Dolibarr", client =>
|
||||||
client.BaseAddress = new Uri(baseUrl);
|
client.BaseAddress = new Uri(baseUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient("VeriFactu", client =>
|
||||||
|
{
|
||||||
|
var baseUrl = builder.Configuration["VeriFactu:ApiUrl"] ?? "http://localhost:6789/";
|
||||||
|
if (!baseUrl.EndsWith('/')) baseUrl += '/';
|
||||||
|
client.BaseAddress = new Uri(baseUrl);
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
});
|
||||||
|
|
||||||
builder.Services.AddScoped<IDolibarrApiClient>(sp =>
|
builder.Services.AddScoped<IDolibarrApiClient>(sp =>
|
||||||
{
|
{
|
||||||
var factory = sp.GetRequiredService<IHttpClientFactory>();
|
var factory = sp.GetRequiredService<IHttpClientFactory>();
|
||||||
var client = factory.CreateClient("Dolibarr");
|
var client = factory.CreateClient("Dolibarr");
|
||||||
var tokenCacheService = sp.GetRequiredService<DolibarrTokenCacheService>();
|
var tokenCacheService = sp.GetRequiredService<DolibarrTokenCacheService>();
|
||||||
return new DolibarrApiClient(client, tokenCacheService);
|
var config = sp.GetRequiredService<IConfiguration>();
|
||||||
|
return new DolibarrApiClient(client, tokenCacheService, config);
|
||||||
});
|
});
|
||||||
|
|
||||||
// =========================================
|
// =========================================
|
||||||
|
|
@ -163,6 +173,13 @@ builder.Services.AddScoped<DocumentService>();
|
||||||
// Servicio de negocio (setup/diccionarios)
|
// Servicio de negocio (setup/diccionarios)
|
||||||
builder.Services.AddScoped<SetupService>();
|
builder.Services.AddScoped<SetupService>();
|
||||||
|
|
||||||
|
// Cliente de VeriFactu MidAPI
|
||||||
|
builder.Services.AddScoped<VeriFactuApiClient>(sp =>
|
||||||
|
{
|
||||||
|
var factory = sp.GetRequiredService<IHttpClientFactory>();
|
||||||
|
return new VeriFactuApiClient(factory.CreateClient("VeriFactu"));
|
||||||
|
});
|
||||||
|
|
||||||
// Servicio de negocio (banco)
|
// Servicio de negocio (banco)
|
||||||
builder.Services.AddScoped<BankService>();
|
builder.Services.AddScoped<BankService>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using DoliMiddlewareApi.Exceptions;
|
using DoliMiddlewareApi.Exceptions;
|
||||||
using DoliMiddlewareApi.Services.Auth;
|
using DoliMiddlewareApi.Services.Auth;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace DoliMiddlewareApi.Services.Clients;
|
namespace DoliMiddlewareApi.Services.Clients;
|
||||||
|
|
||||||
public class DolibarrApiClient(HttpClient httpClient, DolibarrTokenCacheService tokenCacheService)
|
public class DolibarrApiClient(HttpClient httpClient, DolibarrTokenCacheService tokenCacheService, IConfiguration configuration)
|
||||||
: IDolibarrApiClient
|
: IDolibarrApiClient
|
||||||
{
|
{
|
||||||
public async Task<T> GetResourceAsync<T>(string endpoint) where T : class
|
public async Task<T> GetResourceAsync<T>(string endpoint) where T : class
|
||||||
|
|
@ -76,11 +77,10 @@ public class DolibarrApiClient(HttpClient httpClient, DolibarrTokenCacheService
|
||||||
|
|
||||||
private void AddDolibarrTokenHeader(HttpRequestMessage request)
|
private void AddDolibarrTokenHeader(HttpRequestMessage request)
|
||||||
{
|
{
|
||||||
var dolibarrToken = tokenCacheService.GetDolibarrToken();
|
var sharedKey = configuration["Dolibarr:ApiKey"];
|
||||||
if (!string.IsNullOrEmpty(dolibarrToken))
|
var token = !string.IsNullOrEmpty(sharedKey) ? sharedKey : tokenCacheService.GetDolibarrToken();
|
||||||
{
|
if (!string.IsNullOrEmpty(token))
|
||||||
request.Headers.Add("DOLAPIKEY", dolibarrToken);
|
request.Headers.Add("DOLAPIKEY", token);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,19 @@ public class InvoiceService(IDolibarrApiClient apiClient, INotificationService n
|
||||||
public async Task<InvoiceDetailDto> GetInvoiceAsync(int id)
|
public async Task<InvoiceDetailDto> GetInvoiceAsync(int id)
|
||||||
{
|
{
|
||||||
var data = await apiClient.GetResourceAsync<InvoiceDetailResponse>($"invoices/{id}");
|
var data = await apiClient.GetResourceAsync<InvoiceDetailResponse>($"invoices/{id}");
|
||||||
return InvoiceMapper.MapToInvoiceDetailDto(data);
|
var dto = InvoiceMapper.MapToInvoiceDetailDto(data);
|
||||||
|
|
||||||
|
if (dto.ClientId > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = await apiClient.GetResourceAsync<ClientResponse>($"thirdparties/{dto.ClientId}");
|
||||||
|
dto.ClientName = client?.name;
|
||||||
|
}
|
||||||
|
catch { /* best-effort */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<InvoiceDto>> GetInvoicesAsync(
|
public async Task<List<InvoiceDto>> GetInvoicesAsync(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
using DoliMiddlewareApi.Dtos.VeriFactu;
|
||||||
|
|
||||||
|
namespace DoliMiddlewareApi.Services.VeriFactu;
|
||||||
|
|
||||||
|
public class VeriFactuApiClient(HttpClient httpClient)
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
|
public string BaseUrl => httpClient.BaseAddress?.ToString() ?? "http://host.docker.internal:6789/";
|
||||||
|
|
||||||
|
public Task<ProxyResponse> GetHealthAsync() => SendAsync(HttpMethod.Get, "api/v1/health");
|
||||||
|
|
||||||
|
public Task<ProxyResponse> GetPublicKeyAsync() => SendAsync(HttpMethod.Get, "api/v1/auth/public-key");
|
||||||
|
|
||||||
|
public Task<ProxyResponse> GetFormatsAsync() => SendAsync(HttpMethod.Get, "api/v1/formats");
|
||||||
|
|
||||||
|
public async Task<ProxyResponse> RegisterCertificateAsync(RegisterCertificateDto dto)
|
||||||
|
{
|
||||||
|
var payload = new
|
||||||
|
{
|
||||||
|
cert_name = dto.CertName,
|
||||||
|
cert_file = dto.CertFile,
|
||||||
|
password_encrypted = dto.Password
|
||||||
|
};
|
||||||
|
|
||||||
|
return await SendAsync(HttpMethod.Post, "api/v1/auth/register", payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ProxyResponse> SendInvoiceAsync(JsonElement payload)
|
||||||
|
=> SendAsync(HttpMethod.Post, "api/v1/facturas", payload);
|
||||||
|
|
||||||
|
public Task<ProxyResponse> CancelInvoiceAsync(JsonElement payload)
|
||||||
|
=> SendAsync(HttpMethod.Post, "api/v1/facturas/anular", payload);
|
||||||
|
|
||||||
|
private async Task<ProxyResponse> SendAsync(HttpMethod method, string path, object? payload = null)
|
||||||
|
{
|
||||||
|
using var request = new HttpRequestMessage(method, path);
|
||||||
|
|
||||||
|
if (payload is not null)
|
||||||
|
{
|
||||||
|
request.Content = JsonContent.Create(payload, options: JsonOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var response = await httpClient.SendAsync(request);
|
||||||
|
var content = response.Content is null ? string.Empty : await response.Content.ReadAsStringAsync();
|
||||||
|
var contentType = response.Content?.Headers.ContentType?.ToString() ?? "application/json";
|
||||||
|
|
||||||
|
return new ProxyResponse((int)response.StatusCode, content, contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,13 +3,14 @@
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning",
|
"Microsoft.AspNetCore": "Warning",
|
||||||
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None"
|
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None",
|
||||||
|
"System.Net.Http.HttpClient.VeriFactu": "None"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Dolibarr": {
|
"Dolibarr": {
|
||||||
"ApiUrl": "http://localhost/api/index.php",
|
"ApiUrl": "http://localhost/api/index.php",
|
||||||
"ApiKey": ""
|
"ApiKey": "847ad838254caa863c2da3dc949a5028e2317be9"
|
||||||
},
|
},
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"Secret": "",
|
"Secret": "",
|
||||||
|
|
@ -18,5 +19,8 @@
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
"WebhookUrl": ""
|
"WebhookUrl": ""
|
||||||
|
},
|
||||||
|
"VeriFactu": {
|
||||||
|
"ApiUrl": "http://localhost:6789/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue