using Grpc.Core; using Microsoft.JSInterop; namespace FrontOffice.Main.Utilities; public static class CreditChargeNavigation { public const long MinChargeAmount = 10_000; public const string ReturnUrlStorageKey = "credit_charge_return_url"; private const string InsufficientBalanceMarker = "موجودی کیف پول کافی نیست"; public static long NormalizeChargeAmount(long shortfall) => Math.Max(shortfall, MinChargeAmount); public static string BuildChargeUrl(long amount, string? returnUrl) { var url = $"{RouteConstants.Profile.ChargeCreditWallet}?amount={amount}"; if (IsValidReturnUrl(returnUrl)) url += $"&returnUrl={Uri.EscapeDataString(returnUrl!)}"; return url; } public static bool IsValidReturnUrl(string? url) => !string.IsNullOrWhiteSpace(url) && url.StartsWith('/') && !url.StartsWith("//", StringComparison.Ordinal); public static bool IsInsufficientWalletBalanceMessage(string? message) => message?.Contains(InsufficientBalanceMarker, StringComparison.Ordinal) == true; public static bool IsInsufficientWalletBalance(Exception ex) => ex switch { RpcException rpc => IsInsufficientWalletBalanceMessage(rpc.Status.Detail) || IsInsufficientWalletBalanceMessage(rpc.Message), _ => IsInsufficientWalletBalanceMessage(ex.Message) }; public static async Task SaveReturnUrlAsync(IJSRuntime js, string returnUrl) { if (!IsValidReturnUrl(returnUrl)) return; await js.InvokeVoidAsync("sessionStorage.setItem", ReturnUrlStorageKey, returnUrl); } public static async Task GetReturnUrlAsync(IJSRuntime js) => await js.InvokeAsync("sessionStorage.getItem", ReturnUrlStorageKey); public static async Task ClearReturnUrlAsync(IJSRuntime js) => await js.InvokeVoidAsync("sessionStorage.removeItem", ReturnUrlStorageKey); }