Catering for service registration of non-UI apps
continuous-integration/drone/pr Build is passing

This commit is contained in:
Khwezi Mngoma
2026-06-13 10:45:31 +02:00
parent 9b474a398b
commit 630e74814b
4 changed files with 38 additions and 37 deletions
@@ -1,11 +1,12 @@
using LiteCharms.Features.Abstractions;
using LiteCharms.Features.Browser;
using LiteCharms.Features.MidrandBooks.Abstractions;
namespace LiteCharms.Features.MidrandBooks.Extensions;
public static class Shop
{
public static IServiceCollection AddShopServices(this IServiceCollection services)
public static IServiceCollection AddShopServices(this IServiceCollection services, bool includeLocalStorage = false)
{
var serviceType = typeof(IService);
@@ -19,6 +20,9 @@ public static class Shop
foreach (var coreImplementation in coreImplementations) services.AddScoped(coreImplementation);
if (includeLocalStorage)
services.AddScoped<LocalStorageService>();
return services;
}
}
@@ -147,8 +147,35 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
{
var pfOutput = new StringBuilder();
// Define the exact structural sequence mandated by Payfast's documentation
string[] mandatorySequence =
var mandatorySequence = GetPayfastMandatoryFieldSequence();
foreach (string key in mandatorySequence)
{
if (data.TryGetValue(key, out string? rawValue) && !string.IsNullOrEmpty(rawValue))
{
string encodedVal = HttpUtility.UrlEncode(rawValue.Trim());
string val = PercentEncodingRegex.Replace(encodedVal, m => m.Value.ToUpperInvariant());
pfOutput.Append($"{key}={val}&");
}
}
var getString = pfOutput.Length > 0
? pfOutput.ToString()[..^1]
: string.Empty;
if (!string.IsNullOrWhiteSpace(passPhrase))
{
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
string safePassphrase = PercentEncodingRegex.Replace(encodedPassphrase, m => m.Value.ToUpperInvariant());
getString += $"&passphrase={safePassphrase}";
}
return HashService.ToMd5Hash(getString);
}
private static string[] GetPayfastMandatoryFieldSequence() =>
[
"merchant_id",
"merchant_key",
@@ -182,35 +209,4 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
"frequency",
"cycles"
];
// 1. Iterate explicitly by the mandatory positional array sequence instead of the dictionary's internal order
foreach (string key in mandatorySequence)
{
// Only append if the key exists in your source dictionary and contains data
if (data.TryGetValue(key, out string? rawValue) && !string.IsNullOrEmpty(rawValue))
{
// Payfast requires spaces to be '+' signs. HttpUtility does this natively.
string encodedVal = HttpUtility.UrlEncode(rawValue.Trim());
// Payfast requires all OTHER percent-encoded hex arrays to be UPPERCASE (e.g., %3A instead of %3a)
string val = Regex.Replace(encodedVal, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant());
pfOutput.Append($"{key}={val}&");
}
}
string getString = pfOutput.Length > 0
? pfOutput.ToString()[..^1]
: string.Empty;
if (!string.IsNullOrWhiteSpace(passPhrase))
{
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
string safePassphrase = Regex.Replace(encodedPassphrase, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant());
getString += $"&passphrase={safePassphrase}";
}
return HashService.ToMd5Hash(getString);
}
}