diff --git a/Server/Controllers/PaymentsController.cs b/Server/Controllers/PaymentsController.cs index eab8f0a..1b8c91d 100644 --- a/Server/Controllers/PaymentsController.cs +++ b/Server/Controllers/PaymentsController.cs @@ -1,27 +1,37 @@ using Microsoft.AspNetCore.Mvc; +using Server.Models; using Server.Services; -using SharedModels.DataTransferObjects; +using SharedModels.Responses; + +namespace Server.Controllers; [Route("api/payment")] [ApiController] public class PaymentController : ControllerBase { - private readonly IPaymentsService _paymentsService; + readonly IPaymentsService _paymentsService; + public PaymentController(IPaymentsService paymentsService) { _paymentsService = paymentsService; } - [HttpGet] - public async Task GetPaymentLink([FromQuery] PaymentDto payment) + [HttpPost("link")] + public async Task GetPaymentLink([FromBody] IList input) { - var result = await _paymentsService.GetPaymentUrl(payment); - - if (!result.isSucceed) - { - return result.actionResult; - } + var result = await _paymentsService.GetPaymentUrl(input); return Ok(result.url); } + + [HttpPost("callback")] + public async Task ReceiveCallback() + { + if (HttpContext.Request.ContentType != "application/x-www-form-urlencoded") + return BadRequest(); + + var result = await _paymentsService.ReceiveCallback(HttpContext.Request.Form); + + return result.actionResult; + } } diff --git a/Server/Models/TicketGroup.cs b/Server/Models/TicketGroup.cs index 233b291..17ed80b 100644 --- a/Server/Models/TicketGroup.cs +++ b/Server/Models/TicketGroup.cs @@ -7,7 +7,7 @@ public class TicketGroup [Key] public int Id { get; set; } - public DateTime PurchaseDateTimeUtc { get; set; } = DateTime.UtcNow; + public DateTime PurchaseDateTimeUtc { get; set; } = DateTime.MinValue; public bool IsReturned { get; set; } = false; public string UserId { get; set; } = null!; diff --git a/Server/Services/IPaymentsService.cs b/Server/Services/IPaymentsService.cs index e003ffa..97477bf 100644 --- a/Server/Services/IPaymentsService.cs +++ b/Server/Services/IPaymentsService.cs @@ -1,12 +1,12 @@ using LiqPayIntegration; using Microsoft.AspNetCore.Mvc; -using SharedModels.DataTransferObjects; -using System.Dynamic; +using SharedModels.Responses; namespace Server.Services; public interface IPaymentsService { Task<(bool isSucceed, IActionResult? actionResult, IEnumerable payments)> GetPayments(DateTime from, DateTime to); - Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(PaymentDto payment); + Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(IList input); + Task<(bool isSucceed, IActionResult? actionResult)> ReceiveCallback(IFormCollection forms); } \ No newline at end of file diff --git a/Server/Services/PaymentsService.cs b/Server/Services/PaymentsService.cs index 087981a..be79517 100644 --- a/Server/Services/PaymentsService.cs +++ b/Server/Services/PaymentsService.cs @@ -1,23 +1,113 @@ +using System.Text; +using System.Text.Json; using LiqPayIntegration; using Microsoft.AspNetCore.Mvc; -using SharedModels.DataTransferObjects; +using Microsoft.EntityFrameworkCore; +using Server.Constants; +using Server.Data; +using Server.Models; +using SharedModels.Responses; namespace Server.Services; public class PaymentsService : IPaymentsService { + readonly ApplicationDbContext _dbContext; - // LiqpayIntegration - LiqPay liqPay = new LiqPay("sandbox_i23432845039", "sandbox_gymL9PdryqdfAznNQbb7ynLvASDQ5SJCCNJvF2iV"); + readonly string _userId; + + // LiqpayIntegration. Change 3-rd argument` address to current machine address in its network. Don't forget about port. + LiqPay liqPay = new LiqPay("sandbox_i23432845039", "sandbox_gymL9PdryqdfAznNQbb7ynLvASDQ5SJCCNJvF2iV", "http://localhost:5006/api/payment/callback/"); + + public PaymentsService(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor) + { + _dbContext = dbContext; + _userId = httpContextAccessor.HttpContext.User.Claims + .FirstOrDefault(c => c.Properties.Values.Any(v => v == JwtStandardClaimNames.Sub))?.Value; + } public async Task<(bool isSucceed, IActionResult? actionResult, IEnumerable payments)> GetPayments(DateTime from, DateTime to) { return(true, null, (await liqPay.PaymentArchive(from, to)).AsEnumerable()); } - public async Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(PaymentDto payment) + public async Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(IList input) { - return (true, null, (await liqPay.GetPaymentUrl(payment.Amount, payment.Description, payment.OrderId))); + if (input.Count < 1) + return (false, new BadRequestResult(), ""); + + input.ToList().OrderBy(e => e.Order); + + List tickets = new List(); + foreach (StrippedFlattenedEnrollment e in input) + { + tickets.Add(new Ticket + { + VehicleEnrollmentId = e.Id, + FirstRouteAddressId = e.DepartureAddressId, + LastRouteAddressId = e.ArrivalAddressId + }); + } + + TicketGroup ticketGroup = new TicketGroup + { + Tickets = tickets, + UserId = _userId + }; + + _dbContext.TicketGroups.Add(ticketGroup); + await _dbContext.SaveChangesAsync(); + + int orderId = ticketGroup.Id; + + var ticketGroups = _dbContext.TicketGroups; + var dbTicketGroup = await _dbContext.TicketGroups + .Include(tg => tg.Tickets) + .ThenInclude(t => t.VehicleEnrollment) + .ThenInclude(ve => ve.RouteAddressDetails) + .ThenInclude(ve => ve.RouteAddress) + .ThenInclude(ra => ra.Route) + .ThenInclude(r => r.RouteAddresses) + .ThenInclude(ra => ra.Address) + .ThenInclude(a => a.City) + .ThenInclude(c => c.State) + .ThenInclude(s => s.Country) + .FirstAsync(tg => tg.Id == orderId); + + double cost = dbTicketGroup.GetCost(); + string description = + $"Ïî¿çäêà ç { dbTicketGroup.GetDepartureAddress().GetFullName()}\n"+ + $"Äî { dbTicketGroup.GetArrivalAddress().GetFullName()}"; + string text = await liqPay.GetPaymentUrl(cost, description, orderId.ToString()); + + return (true, new OkResult(), text); } - + + public async Task<(bool isSucceed, IActionResult? actionResult)> ReceiveCallback(IFormCollection forms) + { + if (!forms.Keys.Contains("data")) + return (false, new BadRequestResult()); + + var baseData = forms["data"].First(); + + var data = Encoding.UTF8.GetString(Convert.FromBase64String(baseData)); + var jsonData = JsonSerializer.Deserialize>(data); + + if (!jsonData.ContainsKey("status") || !jsonData.ContainsKey("order_id")) + return (false, new BadRequestResult()); + + if (jsonData["status"].GetString() != "success") + return (true, new OkResult()); + + var orderId = int.Parse(jsonData["order_id"].GetString()); + var ticket = _dbContext.TicketGroups + .Find(orderId); + ticket.PurchaseDateTimeUtc = DateTime.UtcNow; + + _dbContext.Update(ticket); + await _dbContext.SaveChangesAsync(); + + return (true, new OkResult()); + } + } \ No newline at end of file diff --git a/SharedModels/Responses/StrippedFlattenedEnrollment.cs b/SharedModels/Responses/StrippedFlattenedEnrollment.cs new file mode 100644 index 0000000..1d1b284 --- /dev/null +++ b/SharedModels/Responses/StrippedFlattenedEnrollment.cs @@ -0,0 +1,10 @@ +namespace SharedModels.Responses +{ + public class StrippedFlattenedEnrollment + { + public int Id { get; set; } + public int DepartureAddressId { get; set; } + public int ArrivalAddressId { get; set; } + public int Order { get; set; } + } +}