Skip to Content
v1.0Documentación oficial de la API LX CloudPos · estable en producción
V1Ejemplos Completos Por LenguajeC# .NET

C# .NET

Cliente HTTP completo para integrarte desde una aplicación .NET (consola, ASP.NET, worker). Incluye emisión, consulta, descarga de PDF/XML, reenvío de correo, anulación, próximo consecutivo y un receptor de webhooks con verificación HMAC.

Setup

dotnet new console -n LxsIntegration cd LxsIntegration dotnet add package System.Text.Json

Cliente completo

El LxsClient envuelve HttpClient con timeout de 30 s y los headers de autenticación (X-Api-Key). Cada llamada a EmitirAsync genera un Idempotency-Key único si no se proporciona uno.

// LxsClient.cs using System.Net.Http; using System.Net.Http.Json; using System.Net.Http.Headers; public class LxsClient { private readonly HttpClient _http; public LxsClient(string apiKey, string baseUrl = "https://api.lxsyscr.com") { _http = new HttpClient { BaseAddress = new Uri(baseUrl), Timeout = TimeSpan.FromSeconds(30) }; _http.DefaultRequestHeaders.Add("X-Api-Key", apiKey); _http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } public async Task<FacturaResponse> EmitirAsync(FacturaRequest data, string? idempotencyKey = null) { var req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/facturas") { Content = JsonContent.Create(data) }; req.Headers.Add("Idempotency-Key", idempotencyKey ?? Guid.NewGuid().ToString()); var res = await _http.SendAsync(req); res.EnsureSuccessStatusCode(); return (await res.Content.ReadFromJsonAsync<FacturaResponse>())!; } public async Task<FacturaResponse> ConsultarAsync(string clave) => (await _http.GetFromJsonAsync<FacturaResponse>($"/api/v1/facturas/{clave}"))!; public async Task<byte[]> DescargarPdfAsync(string clave) => await _http.GetByteArrayAsync($"/api/v1/facturas/{clave}/pdf"); public async Task<string> DescargarXmlAsync(string clave) => await _http.GetStringAsync($"/api/v1/facturas/{clave}/xml"); public async Task ReenviarEmailAsync(string clave) { var res = await _http.PostAsync($"/api/v1/facturas/{clave}/email", null); res.EnsureSuccessStatusCode(); } public async Task AnularAsync(string clave) { var res = await _http.DeleteAsync($"/api/v1/facturas/{clave}"); res.EnsureSuccessStatusCode(); } public async Task<long> ProximoConsecutivoAsync(string tipo) { var res = await _http.GetFromJsonAsync<ConsecutivoResponse>($"/api/v1/consecutivos/proximo?tipo={tipo}"); return res!.Proximo; } } public record FacturaRequest( string Tipo, ClienteDto? Cliente, string CondicionVenta, string MedioPago, string Moneda, decimal? TipoCambio, List<LineaDto> Lineas, List<ReferenciaDto> Referencias); public record ClienteDto(int IdCliente); public record LineaDto(int IdProducto, decimal Cantidad, decimal? PrecioUnitario = null, decimal MontoDescuento = 0); public record ReferenciaDto(string TipoDocumento, string Numero, DateTime? FechaEmision, string? Codigo, string? Razon); public record FacturaResponse( string Clave, string NumeroConsecutivo, string TipoDocumento, string EstadoHacienda, decimal TotalComprobante, string Moneda, DateTime FechaEmision); public record ConsecutivoResponse(string Tipo, long Proximo);

Receptor de webhook (ASP.NET)

El controller lee el body crudo, calcula el HMAC-SHA256 con el LXS_WEBHOOK_SECRET y lo compara contra el header X-Lxs-Signature con FixedTimeEquals para evitar ataques de timing. Si la firma no coincide responde 401; si coincide, deserializa el evento y lo procesa.

[ApiController] [Route("webhooks/lxs")] public class LxsWebhookController : ControllerBase { [HttpPost] public async Task<IActionResult> Handle() { using var reader = new StreamReader(Request.Body); string body = await reader.ReadToEndAsync(); string presented = Request.Headers["X-Lxs-Signature"] .ToString() .Replace("sha256=", "", StringComparison.OrdinalIgnoreCase); var secret = Environment.GetEnvironmentVariable("LXS_WEBHOOK_SECRET")!; var expectedBytes = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(body)); string expected = Convert.ToHexString(expectedBytes).ToLowerInvariant(); if (presented.Length != expected.Length || !CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(presented), Encoding.UTF8.GetBytes(expected))) { return Unauthorized(); } var evento = JsonSerializer.Deserialize<LxsEvent>(body); // Procesa evento return Ok(); } } public record LxsEvent(string Evento, string Clave, string IndEstado, string Mensaje, int Intento, string Nonce);