// C# con HttpClient y System.Text.Json. Sin operaciones automaticas. using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public sealed class ImaCashClient : IDisposable { private readonly HttpClient http; public ImaCashClient(string privateToken) { http = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false, UseCookies = false }); http.BaseAddress = new Uri("https://restapi.imaginaits.com/"); http.Timeout = TimeSpan.FromSeconds(10); http.DefaultRequestHeaders.Add("x-auth-token", privateToken); } private async Task SendAsync(string path, object body = null, string stableKey = null) { using var request = new HttpRequestMessage(body == null ? HttpMethod.Get : HttpMethod.Post, path); if (body != null) request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); if (stableKey != null) request.Headers.Add("Idempotency-Key", stableKey); using var response = await http.SendAsync(request); var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); if (!response.IsSuccessStatusCode || !json.RootElement.TryGetProperty("status", out var status) || status.GetString() != "ok") { json.Dispose(); throw new InvalidOperationException("API request rejected: HTTP " + (int)response.StatusCode); } return json; // El llamante debe disponer el JsonDocument y comprobar data.confirmed. } public Task CapabilitiesAsync() => SendAsync("v2.php?action=capabilities"); public Task CancelAsync(string transactionId, string stableKey) => SendAsync("canceltransactionv2.php", new { transaction_id = transactionId }, stableKey); public Task OperationAsync(string operationId) => SendAsync("v2.php?action=operation&operation_id=" + Uri.EscapeDataString(operationId)); public void Dispose() => http.Dispose(); } // Nunca repetir dinero automaticamente tras timeout. Mantener referencia y clave.