using MediatR; using cuqmbr.TravelGuide.Application.Common.Persistence; using cuqmbr.TravelGuide.Application.Common.Services; using cuqmbr.TravelGuide.Application.Common.Exceptions; using System.Text; using Newtonsoft.Json; using cuqmbr.TravelGuide.Domain.Enums; using Microsoft.Extensions.Localization; using cuqmbr.TravelGuide.Domain.Entities; namespace cuqmbr.TravelGuide.Application.Payments.LiqPay .TicketGroups.Commands.ProcessCallback; public class ProcessCallbackCommandHandler : IRequestHandler { private readonly UnitOfWork _unitOfWork; private readonly LiqPayPaymentService _liqPayPaymentService; private readonly IStringLocalizer _localizer; private readonly EmailSenderService _emailSender; public ProcessCallbackCommandHandler( UnitOfWork unitOfWork, LiqPayPaymentService liqPayPaymentService, IStringLocalizer localizer, EmailSenderService emailSender) { _unitOfWork = unitOfWork; _liqPayPaymentService = liqPayPaymentService; _localizer = localizer; _emailSender = emailSender; } public async Task Handle( ProcessCallbackCommand request, CancellationToken cancellationToken) { // Validate signature. var isSignatureValid = _liqPayPaymentService .IsValidSignature(request.Data, request.Signature); if (!isSignatureValid) { throw new ForbiddenException(); } // Parse request data. var dataBytes = Convert.FromBase64String(request.Data); var dataJson = Encoding.UTF8.GetString(dataBytes); var data = JsonConvert.DeserializeObject(dataJson); string status = data.status; var ticketGroupGuid = Guid.Parse((string)data.order_id); var ticketGroup = await _unitOfWork.TicketGroupRepository .GetOneAsync(e => e.Guid == ticketGroupGuid, e => e.Tickets, cancellationToken); if (ticketGroup == null || ticketGroup.Status == TicketStatus.Purchased) { throw new ForbiddenException(); } // Process callback status if (status.Equals("error") || status.Equals("failure")) { await _unitOfWork.TicketGroupRepository .DeleteOneAsync(ticketGroup, cancellationToken); } else if (status.Equals("success")) { // Update ticket status ticketGroup.Status = TicketStatus.Purchased; await _unitOfWork.TicketGroupRepository .UpdateOneAsync(ticketGroup, cancellationToken); // Hydrate ticket group var vehicleEnrollmentIds = ticketGroup.Tickets.Select(t => t.VehicleEnrollmentId); var vehicleEnrollments = (await _unitOfWork.VehicleEnrollmentRepository .GetPageAsync( ve => vehicleEnrollmentIds.Contains(ve.Id), ve => ve.Route.RouteAddresses, 1, vehicleEnrollmentIds.Count(), cancellationToken)) .Items; var routeAddressIds = vehicleEnrollments .SelectMany(ve => ve.Route.RouteAddresses) .Select(ra => ra.Id); var routeAddressDetails = (await _unitOfWork.RouteAddressDetailRepository .GetPageAsync( rad => routeAddressIds.Contains(rad.RouteAddressId), 1, routeAddressIds.Count(), cancellationToken)) .Items; var addressIds = vehicleEnrollments .SelectMany(ve => ve.Route.RouteAddresses) .Select(ra => ra.AddressId); var addresses = (await _unitOfWork.AddressRepository .GetPageAsync( a => addressIds.Contains(a.Id), a => a.City.Region.Country, 1, addressIds.Count(), cancellationToken)) .Items; var vehicleIds = vehicleEnrollments .Select(ve => ve.VehicleId); var vehicles = (await _unitOfWork.VehicleRepository .GetPageAsync( v => vehicleIds.Contains(v.Id), v => v.Company, 1, vehicleIds.Count(), cancellationToken)) .Items; foreach (var ve in vehicleEnrollments) { ve.Vehicle = vehicles.Single(v => v.Id == ve.VehicleId); foreach (var ra in ve.Route.RouteAddresses) { ra.Address = addresses.Single(a => a.Id == ra.AddressId); ra.Details = routeAddressDetails .Where(rad => rad.RouteAddressId == ra.Id) .ToArray(); } } foreach (var t in ticketGroup.Tickets) { t.VehicleEnrollment = vehicleEnrollments .Single(ve => ve.Id == t.VehicleEnrollmentId); } // Send email if (ticketGroup.PassangerEmail != null) { var subject = _localizer["PaymentProcessing.Ticket" + ".Email.PaymentCompleted.Subject"]; var ticketDetails = GetTicketDetails(ticketGroup); var body = String.Format( _localizer["PaymentProcessing.Ticket" + ".Email.PaymentCompleted.Body"], ticketDetails); await _emailSender.SendAsync( new[] { ticketGroup.PassangerEmail }, subject, body, cancellationToken); } } await _unitOfWork.SaveAsync(cancellationToken); _unitOfWork.Dispose(); } private string GetTicketDetails(TicketGroup ticketGroup) { var sb = new StringBuilder(); sb.AppendLine("General:"); sb.AppendLine(); sb.AppendLine($"Ticket uuid: {ticketGroup.Guid}"); sb.AppendLine($"Purchase Time: {ticketGroup.PurchaseTime}"); sb.AppendLine(); var departureRouteAddressId = ticketGroup.Tickets.First().DepartureRouteAddressId; var arrivalRouteAddressId = ticketGroup.Tickets.Last().ArrivalRouteAddressId; var departureTime = ticketGroup.Tickets.First() .VehicleEnrollment.GetDepartureTime(departureRouteAddressId); var arrivalTime = ticketGroup.Tickets.Last() .VehicleEnrollment.GetArrivalTime(arrivalRouteAddressId); var departureAddress = ticketGroup.Tickets.First() .VehicleEnrollment.Route.RouteAddresses .Single(ra => ra.Id == departureRouteAddressId) .Address; var arrivalAddress = ticketGroup.Tickets.Last() .VehicleEnrollment.Route.RouteAddresses .Single(ra => ra.Id == arrivalRouteAddressId) .Address; var departureAddressName = $"{departureAddress.City.Region.Country.Name}, " + $"{departureAddress.City.Region.Name}, " + $"{departureAddress.City.Name}, " + $"{departureAddress.Name}"; var arrivalAddressName = $"{arrivalAddress.City.Region.Country.Name}, " + $"{arrivalAddress.City.Region.Name}, " + $"{arrivalAddress.City.Name}, " + $"{arrivalAddress.Name}"; sb.AppendLine($"Departure: {departureAddressName} at {departureTime}."); sb.AppendLine($"Arrival: {arrivalAddressName} at {arrivalTime}."); sb.AppendLine(); sb.AppendLine(); sb.AppendLine($"Passanger details:"); sb.AppendLine(); sb.AppendLine($"First Name: {ticketGroup.PassangerFirstName}."); sb.AppendLine($"Last Name: {ticketGroup.PassangerLastName}."); sb.AppendLine($"Patronymic: {ticketGroup.PassangerPatronymic}."); sb.AppendLine($"Sex: {ticketGroup.PassangerSex}."); sb.AppendLine($"Birth Date: {ticketGroup.PassangerBirthDate}."); sb.AppendLine($"Email: {ticketGroup.PassangerEmail}."); sb.AppendLine(); sb.AppendLine(); sb.AppendLine("Vehicle enrollments' details:"); sb.AppendLine(); foreach (var t in ticketGroup.Tickets) { departureRouteAddressId = t.DepartureRouteAddressId; arrivalRouteAddressId = t.ArrivalRouteAddressId; departureTime = t.VehicleEnrollment.GetDepartureTime(departureRouteAddressId); arrivalTime = t.VehicleEnrollment.GetArrivalTime(arrivalRouteAddressId); departureAddress = t.VehicleEnrollment.Route.RouteAddresses .Single(ra => ra.Id == departureRouteAddressId) .Address; arrivalAddress = t.VehicleEnrollment.Route.RouteAddresses .Single(ra => ra.Id == arrivalRouteAddressId) .Address; departureAddressName = $"{departureAddress.City.Region.Country.Name}, " + $"{departureAddress.City.Region.Name}, " + $"{departureAddress.City.Name}, " + $"{departureAddress.Name}"; arrivalAddressName = $"{arrivalAddress.City.Region.Country.Name}, " + $"{arrivalAddress.City.Region.Name}, " + $"{arrivalAddress.City.Name}, " + $"{arrivalAddress.Name}"; var vehicle = t.VehicleEnrollment.Vehicle; var company = vehicle.Company; sb.AppendLine($"Departure: {departureAddressName} at {departureTime}."); sb.AppendLine($"Arrival: {arrivalAddressName} at {arrivalTime}."); if (vehicle is Bus) { sb.AppendLine($"Vehicle: Bus, {((Bus)vehicle).Model}, " + $"{((Bus)vehicle).Number}."); } else if (vehicle is Aircraft) { sb.AppendLine($"Vehicle: Aircraft, {((Aircraft)vehicle).Model}, " + $"{((Aircraft)vehicle).Number}."); } else if (vehicle is Train) { sb.AppendLine($"Vehicle: Train, {((Train)vehicle).Model}, " + $"{((Train)vehicle).Number}."); } else { throw new NotImplementedException(); } sb.AppendLine($"Company: {company.Name}, ({company.ContactEmail}, " + $"{company.ContactPhoneNumber})."); var cost = t.Currency.Round( t.VehicleEnrollment .GetCost(departureRouteAddressId,arrivalRouteAddressId)); sb.AppendLine($"Cost: {cost} {t.Currency.Name}"); sb.AppendLine(); } return sb.ToString(); } }