payment methods
This commit is contained in:
@@ -0,0 +1,124 @@
|
|||||||
|
// src/Webshop.Api/JsonConverters/AdminPaymentMethodDtoConverter.cs
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Webshop.Application.DTOs.Payments;
|
||||||
|
using Webshop.Domain.Enums;
|
||||||
|
|
||||||
|
namespace Webshop.Api.JsonConverters
|
||||||
|
{
|
||||||
|
public class AdminPaymentMethodDtoConverter : JsonConverter<AdminPaymentMethodDto>
|
||||||
|
{
|
||||||
|
public override AdminPaymentMethodDto Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType != JsonTokenType.StartObject)
|
||||||
|
{
|
||||||
|
throw new JsonException("Expected StartObject token.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ein temporäres JsonDocument erstellen, um das Objekt zu analysieren
|
||||||
|
using var jsonDocument = JsonDocument.ParseValue(ref reader);
|
||||||
|
var jsonObject = jsonDocument.RootElement;
|
||||||
|
|
||||||
|
var dto = new AdminPaymentMethodDto();
|
||||||
|
|
||||||
|
// Lese alle Standard-Eigenschaften
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.Id), out var idElement))
|
||||||
|
{
|
||||||
|
dto.Id = idElement.GetGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.Name), out var nameElement))
|
||||||
|
{
|
||||||
|
dto.Name = nameElement.GetString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.Description), out var descriptionElement))
|
||||||
|
{
|
||||||
|
dto.Description = descriptionElement.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.IsActive), out var isActiveElement))
|
||||||
|
{
|
||||||
|
dto.IsActive = isActiveElement.GetBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.ProcessingFee), out var processingFeeElement))
|
||||||
|
{
|
||||||
|
dto.ProcessingFee = processingFeeElement.GetDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lese den PaymentGatewayType, um zu entscheiden, wie die Konfiguration deserialisiert werden soll
|
||||||
|
if (!jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.PaymentGatewayType), out var typeElement) ||
|
||||||
|
!Enum.TryParse<PaymentGatewayType>(typeElement.GetString(), true, out var gatewayType))
|
||||||
|
{
|
||||||
|
throw new JsonException("PaymentGatewayType is missing or invalid.");
|
||||||
|
}
|
||||||
|
dto.PaymentGatewayType = gatewayType;
|
||||||
|
|
||||||
|
// Deserialisiere die Konfiguration basierend auf dem Typ
|
||||||
|
if (jsonObject.TryGetProperty(nameof(AdminPaymentMethodDto.Configuration), out var configElement) && configElement.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
|
var configOptions = new JsonSerializerOptions(options); // Klonen der Optionen
|
||||||
|
configOptions.Converters.Remove(this); // Entferne diesen Konverter, um eine Endlosschleife zu vermeiden
|
||||||
|
|
||||||
|
switch (gatewayType)
|
||||||
|
{
|
||||||
|
case PaymentGatewayType.BankTransfer:
|
||||||
|
dto.Configuration = configElement.Deserialize<BankTransferConfigurationDto>(configOptions);
|
||||||
|
break;
|
||||||
|
case PaymentGatewayType.Stripe:
|
||||||
|
dto.Configuration = configElement.Deserialize<StripeConfigurationDto>(configOptions);
|
||||||
|
break;
|
||||||
|
case PaymentGatewayType.PayPal:
|
||||||
|
dto.Configuration = configElement.Deserialize<PayPalConfigurationDto>(configOptions);
|
||||||
|
break;
|
||||||
|
// Fügen Sie hier weitere Fälle hinzu
|
||||||
|
default:
|
||||||
|
// Behandeln Sie den Fall, dass keine spezifische Konfiguration benötigt wird
|
||||||
|
dto.Configuration = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, AdminPaymentMethodDto value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var writeOptions = new JsonSerializerOptions(options);
|
||||||
|
writeOptions.Converters.Remove(this); // Entferne diesen Konverter, um eine Endlosschleife zu vermeiden
|
||||||
|
|
||||||
|
writer.WriteStartObject();
|
||||||
|
|
||||||
|
writer.WriteString("id", value.Id);
|
||||||
|
writer.WriteString("name", value.Name);
|
||||||
|
writer.WriteString("description", value.Description);
|
||||||
|
writer.WriteBoolean("isActive", value.IsActive);
|
||||||
|
// Schreibe den Enum als String
|
||||||
|
writer.WriteString("paymentGatewayType", value.PaymentGatewayType.ToString());
|
||||||
|
|
||||||
|
if (value.Configuration != null)
|
||||||
|
{
|
||||||
|
writer.WritePropertyName("configuration");
|
||||||
|
// Serialisiere das Konfigurationsobjekt mit seinem konkreten Typ
|
||||||
|
JsonSerializer.Serialize(writer, value.Configuration, value.Configuration.GetType(), writeOptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNull("configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.ProcessingFee.HasValue)
|
||||||
|
{
|
||||||
|
writer.WriteNumber("processingFee", value.ProcessingFee.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNull("processingFee");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs
|
// src/Webshop.Application/DTOs/Payments/AdminPaymentMethodDto.cs
|
||||||
using System;
|
|
||||||
using Webshop.Domain.Enums;
|
using Webshop.Domain.Enums;
|
||||||
|
using System.Text.Json.Serialization; // Für JsonConverter
|
||||||
|
|
||||||
namespace Webshop.Application.DTOs.Payments
|
namespace Webshop.Application.DTOs.Payments
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,11 @@ namespace Webshop.Application.DTOs.Payments
|
|||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
public PaymentGatewayType PaymentGatewayType { get; set; }
|
public PaymentGatewayType PaymentGatewayType { get; set; }
|
||||||
public string? Configuration { get; set; } // Als JSON-String, den der Admin bearbeitet
|
|
||||||
|
// Configuration ist jetzt ein Objekt, das je nach PaymentGatewayType
|
||||||
|
// eine Instanz von BankTransferConfigurationDto, StripeConfigurationDto etc. sein wird.
|
||||||
|
public object? Configuration { get; set; }
|
||||||
|
|
||||||
public decimal? ProcessingFee { get; set; }
|
public decimal? ProcessingFee { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// src/Webshop.Application/DTOs/Payments/BankTransferConfigurationDto.cs
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Payments;
|
||||||
|
|
||||||
|
public class BankTransferConfigurationDto : IPaymentMethodConfiguration
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string IBAN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string BIC { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string BankName { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// src/Webshop.Application/DTOs/Payments/IPaymentMethodConfiguration.cs
|
||||||
|
namespace Webshop.Application.DTOs.Payments;
|
||||||
|
|
||||||
|
// Leeres Marker-Interface für polymorphe Deserialisierung
|
||||||
|
public interface IPaymentMethodConfiguration { }
|
||||||
18
Webshop.Application/DTOs/Payments/PayPalConfigurationDto.cs
Normal file
18
Webshop.Application/DTOs/Payments/PayPalConfigurationDto.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Payments
|
||||||
|
{
|
||||||
|
public class PayPalConfigurationDto : IPaymentMethodConfiguration
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string ClientId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string ClientSecret { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Webshop.Application/DTOs/Payments/StripeConfigurationDto.cs
Normal file
19
Webshop.Application/DTOs/Payments/StripeConfigurationDto.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Webshop.Application.DTOs.Payments
|
||||||
|
{
|
||||||
|
public class StripeConfigurationDto : IPaymentMethodConfiguration
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string PublicKey { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string SecretKey { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
// src/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs
|
// src/Webshop.Application/Services/Admin/AdminPaymentMethodService.cs
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq; // Für Select
|
using System.Linq;
|
||||||
|
using System.Text.Json; // Wichtig für JsonSerializer
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Webshop.Application.DTOs.Payments; // AdminPaymentMethodDto
|
using Webshop.Application.DTOs.Payments; // AdminPaymentMethodDto und die neuen Configuration-DTOs
|
||||||
using Webshop.Domain.Entities; // PaymentMethod
|
using Webshop.Domain.Entities; // PaymentMethod
|
||||||
|
using Webshop.Domain.Enums; // PaymentGatewayType
|
||||||
using Webshop.Domain.Interfaces; // IPaymentMethodRepository
|
using Webshop.Domain.Interfaces; // IPaymentMethodRepository
|
||||||
using Webshop.Domain.Enums;
|
|
||||||
|
|
||||||
namespace Webshop.Application.Services.Admin
|
namespace Webshop.Application.Services.Admin
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,8 @@ namespace Webshop.Application.Services.Admin
|
|||||||
Description = pm.Description,
|
Description = pm.Description,
|
||||||
IsActive = pm.IsActive,
|
IsActive = pm.IsActive,
|
||||||
PaymentGatewayType = pm.PaymentGatewayType,
|
PaymentGatewayType = pm.PaymentGatewayType,
|
||||||
Configuration = pm.Configuration, // Gibt den JSON-String für den Admin zurück
|
// Deserialisiere den JSON-String aus der DB in ein Objekt für die Admin-Ansicht
|
||||||
|
Configuration = pm.Configuration != null ? JsonSerializer.Deserialize<object>(pm.Configuration) : null,
|
||||||
ProcessingFee = pm.ProcessingFee
|
ProcessingFee = pm.ProcessingFee
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
@@ -47,27 +49,36 @@ namespace Webshop.Application.Services.Admin
|
|||||||
Description = paymentMethod.Description,
|
Description = paymentMethod.Description,
|
||||||
IsActive = paymentMethod.IsActive,
|
IsActive = paymentMethod.IsActive,
|
||||||
PaymentGatewayType = paymentMethod.PaymentGatewayType,
|
PaymentGatewayType = paymentMethod.PaymentGatewayType,
|
||||||
Configuration = paymentMethod.Configuration,
|
// Deserialisiere den JSON-String aus der DB in ein Objekt
|
||||||
|
Configuration = paymentMethod.Configuration != null ? JsonSerializer.Deserialize<object>(paymentMethod.Configuration) : null,
|
||||||
ProcessingFee = paymentMethod.ProcessingFee
|
ProcessingFee = paymentMethod.ProcessingFee
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AdminPaymentMethodDto> CreateAsync(AdminPaymentMethodDto paymentMethodDto)
|
public async Task<AdminPaymentMethodDto> CreateAsync(AdminPaymentMethodDto paymentMethodDto)
|
||||||
{
|
{
|
||||||
|
var (isValid, configJson, errorMessage) = ValidateAndSerializeConfiguration(paymentMethodDto.PaymentGatewayType, paymentMethodDto.Configuration);
|
||||||
|
if (!isValid)
|
||||||
|
{
|
||||||
|
// In einer echten Anwendung würden Sie hier eine benutzerdefinierte Validierungs-Exception werfen,
|
||||||
|
// damit der Controller einen 400 Bad Request zurückgeben kann.
|
||||||
|
throw new ArgumentException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
var newPaymentMethod = new PaymentMethod
|
var newPaymentMethod = new PaymentMethod
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(), // API generiert die ID
|
Id = Guid.NewGuid(),
|
||||||
Name = paymentMethodDto.Name,
|
Name = paymentMethodDto.Name,
|
||||||
Description = paymentMethodDto.Description,
|
Description = paymentMethodDto.Description,
|
||||||
IsActive = paymentMethodDto.IsActive,
|
IsActive = paymentMethodDto.IsActive,
|
||||||
PaymentGatewayType = paymentMethodDto.PaymentGatewayType,
|
PaymentGatewayType = paymentMethodDto.PaymentGatewayType,
|
||||||
Configuration = paymentMethodDto.Configuration,
|
Configuration = configJson, // Speichere den validierten JSON-String
|
||||||
ProcessingFee = paymentMethodDto.ProcessingFee
|
ProcessingFee = paymentMethodDto.ProcessingFee
|
||||||
};
|
};
|
||||||
|
|
||||||
await _paymentMethodRepository.AddAsync(newPaymentMethod);
|
await _paymentMethodRepository.AddAsync(newPaymentMethod);
|
||||||
|
|
||||||
paymentMethodDto.Id = newPaymentMethod.Id; // ID zurückschreiben
|
paymentMethodDto.Id = newPaymentMethod.Id;
|
||||||
return paymentMethodDto;
|
return paymentMethodDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,12 +87,17 @@ namespace Webshop.Application.Services.Admin
|
|||||||
var existingPaymentMethod = await _paymentMethodRepository.GetByIdAsync(paymentMethodDto.Id);
|
var existingPaymentMethod = await _paymentMethodRepository.GetByIdAsync(paymentMethodDto.Id);
|
||||||
if (existingPaymentMethod == null) return false;
|
if (existingPaymentMethod == null) return false;
|
||||||
|
|
||||||
// Eigenschaften aktualisieren
|
var (isValid, configJson, errorMessage) = ValidateAndSerializeConfiguration(paymentMethodDto.PaymentGatewayType, paymentMethodDto.Configuration);
|
||||||
|
if (!isValid)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
existingPaymentMethod.Name = paymentMethodDto.Name;
|
existingPaymentMethod.Name = paymentMethodDto.Name;
|
||||||
existingPaymentMethod.Description = paymentMethodDto.Description;
|
existingPaymentMethod.Description = paymentMethodDto.Description;
|
||||||
existingPaymentMethod.IsActive = paymentMethodDto.IsActive;
|
existingPaymentMethod.IsActive = paymentMethodDto.IsActive;
|
||||||
existingPaymentMethod.PaymentGatewayType = paymentMethodDto.PaymentGatewayType;
|
existingPaymentMethod.PaymentGatewayType = paymentMethodDto.PaymentGatewayType;
|
||||||
existingPaymentMethod.Configuration = paymentMethodDto.Configuration;
|
existingPaymentMethod.Configuration = configJson;
|
||||||
existingPaymentMethod.ProcessingFee = paymentMethodDto.ProcessingFee;
|
existingPaymentMethod.ProcessingFee = paymentMethodDto.ProcessingFee;
|
||||||
|
|
||||||
await _paymentMethodRepository.UpdateAsync(existingPaymentMethod);
|
await _paymentMethodRepository.UpdateAsync(existingPaymentMethod);
|
||||||
@@ -96,5 +112,51 @@ namespace Webshop.Application.Services.Admin
|
|||||||
await _paymentMethodRepository.DeleteAsync(id);
|
await _paymentMethodRepository.DeleteAsync(id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (bool IsValid, string? ConfigJson, string? ErrorMessage) ValidateAndSerializeConfiguration(PaymentGatewayType type, object? configObject)
|
||||||
|
{
|
||||||
|
if (configObject == null) return (true, null, null);
|
||||||
|
|
||||||
|
var configElement = (JsonElement)configObject;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PaymentGatewayType.BankTransfer:
|
||||||
|
var bankConfig = configElement.Deserialize<BankTransferConfigurationDto>();
|
||||||
|
if (bankConfig == null || string.IsNullOrEmpty(bankConfig.IBAN) || string.IsNullOrEmpty(bankConfig.BIC))
|
||||||
|
{
|
||||||
|
return (false, null, "Für Banküberweisung müssen IBAN und BIC angegeben werden.");
|
||||||
|
}
|
||||||
|
return (true, JsonSerializer.Serialize(bankConfig), null);
|
||||||
|
|
||||||
|
case PaymentGatewayType.Stripe:
|
||||||
|
var stripeConfig = configElement.Deserialize<StripeConfigurationDto>();
|
||||||
|
if (stripeConfig == null || string.IsNullOrEmpty(stripeConfig.PublicKey) || string.IsNullOrEmpty(stripeConfig.SecretKey))
|
||||||
|
{
|
||||||
|
return (false, null, "Für Stripe müssen PublicKey und SecretKey angegeben werden.");
|
||||||
|
}
|
||||||
|
return (true, JsonSerializer.Serialize(stripeConfig), null);
|
||||||
|
|
||||||
|
case PaymentGatewayType.PayPal:
|
||||||
|
var payPalConfig = configElement.Deserialize<PayPalConfigurationDto>();
|
||||||
|
if (payPalConfig == null || string.IsNullOrEmpty(payPalConfig.ClientId) || string.IsNullOrEmpty(payPalConfig.ClientSecret))
|
||||||
|
{
|
||||||
|
return (false, null, "Für PayPal müssen ClientId und ClientSecret angegeben werden.");
|
||||||
|
}
|
||||||
|
return (true, JsonSerializer.Serialize(payPalConfig), null);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Für Typen wie Invoice oder CashOnDelivery, die keine spezielle Konfiguration brauchen,
|
||||||
|
// ist ein leeres JSON-Objekt '{}' ein gültiger Wert.
|
||||||
|
return (true, JsonSerializer.Serialize(new object()), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
return (false, null, $"Ungültiges JSON-Format für die Konfiguration: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user