using System.Text; using System.Text.Json; namespace DoliMiddlewareApi.Services.Notifications; /// /// Envía notificaciones a un webhook de Teams o Slack cuando cambia el estado de una factura. /// Compatible con Teams (Adaptive Cards) y Slack (Block Kit). /// Configurar la URL en appsettings: Notifications:WebhookUrl /// public class WebhookNotificationService( IHttpClientFactory httpClientFactory, WebhookSettings webhookSettings, ILogger logger) : INotificationService { private static readonly Dictionary StatusMeta = new() { ["draft"] = ("Borrador", "#8a7dff"), ["unpaid"] = ("Pendiente de pago", "#ffb454"), ["paid"] = ("Pagada ✓", "#5dd39e"), }; public async Task NotifyInvoiceStatusChangedAsync(int invoiceId, string invoiceRef, string newStatus) { var webhookUrl = webhookSettings.WebhookUrl; if (string.IsNullOrWhiteSpace(webhookUrl)) return; // Notificaciones desactivadas — no hay webhook configurado var (label, color) = StatusMeta.GetValueOrDefault(newStatus, (newStatus, "#888")); var payload = BuildPayload(invoiceId, invoiceRef, label, color); _ = SendAsync(webhookUrl, payload, invoiceId); } private async Task SendAsync(string url, object payload, int invoiceId) { try { var client = httpClientFactory.CreateClient("Webhook"); var content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, content); if (!response.IsSuccessStatusCode) logger.LogWarning("Webhook respondió {Status} para factura {Id}", response.StatusCode, invoiceId); } catch (Exception ex) { logger.LogError(ex, "Error enviando notificación para factura {Id}", invoiceId); } } /// /// Construye un payload compatible con Teams (Adaptive Card via Incoming Webhook) /// y con Slack (message payload básico con attachments). /// Teams espera { "type": "message", "attachments": [...] } con Adaptive Card. /// Slack espera { "text": "...", "attachments": [...] }. /// Detectamos por URL cuál usar. /// private object BuildPayload(int invoiceId, string invoiceRef, string label, string color) { var webhookUrl = webhookSettings.WebhookUrl ?? ""; if (webhookUrl.Contains("slack.com")) { return new { text = $"🧾 Factura *{invoiceRef}* ha cambiado de estado", attachments = new[] { new { color, fields = new[] { new { title = "Factura", value = invoiceRef, @short = true }, new { title = "Estado", value = label, @short = true }, } } } }; } // Teams — Adaptive Card return new { type = "message", attachments = new[] { new { contentType = "application/vnd.microsoft.card.adaptive", content = new { type = "AdaptiveCard", version = "1.2", body = new object[] { new { type = "TextBlock", text = "🧾 Estado de factura actualizado", weight = "Bolder", size = "Medium" }, new { type = "FactSet", facts = new[] { new { title = "Factura", value = invoiceRef }, new { title = "ID", value = invoiceId.ToString() }, new { title = "Estado", value = label }, } } }, msteams = new { width = "Full" } } } } }; } }