using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Text.RegularExpressions; namespace QRPay.Models; public class PaymentRequest : IValidatableObject { private static readonly Regex IbanRegex = new( "^[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}$", RegexOptions.Compiled); [StringLength(70, ErrorMessage = "Name cannot exceed 70 characters")] public string Name { get; set; } = string.Empty; [Required(ErrorMessage = "IBAN is required")] public string IBAN { get; set; } = string.Empty; [Required(ErrorMessage = "Amount is required")] [Range(typeof(decimal), "0.01", "999999.99", ErrorMessage = "Amount must be between €0.01 and €999,999.99")] public decimal? Amount { get; set; } [StringLength(140, ErrorMessage = "Remark cannot exceed 140 characters")] public string Remark { get; set; } = string.Empty; public string NormalizedIBAN => NormalizeIban(IBAN); public static string NormalizeIban(string? iban) { return string.IsNullOrWhiteSpace(iban) ? string.Empty : iban.Replace(" ", string.Empty, StringComparison.Ordinal).ToUpperInvariant(); } public IReadOnlyList ValidateForQrRoute() { var errors = new List(); var normalizedIban = NormalizedIBAN; if (string.IsNullOrWhiteSpace(normalizedIban)) { errors.Add("Missing required query parameter: iban."); } else if (!IbanRegex.IsMatch(normalizedIban)) { errors.Add("Invalid IBAN format."); } if (Amount is null) { errors.Add("Missing required query parameter: amount."); } else if (Amount < 0.01m || Amount > 999999.99m) { errors.Add("Amount must be between 0.01 and 999999.99 EUR."); } if (!string.IsNullOrEmpty(Name) && Name.Length > 70) { errors.Add("Name cannot exceed 70 characters."); } if (!string.IsNullOrEmpty(Remark) && Remark.Length > 140) { errors.Add("Remark cannot exceed 140 characters."); } return errors; } public static bool TryParseAmount(string? value, out decimal amount) { return decimal.TryParse( value, NumberStyles.Number, CultureInfo.InvariantCulture, out amount); } public IEnumerable Validate(ValidationContext validationContext) { if (!string.IsNullOrWhiteSpace(IBAN) && !IbanRegex.IsMatch(NormalizedIBAN)) { yield return new ValidationResult( "Please enter a valid IBAN", [nameof(IBAN)]); } } }