diff --git a/TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs b/TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs index b6f1058..de24569 100644 --- a/TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs @@ -638,4 +638,121 @@ public async Task ScanTicket_WhenScanningSuccesful_ShouldReturnSuccess() // Assert Assert.True(res.IsSuccess); } + + [Fact] + public async Task SetTicketForResellAsync_ReturnsFailure_WhenPriceIsZero() + { + var ticketRepositoryMock = new Mock(); + var paginationServiceMock = new Mock(); + var qrServiceMock = new Mock(); + var ticketTypeRepositoryMock = new Mock(); + var shoppingCartRepositoryMock = new Mock(); + + var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object, + shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object); + + var result = await sut.SetTicketForResellAsync(Guid.NewGuid(), "test@example.com", 0, "zl"); + + Assert.True(result.IsError); + Assert.Equal(500, result.StatusCode); + } + + [Fact] + public async Task SetTicketForResellAsync_ReturnsFailure_WhenPriceTooHigh() + { + var ticket = CreateTicketForResellTest(price: 100); + var ticketRepositoryMock = new Mock(); + var paginationServiceMock = new Mock(); + var qrServiceMock = new Mock(); + ticketRepositoryMock + .Setup(r => r.GetTicketWithDetailsByIdAndEmailAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(ticket)); + + var ticketTypeRepositoryMock = new Mock(); + var shoppingCartRepositoryMock = new Mock(); + var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object, + shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object); + var result = await sut.SetTicketForResellAsync(Guid.NewGuid(), "test@example.com", 160, "zl"); + + Assert.True(result.IsError); + Assert.Equal(500, result.StatusCode); + } + + [Fact] + public async Task SetTicketForResellAsync_ReturnsFailure_WhenAlreadyForResell() + { + var ticket = CreateTicketForResellTest(price: 100, forResell: true); + var ticketRepositoryMock = new Mock(); + var paginationServiceMock = new Mock(); + var qrServiceMock = new Mock(); + ticketRepositoryMock + .Setup(r => r.GetTicketWithDetailsByIdAndEmailAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(ticket)); + var ticketTypeRepositoryMock = new Mock(); + var shoppingCartRepositoryMock = new Mock(); + var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object, + shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object); + var result = await sut.SetTicketForResellAsync(Guid.NewGuid(), "test@example.com", 120, "zl"); + + Assert.True(result.IsError); + Assert.Equal(500, result.StatusCode); + } + + [Fact] + public async Task SetTicketForResellAsync_ReturnsFailure_WhenTicketIsUsed() + { + var ticket = CreateTicketForResellTest(price: 100, used: true); + var ticketRepositoryMock = new Mock(); + var paginationServiceMock = new Mock(); + var qrServiceMock = new Mock(); + + ticketRepositoryMock + .Setup(r => r.GetTicketWithDetailsByIdAndEmailAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(ticket)); + var ticketTypeRepositoryMock = new Mock(); + var shoppingCartRepositoryMock = new Mock(); + var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object, + shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object); + + var result = await sut.SetTicketForResellAsync(Guid.NewGuid(), "test@example.com", 120, "zl"); + + Assert.True(result.IsError); + Assert.Equal(500, result.StatusCode); + } + + [Fact] + public async Task SetTicketForResellAsync_ReturnsSuccess_WhenValid() + { + var ticketRepositoryMock = new Mock(); + var paginationServiceMock = new Mock(); + var qrServiceMock = new Mock(); + var ticket = CreateTicketForResellTest(price: 100); + ticketRepositoryMock + .Setup(r => r.GetTicketWithDetailsByIdAndEmailAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(ticket)); + + ticketRepositoryMock + .Setup(r => r.SetTicketForResell(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success()); + + var ticketTypeRepositoryMock = new Mock(); + var shoppingCartRepositoryMock = new Mock(); + var sut = new TicketService(ticketRepositoryMock.Object, ticketTypeRepositoryMock.Object, + shoppingCartRepositoryMock.Object, paginationServiceMock.Object, qrServiceMock.Object); + + var result = await sut.SetTicketForResellAsync(Guid.NewGuid(), "test@example.com", 130, "zl"); + + Assert.False(result.IsError); + } + + private Ticket CreateTicketForResellTest(decimal price, bool forResell = false, bool used = false) + { + return new Ticket + { + Id = Guid.NewGuid(), + Type = new TicketType { Price = price }, + ForResell = forResell, + Used = used + }; + } } \ No newline at end of file diff --git a/TickAPI/TickAPI/Migrations/20250525174930_resell.Designer.cs b/TickAPI/TickAPI/Migrations/20250525174930_resell.Designer.cs new file mode 100644 index 0000000..7a09fdb --- /dev/null +++ b/TickAPI/TickAPI/Migrations/20250525174930_resell.Designer.cs @@ -0,0 +1,397 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TickAPI.Common.TickApiDbContext; + +#nullable disable + +namespace TickAPI.Migrations +{ + [DbContext(typeof(TickApiDbContext))] + [Migration("20250525174930_resell")] + partial class resell + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("CategoryEvent", b => + { + b.Property("CategoriesId") + .HasColumnType("uniqueidentifier"); + + b.Property("EventsId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CategoriesId", "EventsId"); + + b.HasIndex("EventsId"); + + b.ToTable("CategoryEvent"); + }); + + modelBuilder.Entity("TickAPI.Addresses.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FlatNumber") + .HasColumnType("bigint"); + + b.Property("HouseNumber") + .HasColumnType("bigint"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Street") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("TickAPI.Admins.Models.Admin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Login") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Admins"); + }); + + modelBuilder.Entity("TickAPI.Categories.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b.HasData( + new + { + Id = new Guid("ec3daf69-baa9-4fcd-a674-c09884a57272"), + Name = "Music" + }, + new + { + Id = new Guid("de89dd76-3b29-43e1-8f4b-5278b1b8bde2"), + Name = "Sports" + }, + new + { + Id = new Guid("ea58370b-2a17-4770-abea-66399ad69fb8"), + Name = "Conferences" + }, + new + { + Id = new Guid("4a086d9e-59de-4fd1-a1b2-bd9b5eec797c"), + Name = "Theatre" + }, + new + { + Id = new Guid("5f8dbe65-30be-453f-8f22-191a11b2977b"), + Name = "Comedy" + }, + new + { + Id = new Guid("4421327a-4bc8-4706-bec0-666f78ed0c69"), + Name = "Workshops" + }); + }); + + modelBuilder.Entity("TickAPI.Customers.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("TickAPI.Events.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AddressId") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("EventStatus") + .HasColumnType("int"); + + b.Property("MinimumAge") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizerId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("OrganizerId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("TickAPI.Organizers.Models.Organizer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsVerified") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Organizers"); + }); + + modelBuilder.Entity("TickAPI.TicketTypes.Models.TicketType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AvailableFrom") + .HasColumnType("datetime2"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventId") + .HasColumnType("uniqueidentifier"); + + b.Property("MaxCount") + .HasColumnType("bigint"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("TicketTypes"); + }); + + modelBuilder.Entity("TickAPI.Tickets.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ForResell") + .HasColumnType("bit"); + + b.Property("NameOnTicket") + .HasColumnType("nvarchar(max)"); + + b.Property("OwnerId") + .HasColumnType("uniqueidentifier"); + + b.Property("ResellCurrency") + .HasColumnType("nvarchar(max)"); + + b.Property("ResellPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("Seats") + .HasColumnType("nvarchar(max)"); + + b.Property("TypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Used") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("TypeId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("CategoryEvent", b => + { + b.HasOne("TickAPI.Categories.Models.Category", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("TickAPI.Events.Models.Event", null) + .WithMany() + .HasForeignKey("EventsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TickAPI.Events.Models.Event", b => + { + b.HasOne("TickAPI.Addresses.Models.Address", "Address") + .WithMany() + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("TickAPI.Organizers.Models.Organizer", "Organizer") + .WithMany("Events") + .HasForeignKey("OrganizerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Organizer"); + }); + + modelBuilder.Entity("TickAPI.TicketTypes.Models.TicketType", b => + { + b.HasOne("TickAPI.Events.Models.Event", "Event") + .WithMany("TicketTypes") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("TickAPI.Tickets.Models.Ticket", b => + { + b.HasOne("TickAPI.Customers.Models.Customer", "Owner") + .WithMany("Tickets") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("TickAPI.TicketTypes.Models.TicketType", "Type") + .WithMany("Tickets") + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("TickAPI.Customers.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("TickAPI.Events.Models.Event", b => + { + b.Navigation("TicketTypes"); + }); + + modelBuilder.Entity("TickAPI.Organizers.Models.Organizer", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("TickAPI.TicketTypes.Models.TicketType", b => + { + b.Navigation("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TickAPI/TickAPI/Migrations/20250525174930_resell.cs b/TickAPI/TickAPI/Migrations/20250525174930_resell.cs new file mode 100644 index 0000000..2c65ace --- /dev/null +++ b/TickAPI/TickAPI/Migrations/20250525174930_resell.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TickAPI.Migrations +{ + /// + public partial class resell : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "NameOnTicket", + table: "Tickets", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AddColumn( + name: "ResellCurrency", + table: "Tickets", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ResellCurrency", + table: "Tickets"); + + migrationBuilder.AlterColumn( + name: "NameOnTicket", + table: "Tickets", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + } + } +} diff --git a/TickAPI/TickAPI/Migrations/TickApiDbContextModelSnapshot.cs b/TickAPI/TickAPI/Migrations/TickApiDbContextModelSnapshot.cs index dc78ef4..71d888e 100644 --- a/TickAPI/TickAPI/Migrations/TickApiDbContextModelSnapshot.cs +++ b/TickAPI/TickAPI/Migrations/TickApiDbContextModelSnapshot.cs @@ -281,6 +281,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OwnerId") .HasColumnType("uniqueidentifier"); + b.Property("ResellCurrency") + .HasColumnType("nvarchar(max)"); + + b.Property("ResellPrice") + .HasColumnType("decimal(18,2)"); + b.Property("Seats") .HasColumnType("nvarchar(max)"); diff --git a/TickAPI/TickAPI/Tickets/Abstractions/ITicketRepository.cs b/TickAPI/TickAPI/Tickets/Abstractions/ITicketRepository.cs index ff20841..4add328 100644 --- a/TickAPI/TickAPI/Tickets/Abstractions/ITicketRepository.cs +++ b/TickAPI/TickAPI/Tickets/Abstractions/ITicketRepository.cs @@ -12,5 +12,6 @@ public interface ITicketRepository public IQueryable GetTicketsByEventId(Guid eventId); public IQueryable GetTicketsByCustomerEmail(string email); public Task MarkTicketAsUsed(Guid id); + public Task SetTicketForResell(Guid ticketId, decimal newPrice, string currency); public Task AddTicketAsync(Ticket ticket); } \ No newline at end of file diff --git a/TickAPI/TickAPI/Tickets/Abstractions/ITicketService.cs b/TickAPI/TickAPI/Tickets/Abstractions/ITicketService.cs index d1fb208..1fce588 100644 --- a/TickAPI/TickAPI/Tickets/Abstractions/ITicketService.cs +++ b/TickAPI/TickAPI/Tickets/Abstractions/ITicketService.cs @@ -20,6 +20,8 @@ public Task>> GetTicketsForCustome public Task ScanTicket(Guid ticketGuid); public Task> GetTicketDetailsAsync(Guid ticketGuid, string email, string scanUrl); + + public Task SetTicketForResellAsync(Guid ticketId, string email, decimal resellPrice, string resellCurrency); public Task> GetTicketTypeByIdAsync(Guid ticketTypeId); public Task CreateTicketAsync(TicketType type, Customer owner, string? nameOnTicket = null, string? seats = null); diff --git a/TickAPI/TickAPI/Tickets/Controllers/TicketsController.cs b/TickAPI/TickAPI/Tickets/Controllers/TicketsController.cs index 77b45e2..54209b7 100644 --- a/TickAPI/TickAPI/Tickets/Controllers/TicketsController.cs +++ b/TickAPI/TickAPI/Tickets/Controllers/TicketsController.cs @@ -63,5 +63,18 @@ public async Task> ScanTicket(Guid id) var res = await _ticketService.ScanTicket(id); return res.ToObjectResult(); } + + [AuthorizeWithPolicy(AuthPolicies.CustomerPolicy)] + [HttpPost("resell/{id:guid}")] + public async Task> SetTicketForResell([FromRoute] Guid id, [FromBody] SetTicketForResellDataDto data) + { + var emailResult = _claimsService.GetEmailFromClaims(User.Claims); + if (emailResult.IsError) + { + return emailResult.ToObjectResult(); + } + var res = await _ticketService.SetTicketForResellAsync(id, emailResult.Value!, data.ResellPrice, data.ResellCurrency); + return res.ToObjectResult(); + } } \ No newline at end of file diff --git a/TickAPI/TickAPI/Tickets/DTOs/Request/SetTicketForResellDataDto.cs b/TickAPI/TickAPI/Tickets/DTOs/Request/SetTicketForResellDataDto.cs new file mode 100644 index 0000000..7ee6d82 --- /dev/null +++ b/TickAPI/TickAPI/Tickets/DTOs/Request/SetTicketForResellDataDto.cs @@ -0,0 +1,7 @@ +namespace TickAPI.Tickets.DTOs.Request; + +public record SetTicketForResellDataDto +( + decimal ResellPrice, + string ResellCurrency +); \ No newline at end of file diff --git a/TickAPI/TickAPI/Tickets/Models/Ticket.cs b/TickAPI/TickAPI/Tickets/Models/Ticket.cs index 631f8db..3622cc6 100644 --- a/TickAPI/TickAPI/Tickets/Models/Ticket.cs +++ b/TickAPI/TickAPI/Tickets/Models/Ticket.cs @@ -11,5 +11,7 @@ public class Ticket public string? NameOnTicket { get; set; } public string? Seats { get; set; } public bool ForResell { get; set; } + public decimal? ResellPrice { get; set; } + public string? ResellCurrency { get; set; } public bool Used { get; set; } } \ No newline at end of file diff --git a/TickAPI/TickAPI/Tickets/Repositories/TicketRepository.cs b/TickAPI/TickAPI/Tickets/Repositories/TicketRepository.cs index 57774e6..c3d3911 100644 --- a/TickAPI/TickAPI/Tickets/Repositories/TicketRepository.cs +++ b/TickAPI/TickAPI/Tickets/Repositories/TicketRepository.cs @@ -87,4 +87,18 @@ public async Task AddTicketAsync(Ticket ticket) return Result.Success(); } + + public async Task SetTicketForResell(Guid ticketId, decimal newPrice, string currency) + { + var ticket = await _tickApiDbContext.Tickets.FirstOrDefaultAsync(t => t.Id == ticketId); + if (ticket == null) + { + return Result.Failure(StatusCodes.Status404NotFound, "Ticket with this id doesn't exist"); + } + ticket.ForResell = true; + ticket.ResellCurrency = currency; + ticket.ResellPrice = newPrice; + await _tickApiDbContext.SaveChangesAsync(); + return Result.Success(); + } } \ No newline at end of file diff --git a/TickAPI/TickAPI/Tickets/Services/TicketService.cs b/TickAPI/TickAPI/Tickets/Services/TicketService.cs index 74b6dad..c643424 100644 --- a/TickAPI/TickAPI/Tickets/Services/TicketService.cs +++ b/TickAPI/TickAPI/Tickets/Services/TicketService.cs @@ -184,4 +184,36 @@ public async Task ScanTicket(Guid ticketGuid) var res = await _ticketRepository.MarkTicketAsUsed(ticketGuid); return res; } + + public async Task SetTicketForResellAsync(Guid ticketId, string email, decimal resellPrice, string resellCurrency) + { + if (resellPrice <= 0) + { + return Result.Failure(StatusCodes.Status500InternalServerError, "Price must be greater than zero"); + } + var ticketRes = await _ticketRepository.GetTicketWithDetailsByIdAndEmailAsync(ticketId, email); + if (ticketRes.IsError) + { + return Result.PropagateError(ticketRes); + } + + if (ticketRes.Value!.Type.Price*1.5m < resellPrice) + { + return Result.Failure(StatusCodes.Status500InternalServerError, "Resell price cannot exceed " + + "value of original price times 1.5"); + } + + if (ticketRes.Value!.ForResell) + { + return Result.Failure(StatusCodes.Status500InternalServerError, "Ticket is already set for resell"); + } + + if (ticketRes.Value!.Used) + { + return Result.Failure(StatusCodes.Status500InternalServerError, "Ticket is already used"); + } + + var res = await _ticketRepository.SetTicketForResell(ticketId, resellPrice, resellCurrency); + return res; + } } \ No newline at end of file