Add /qr route and refactor payment QR code generation
Introduce a dedicated /qr route for QR code preview via query params. Add PaymentRequest model for payment data and validation, replacing the old inline model. Extract QR code generation to PaymentQrCodeService for reuse. Refactor Home.razor to use new model/service and improve validation/UI. Add EmptyLayout for minimal layout needs. Update .gitignore and _Imports.razor for new structure.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
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<string> ValidateForQrRoute()
|
||||
{
|
||||
var errors = new List<string>();
|
||||
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<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(IBAN) && !IbanRegex.IsMatch(NormalizedIBAN))
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
"Please enter a valid IBAN",
|
||||
[nameof(IBAN)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user