using System.Text.Json; using Microsoft.EntityFrameworkCore; using Tiku.Application.Commerce; using Tiku.Application.Jobs; using Tiku.Domain.Commerce; namespace Tiku.Infrastructure.Commerce; internal sealed partial class RefundAdministrationService { public async Task ProcessRefundNotificationAsync( Guid tenantId, RefundNotificationCommand command, CancellationToken cancellationToken = default) { var provider = NormalizeProvider(command.Provider); var refund = await dbContext.CommerceRefundRequests.SingleOrDefaultAsync( item => item.TenantId == tenantId && item.RefundNo == command.RefundNo, cancellationToken) ?? throw new CommerceException("Refund request was not found.", "refund_not_found"); var eventId = string.IsNullOrWhiteSpace(command.EventId) ? $"{provider}:{command.RefundNo}:{command.Status}" : command.EventId.Trim(); var duplicate = await dbContext.PaymentEvents.AnyAsync( item => item.TenantId == tenantId && item.Provider == provider && item.EventType == "refund" && item.EventId == eventId, cancellationToken); if (duplicate) return refund; dbContext.PaymentEvents.Add(new PaymentEvent { TenantId = tenantId, Provider = provider, EventType = "refund", EventId = eventId, SignatureValid = true, Payload = JsonObjectOrDefault(command.Payload), ProcessedAt = DateTimeOffset.UtcNow }); var fromStatus = refund.Status; if (fromStatus != command.Status && IsAllowedRefundTransition(fromStatus, command.Status)) { refund.Status = command.Status; refund.ProviderRefundNo = string.IsNullOrWhiteSpace(command.ProviderRefundNo) ? refund.ProviderRefundNo : command.ProviderRefundNo.Trim(); if (command.Status == CommerceRefundStatus.Succeeded) { refund.SucceededAt = DateTimeOffset.UtcNow; await ApplyRefundToOrderAsync(refund, cancellationToken); } else if (command.Status == CommerceRefundStatus.Failed) { refund.FailedAt = DateTimeOffset.UtcNow; } dbContext.CommerceRefundEvents.Add(new CommerceRefundEvent { TenantId = tenantId, RefundRequestId = refund.Id, FromStatus = fromStatus, ToStatus = command.Status, EventType = "provider_notify", Details = JsonObjectOrDefault(command.Payload) }); } await dbContext.SaveChangesAsync(cancellationToken); return refund; } }