Files
raceplanner/backend/Controllers/PaymentsController.cs
T
2026-04-03 21:06:38 +02:00

258 lines
7.9 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RacePlannerApi.Data;
using RacePlannerApi.DTOs;
using RacePlannerApi.Models;
namespace RacePlannerApi.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class PaymentsController : ControllerBase
{
private readonly RacePlannerDbContext _context;
public PaymentsController(RacePlannerDbContext context)
{
_context = context;
}
[HttpPost]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentDto>> RecordPayment(CreatePaymentRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Load registration with event
var registration = await _context.Registrations
.Include(r => r.Event)
.FirstOrDefaultAsync(r => r.Id == request.RegistrationId);
if (registration == null)
{
return NotFound(new { error = "Registration not found" });
}
// Verify user is organizer of the event
if (registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
// Check registration is not cancelled
if (registration.Status == RegistrationStatus.Cancelled)
{
return BadRequest(new { error = "Cannot record payment for cancelled registration" });
}
var payment = new Payment
{
RegistrationId = request.RegistrationId,
Amount = request.Amount,
Method = request.Method,
TransactionId = request.TransactionId,
Notes = request.Notes,
PaymentDate = request.PaymentDate ?? DateTime.UtcNow
};
_context.Payments.Add(payment);
await _context.SaveChangesAsync();
// Update registration status if fully paid (assuming a fixed fee amount would be known)
// For now, we'll just record the payment
return CreatedAtAction(
nameof(GetPayment),
new { id = payment.Id },
MapToPaymentDto(payment));
}
[HttpGet("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentDto>> GetPayment(Guid id)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var payment = await _context.Payments
.Include(p => p.Registration)
.ThenInclude(r => r.Event)
.FirstOrDefaultAsync(p => p.Id == id);
if (payment == null)
{
return NotFound();
}
// Verify user is organizer
if (payment.Registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
return Ok(MapToPaymentDto(payment));
}
[HttpGet("registration/{registrationId}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<IEnumerable<PaymentDto>>> GetRegistrationPayments(Guid registrationId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Verify organizer has access to this registration
var registration = await _context.Registrations
.Include(r => r.Event)
.FirstOrDefaultAsync(r => r.Id == registrationId);
if (registration == null)
{
return NotFound();
}
if (registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
var payments = await _context.Payments
.Where(p => p.RegistrationId == registrationId)
.OrderByDescending(p => p.PaymentDate)
.ToListAsync();
return Ok(payments.Select(MapToPaymentDto));
}
[HttpGet("registration/{registrationId}/status")]
[Authorize]
public async Task<ActionResult<object>> GetPaymentStatus(Guid registrationId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var registration = await _context.Registrations
.Include(r => r.Event)
.Include(r => r.Payments)
.FirstOrDefaultAsync(r => r.Id == registrationId);
if (registration == null)
{
return NotFound();
}
// Participant can view their own, organizer can view any
var isOrganizer = registration.Event.OrganizerId == userId.Value;
if (registration.ParticipantId != userId.Value && !isOrganizer)
{
return Forbid();
}
var totalPaid = registration.Payments.Sum(p => p.Amount);
var status = totalPaid > 0 ? "paid" : "unpaid";
return Ok(new
{
RegistrationId = registrationId,
TotalPaid = totalPaid,
PaymentCount = registration.Payments.Count,
Status = status,
Payments = registration.Payments.Select(MapToPaymentDto)
});
}
[HttpGet("event/{eventId}/report")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentReportDto>> GetPaymentReport(Guid eventId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var eventEntity = await _context.Events
.FirstOrDefaultAsync(e => e.Id == eventId && e.OrganizerId == userId.Value);
if (eventEntity == null)
{
return NotFound();
}
var registrations = await _context.Registrations
.Include(r => r.Participant)
.Include(r => r.Payments)
.Where(r => r.EventId == eventId && r.Status != RegistrationStatus.Cancelled)
.ToListAsync();
var totalCollected = registrations.Sum(r => r.Payments.Sum(p => p.Amount));
var totalRegistrations = registrations.Count;
var paidRegistrations = registrations.Count(r => r.Payments.Any());
var unpaidRegistrations = registrations.Count(r => !r.Payments.Any());
var report = new PaymentReportDto
{
EventId = eventId,
EventName = eventEntity.Name,
TotalCollected = totalCollected,
TotalPending = 0, // Would require event fee structure
TotalOutstanding = 0, // Would require event fee structure
TotalRegistrations = totalRegistrations,
PaidRegistrations = paidRegistrations,
PartialRegistrations = 0, // Would require partial payment logic
UnpaidRegistrations = unpaidRegistrations,
Registrations = registrations.Select(r => new RegistrationPaymentDto
{
RegistrationId = r.Id,
ParticipantName = r.Participant.Name,
Status = r.Status.ToString(),
TotalPaid = r.Payments.Sum(p => p.Amount),
AmountDue = 0, // Would require event fee structure
PaymentStatus = r.Payments.Any() ? "paid" : "unpaid",
Payments = r.Payments.Select(MapToPaymentDto).ToList()
}).ToList()
};
return Ok(report);
}
private Guid? GetCurrentUserId()
{
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
if (Guid.TryParse(userIdClaim, out var userId))
{
return userId;
}
return null;
}
private static PaymentDto MapToPaymentDto(Payment payment)
{
return new PaymentDto
{
Id = payment.Id,
RegistrationId = payment.RegistrationId,
Amount = payment.Amount,
Method = payment.Method.ToString(),
TransactionId = payment.TransactionId,
Notes = payment.Notes,
PaymentDate = payment.PaymentDate,
CreatedAt = payment.CreatedAt
};
}
}