diff --git a/Webshop.Api/Controllers/Admin/AdminPaymentMethodsController.cs b/Webshop.Api/Controllers/Admin/AdminPaymentMethodsController.cs new file mode 100644 index 0000000..b83a672 --- /dev/null +++ b/Webshop.Api/Controllers/Admin/AdminPaymentMethodsController.cs @@ -0,0 +1,66 @@ +// src/Webshop.Api/Controllers/Admin/AdminPaymentMethodsController.cs +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; // AdminPaymentMethodDto +using Webshop.Application.Services.Admin; // IAdminPaymentMethodService + +namespace Webshop.Api.Controllers.Admin +{ + [ApiController] + [Route("api/v1/admin/paymentmethods")] // Saubere Route + [Authorize(Roles = "Admin")] // Nur Admins + public class AdminPaymentMethodsController : ControllerBase + { + private readonly IAdminPaymentMethodService _adminPaymentMethodService; + + public AdminPaymentMethodsController(IAdminPaymentMethodService adminPaymentMethodService) + { + _adminPaymentMethodService = adminPaymentMethodService; + } + + [HttpGet] + public async Task>> GetAllPaymentMethods() + { + var paymentMethods = await _adminPaymentMethodService.GetAllAsync(); + return Ok(paymentMethods); + } + + [HttpGet("{id}")] + public async Task> GetPaymentMethodById(Guid id) + { + var paymentMethod = await _adminPaymentMethodService.GetByIdAsync(id); + if (paymentMethod == null) return NotFound(); + return Ok(paymentMethod); + } + + [HttpPost] + public async Task> CreatePaymentMethod([FromBody] AdminPaymentMethodDto paymentMethodDto) + { + if (!ModelState.IsValid) return BadRequest(ModelState); + var createdPaymentMethod = await _adminPaymentMethodService.CreateAsync(paymentMethodDto); + return CreatedAtAction(nameof(GetPaymentMethodById), new { id = createdPaymentMethod.Id }, createdPaymentMethod); + } + + [HttpPut("{id}")] + public async Task UpdatePaymentMethod(Guid id, [FromBody] AdminPaymentMethodDto paymentMethodDto) + { + if (id != paymentMethodDto.Id) return BadRequest(); + if (!ModelState.IsValid) return BadRequest(ModelState); + + var success = await _adminPaymentMethodService.UpdateAsync(paymentMethodDto); + if (!success) return NotFound(); + return NoContent(); // 204 No Content für erfolgreiches Update + } + + [HttpDelete("{id}")] + public async Task DeletePaymentMethod(Guid id) + { + var success = await _adminPaymentMethodService.DeleteAsync(id); + if (!success) return NotFound(); + return NoContent(); + } + } +} \ No newline at end of file diff --git a/Webshop.Api/Controllers/Public/PaymentMethodsController.cs b/Webshop.Api/Controllers/Public/PaymentMethodsController.cs new file mode 100644 index 0000000..10e6ff2 --- /dev/null +++ b/Webshop.Api/Controllers/Public/PaymentMethodsController.cs @@ -0,0 +1,30 @@ +// src/Webshop.Api/Controllers/Public/PaymentMethodsController.cs +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; // PaymentMethodDto +using Webshop.Application.Services.Public; // IPaymentMethodService + +namespace Webshop.Api.Controllers.Public +{ + [ApiController] + [Route("api/v1/public/paymentmethods")] // Saubere Route + [AllowAnonymous] // Jeder darf die verfügbaren Zahlungsmethoden sehen + public class PaymentMethodsController : ControllerBase + { + private readonly IPaymentMethodService _paymentMethodService; + + public PaymentMethodsController(IPaymentMethodService paymentMethodService) + { + _paymentMethodService = paymentMethodService; + } + + [HttpGet] + public async Task>> GetActivePaymentMethods() + { + var paymentMethods = await _paymentMethodService.GetAllActiveAsync(); + return Ok(paymentMethods); + } + } +} \ No newline at end of file diff --git a/Webshop.Api/Program.cs b/Webshop.Api/Program.cs index a83344c..a5c7295 100644 --- a/Webshop.Api/Program.cs +++ b/Webshop.Api/Program.cs @@ -85,11 +85,13 @@ builder.Services.AddScoped(); // PUBLIC Services builder.Services.AddScoped(); +builder.Services.AddScoped(); // ADMIN Services builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // CUSTOMER Services (später Implementierungen hinzufügen) builder.Services.AddScoped(); diff --git a/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs b/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs index 3ed6dfa..d30f776 100644 --- a/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs +++ b/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs @@ -16,6 +16,7 @@ using Webshop.Application.DTOs.Orders; using Webshop.Application.DTOs.Discounts; using Webshop.Application.DTOs.Categorys; using Webshop.Api.Controllers.Auth; +using Webshop.Domain.Enums; namespace Webshop.Api.SwaggerFilters { diff --git a/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs b/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs new file mode 100644 index 0000000..ea0d814 --- /dev/null +++ b/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs @@ -0,0 +1,17 @@ +// src/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs +using System; +using Webshop.Domain.Enums; + +namespace Webshop.Application.DTOs.Payments +{ + public class AdminPaymentMethodDto + { + public Guid Id { get; set; } = Guid.NewGuid(); + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public bool IsActive { get; set; } + public PaymentGatewayType PaymentGatewayType { get; set; } + public string? Configuration { get; set; } // Als JSON-String, den der Admin bearbeitet + public decimal? ProcessingFee { get; set; } + } +} \ No newline at end of file diff --git a/Webshop.Application/DTOs/Payments/PaymentMethodDto.cs b/Webshop.Application/DTOs/Payments/PaymentMethodDto.cs index d029639..31605c2 100644 --- a/Webshop.Application/DTOs/Payments/PaymentMethodDto.cs +++ b/Webshop.Application/DTOs/Payments/PaymentMethodDto.cs @@ -1,7 +1,8 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Application/DTOs/Payments/PaymentMethodDto.cs using System; using System.Collections.Generic; using System.Threading.Tasks; +using Webshop.Domain.Enums; @@ -12,8 +13,10 @@ namespace Webshop.Application.DTOs.Payments public Guid Id { get; set; } public string Name { get; set; } = string.Empty; public string? Description { get; set; } - public bool IsActive { get; set; } - public PaymentGatewayType GatewayType { get; set; } + public PaymentGatewayType PaymentGatewayType { get; set; } public decimal? ProcessingFee { get; set; } + + // Wichtig: Wir geben hier nur öffentliche Konfigurationsdaten preis (z.B. IBAN) + public object? PublicConfiguration { get; set; } } -} +} \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs b/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs new file mode 100644 index 0000000..fc227f5 --- /dev/null +++ b/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs @@ -0,0 +1,100 @@ +// src/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs +using System; +using System.Collections.Generic; +using System.Linq; // Für Select +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; // AdminPaymentMethodDto +using Webshop.Domain.Entities; // PaymentMethod +using Webshop.Domain.Interfaces; // IPaymentMethodRepository +using Webshop.Domain.Enums; + +namespace Webshop.Application.Services.Admin +{ + public class AdminPaymentMethodService : IAdminPaymentMethodService + { + private readonly IPaymentMethodRepository _paymentMethodRepository; + + public AdminPaymentMethodService(IPaymentMethodRepository paymentMethodRepository) + { + _paymentMethodRepository = paymentMethodRepository; + } + + public async Task> GetAllAsync() + { + var paymentMethods = await _paymentMethodRepository.GetAllAsync(); + + return paymentMethods.Select(pm => new AdminPaymentMethodDto + { + Id = pm.Id, + Name = pm.Name, + Description = pm.Description, + IsActive = pm.IsActive, + PaymentGatewayType = pm.PaymentGatewayType, + Configuration = pm.Configuration, // Gibt den JSON-String für den Admin zurück + ProcessingFee = pm.ProcessingFee + }).ToList(); + } + + public async Task GetByIdAsync(Guid id) + { + var paymentMethod = await _paymentMethodRepository.GetByIdAsync(id); + if (paymentMethod == null) return null; + + return new AdminPaymentMethodDto + { + Id = paymentMethod.Id, + Name = paymentMethod.Name, + Description = paymentMethod.Description, + IsActive = paymentMethod.IsActive, + PaymentGatewayType = paymentMethod.PaymentGatewayType, + Configuration = paymentMethod.Configuration, + ProcessingFee = paymentMethod.ProcessingFee + }; + } + + public async Task CreateAsync(AdminPaymentMethodDto paymentMethodDto) + { + var newPaymentMethod = new PaymentMethod + { + Id = Guid.NewGuid(), // API generiert die ID + Name = paymentMethodDto.Name, + Description = paymentMethodDto.Description, + IsActive = paymentMethodDto.IsActive, + PaymentGatewayType = paymentMethodDto.PaymentGatewayType, + Configuration = paymentMethodDto.Configuration, + ProcessingFee = paymentMethodDto.ProcessingFee + }; + + await _paymentMethodRepository.AddAsync(newPaymentMethod); + + paymentMethodDto.Id = newPaymentMethod.Id; // ID zurückschreiben + return paymentMethodDto; + } + + public async Task UpdateAsync(AdminPaymentMethodDto paymentMethodDto) + { + var existingPaymentMethod = await _paymentMethodRepository.GetByIdAsync(paymentMethodDto.Id); + if (existingPaymentMethod == null) return false; + + // Eigenschaften aktualisieren + existingPaymentMethod.Name = paymentMethodDto.Name; + existingPaymentMethod.Description = paymentMethodDto.Description; + existingPaymentMethod.IsActive = paymentMethodDto.IsActive; + existingPaymentMethod.PaymentGatewayType = paymentMethodDto.PaymentGatewayType; + existingPaymentMethod.Configuration = paymentMethodDto.Configuration; + existingPaymentMethod.ProcessingFee = paymentMethodDto.ProcessingFee; + + await _paymentMethodRepository.UpdateAsync(existingPaymentMethod); + return true; + } + + public async Task DeleteAsync(Guid id) + { + var paymentMethod = await _paymentMethodRepository.GetByIdAsync(id); + if (paymentMethod == null) return false; + + await _paymentMethodRepository.DeleteAsync(id); + return true; + } + } +} \ No newline at end of file diff --git a/Webshop.Application/Services/Admin/Interfaces/IAdminPaymentMethodService.cs b/Webshop.Application/Services/Admin/Interfaces/IAdminPaymentMethodService.cs new file mode 100644 index 0000000..7b225af --- /dev/null +++ b/Webshop.Application/Services/Admin/Interfaces/IAdminPaymentMethodService.cs @@ -0,0 +1,36 @@ +// src/Webshop.Application/Services/Admin/IAdminPaymentMethodService.cs +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; // Für AdminPaymentMethodDto + +namespace Webshop.Application.Services.Admin +{ + public interface IAdminPaymentMethodService + { + /// + /// Ruft alle Zahlungsmethoden ab (sowohl aktive als auch inaktive). + /// + Task> GetAllAsync(); + + /// + /// Ruft eine einzelne Zahlungsmethode anhand ihrer ID ab. + /// + Task GetByIdAsync(Guid id); + + /// + /// Erstellt eine neue Zahlungsmethode. + /// + Task CreateAsync(AdminPaymentMethodDto paymentMethodDto); + + /// + /// Aktualisiert eine bestehende Zahlungsmethode. + /// + Task UpdateAsync(AdminPaymentMethodDto paymentMethodDto); + + /// + /// Löscht eine Zahlungsmethode anhand ihrer ID. + /// + Task DeleteAsync(Guid id); + } +} \ No newline at end of file diff --git a/Webshop.Application/Services/Public/Interfaces/IPaymentMethodService.cs b/Webshop.Application/Services/Public/Interfaces/IPaymentMethodService.cs new file mode 100644 index 0000000..6eb7654 --- /dev/null +++ b/Webshop.Application/Services/Public/Interfaces/IPaymentMethodService.cs @@ -0,0 +1,16 @@ +// src/Webshop.Application/Services/Public/IPaymentMethodService.cs +using System.Collections.Generic; +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; // Für PaymentMethodDto + +namespace Webshop.Application.Services.Public +{ + public interface IPaymentMethodService + { + /// + /// Ruft alle aktiven Zahlungsmethoden ab, die im Checkout für den Kunden sichtbar sein sollen. + /// + /// Eine Liste von PaymentMethodDto mit öffentlichen Konfigurationsdaten. + Task> GetAllActiveAsync(); + } +} \ No newline at end of file diff --git a/Webshop.Application/Services/Public/PaymentMethodService.cs b/Webshop.Application/Services/Public/PaymentMethodService.cs new file mode 100644 index 0000000..36ad87a --- /dev/null +++ b/Webshop.Application/Services/Public/PaymentMethodService.cs @@ -0,0 +1,67 @@ +// src/Webshop.Application/Services/Public/PaymentMethodService.cs +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; // Für JSON-Verarbeitung +using System.Threading.Tasks; +using Webshop.Application.DTOs.Payments; +using Webshop.Domain.Enums; +using Webshop.Domain.Interfaces; + + +namespace Webshop.Application.Services.Public +{ + public class PaymentMethodService : IPaymentMethodService + { + private readonly IPaymentMethodRepository _paymentMethodRepository; + + public PaymentMethodService(IPaymentMethodRepository paymentMethodRepository) + { + _paymentMethodRepository = paymentMethodRepository; + } + + public async Task> GetAllActiveAsync() + { + var paymentMethods = await _paymentMethodRepository.GetAllAsync(); + + return paymentMethods + .Where(pm => pm.IsActive) + .Select(pm => new PaymentMethodDto + { + Id = pm.Id, + Name = pm.Name, + Description = pm.Description, + PaymentGatewayType = pm.PaymentGatewayType, + ProcessingFee = pm.ProcessingFee, + PublicConfiguration = GetPublicConfiguration(pm.PaymentGatewayType, pm.Configuration) + }).ToList(); + } + + private object? GetPublicConfiguration(PaymentGatewayType type, string? configJson) + { + if (string.IsNullOrEmpty(configJson)) return null; + + // Beispiel: Nur für BankTransfer geben wir IBAN etc. preis + if (type == PaymentGatewayType.BankTransfer) + { + var config = JsonSerializer.Deserialize>(configJson); + if (config != null) + { + // Filter, um geheime Schlüssel NICHT an das Frontend zu senden + return new { IBAN = config.GetValueOrDefault("IBAN"), BIC = config.GetValueOrDefault("BIC"), BankName = config.GetValueOrDefault("BankName") }; + } + } + + // Für Stripe/PayPal geben wir nur den Public Key preis + if (type == PaymentGatewayType.Stripe) + { + var config = JsonSerializer.Deserialize>(configJson); + if (config != null) + { + return new { PublicKey = config.GetValueOrDefault("PublicKey") }; + } + } + + return null; // Für andere Typen geben wir keine Konfigurationsdetails preis + } + } +} \ No newline at end of file diff --git a/Webshop.Domain/Entities/PaymentMethod.cs b/Webshop.Domain/Entities/PaymentMethod.cs index 2296324..c93cd5e 100644 --- a/Webshop.Domain/Entities/PaymentMethod.cs +++ b/Webshop.Domain/Entities/PaymentMethod.cs @@ -1,5 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Webshop.Domain.Enums; namespace Webshop.Domain.Entities; @@ -8,22 +10,21 @@ namespace Webshop.Domain.Entities; /// public class PaymentMethod { - [Key] - public Guid Id { get; set; } + public Guid Id { get; set; } = Guid.NewGuid(); [Required] [MaxLength(100)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [MaxLength(500)] public string? Description { get; set; } - [Required] - public bool IsActive { get; set; } + public bool IsActive { get; set; } = true; - [Required] - [MaxLength(50)] - public string PaymentGatewayType { get; set; } // "Stripe", "PayPal", etc. + public PaymentGatewayType PaymentGatewayType { get; set; } + + [Column(TypeName = "jsonb")] + public string? Configuration { get; set; } public decimal? ProcessingFee { get; set; } } \ No newline at end of file diff --git a/Webshop.Domain/Enums/PaymentGatewayType.cs b/Webshop.Domain/Enums/PaymentGatewayType.cs index 9270078..1426e28 100644 --- a/Webshop.Domain/Enums/PaymentGatewayType.cs +++ b/Webshop.Domain/Enums/PaymentGatewayType.cs @@ -1,7 +1,12 @@ -public enum PaymentGatewayType +// src/Webshop.Domain/Enums/PaymentGatewayType.cs +namespace Webshop.Domain.Enums { - Stripe, - PayPal, - Klarna, - Manual // z.B. Vorkasse + public enum PaymentGatewayType + { + BankTransfer, + PayPal, + Stripe, + CashOnDelivery, + Invoice + } } \ No newline at end of file diff --git a/Webshop.Domain/Interfaces/IPaymentMethodRepository.cs b/Webshop.Domain/Interfaces/IPaymentMethodRepository.cs index 1ccf502..c703625 100644 --- a/Webshop.Domain/Interfaces/IPaymentMethodRepository.cs +++ b/Webshop.Domain/Interfaces/IPaymentMethodRepository.cs @@ -1,14 +1,16 @@ -// Auto-generiert von CreateWebshopFiles.ps1 -using System; +// src/Webshop.Domain/Interfaces/IPaymentMethodRepository.cs using System.Collections.Generic; using System.Threading.Tasks; using Webshop.Domain.Entities; - namespace Webshop.Domain.Interfaces { public interface IPaymentMethodRepository { -// Fügen Sie hier Methodensignaturen hinzu + Task> GetAllAsync(); + Task GetByIdAsync(Guid id); + Task AddAsync(PaymentMethod paymentMethod); + Task UpdateAsync(PaymentMethod paymentMethod); + Task DeleteAsync(Guid id); } -} +} \ No newline at end of file diff --git a/Webshop.Infrastructure/Repositories/PaymentMethodRepository.cs b/Webshop.Infrastructure/Repositories/PaymentMethodRepository.cs index 29a21f2..1839130 100644 --- a/Webshop.Infrastructure/Repositories/PaymentMethodRepository.cs +++ b/Webshop.Infrastructure/Repositories/PaymentMethodRepository.cs @@ -1,4 +1,4 @@ -// Auto-generiert von CreateWebshopFiles.ps1 +// src/Webshop.Infrastructure/Repositories/PaymentMethodRepository.cs using Microsoft.EntityFrameworkCore; using Webshop.Domain.Entities; using Webshop.Domain.Interfaces; @@ -18,12 +18,10 @@ namespace Webshop.Infrastructure.Repositories _context = context; } - // Fügen Sie hier Repository-Methoden hinzu - // Beispiel: - // public async Task> GetAllAsync() { return await _context.Set().ToListAsync(); } - // public async Task GetByIdAsync(Guid id) { return await _context.Set().FindAsync(id); } - // public async Task AddAsync(T entity) { _context.Set().Add(entity); await _context.SaveChangesAsync(); } - // public async Task UpdateAsync(T entity) { _context.Set().Update(entity); await _context.SaveChangesAsync(); } - // public async Task DeleteAsync(Guid id) { var entity = await _context.Set().FindAsync(id); if (entity != null) { _context.Set().Remove(entity); await _context.SaveChangesAsync(); } } + public async Task> GetAllAsync() => await _context.PaymentMethods.ToListAsync(); + public async Task GetByIdAsync(Guid id) => await _context.PaymentMethods.FindAsync(id); + public async Task AddAsync(PaymentMethod paymentMethod) { _context.PaymentMethods.Add(paymentMethod); await _context.SaveChangesAsync(); } + public async Task UpdateAsync(PaymentMethod paymentMethod) { _context.PaymentMethods.Update(paymentMethod); await _context.SaveChangesAsync(); } + public async Task DeleteAsync(Guid id) { var entity = await _context.PaymentMethods.FindAsync(id); if (entity != null) { _context.PaymentMethods.Remove(entity); await _context.SaveChangesAsync(); } } } -} +} \ No newline at end of file