Skip to Content
v1.5Official documentation for the LX CloudPos API · stable in production

C# .NET

Complete HTTP client for integrating from a .NET application (console, ASP.NET, worker). Includes issuing, querying, downloading PDF/XML, resending email, voiding, next consecutive number, and a webhook receiver with HMAC verification.

Setup

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

Complete client

The LxsClient wraps HttpClient with a 30 s timeout and the authentication headers (X-Api-Key). Each call to EmitirAsync generates a unique Idempotency-Key if one is not provided.

// 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.pos.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, int IdUsuario, 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);

Webhook receiver (ASP.NET)

The controller reads the raw body, computes the HMAC-SHA256 with LXS_WEBHOOK_SECRET, and compares it against the X-Lxs-Signature header using FixedTimeEquals to prevent timing attacks. If the signature does not match, it responds 401; if it matches, it deserializes the event and processes it.

[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); // Process event return Ok(); } } public record LxsEvent(string Evento, string Clave, string IndEstado, string Mensaje, int Intento, string Nonce);