using Hangfire; using Meezi.API.Jobs; using Meezi.Core.Delivery; using Meezi.Core.Entities; using Meezi.Core.Enums; using Meezi.Infrastructure.Data; namespace Meezi.API.Services.Delivery; public record WebhookIngressResult(bool Accepted, string? WebhookLogId, string? ErrorCode, string? Message); public interface IDeliveryWebhookIngressService { Task ReceiveAsync( DeliveryPlatform platform, string rawBody, string? signatureHeader, CancellationToken ct = default); } public class DeliveryWebhookIngressService : IDeliveryWebhookIngressService { private readonly AppDbContext _db; private readonly IWebhookSignatureService _signatures; private readonly IOrderNormalizer _normalizer; public DeliveryWebhookIngressService( AppDbContext db, IWebhookSignatureService signatures, IOrderNormalizer normalizer) { _db = db; _signatures = signatures; _normalizer = normalizer; } public async Task ReceiveAsync( DeliveryPlatform platform, string rawBody, string? signatureHeader, CancellationToken ct = default) { var signatureValid = _signatures.Verify(platform, rawBody, signatureHeader); var log = new WebhookLog { Id = $"wh_{Guid.NewGuid():N}"[..24], Platform = platform, RawBody = rawBody, SignatureHeader = signatureHeader, SignatureValid = signatureValid, CreatedAt = DateTime.UtcNow }; _db.WebhookLogs.Add(log); await _db.SaveChangesAsync(ct); if (!signatureValid) return new WebhookIngressResult(false, log.Id, "UNAUTHORIZED", "Invalid signature."); var unified = _normalizer.FromJson(platform, rawBody); if (unified is null) { log.Success = false; log.Processed = true; log.ErrorMessage = "Could not normalize payload."; log.ProcessedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); return new WebhookIngressResult(false, log.Id, "VALIDATION_ERROR", log.ErrorMessage); } BackgroundJob.Enqueue(job => job.ExecuteAsync(log.Id, unified, CancellationToken.None)); return new WebhookIngressResult(true, log.Id, null, null); } }