Compare commits

...
8 Commits
3 changed files with 51 additions and 22 deletions
@@ -1,5 +1,4 @@
using LiteCharms.Features.Abstractions; using LiteCharms.Features.Browser;
using LiteCharms.Features.Browser;
using LiteCharms.Features.Hasher; using LiteCharms.Features.Hasher;
using LiteCharms.Features.MidrandBooks.Authors.Models; using LiteCharms.Features.MidrandBooks.Authors.Models;
using LiteCharms.Features.MidrandBooks.Payments.Models; using LiteCharms.Features.MidrandBooks.Payments.Models;
@@ -7,7 +6,7 @@ using LiteCharms.Features.MidrandBooks.Products.Models;
namespace LiteCharms.Features.MidrandBooks.Payments; namespace LiteCharms.Features.MidrandBooks.Payments;
public sealed class CartService(LocalStorageService localStorage) : IService public sealed class CartService(LocalStorageService localStorage)
{ {
private readonly string CartStorageKey = HashService.ToMd5Hash(nameof(Cart)).Value; private readonly string CartStorageKey = HashService.ToMd5Hash(nameof(Cart)).Value;
@@ -147,23 +147,56 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
{ {
var pfOutput = new StringBuilder(); var pfOutput = new StringBuilder();
foreach (var kvp in data) // Define the exact structural sequence mandated by Payfast's documentation
string[] mandatorySequence =
[
"merchant_id",
"merchant_key",
"return_url",
"cancel_url",
"notify_url",
"name_first",
"name_last",
"email_address",
"cell_number",
"m_payment_id",
"amount",
"item_name",
"item_description",
"custom_int1",
"custom_int2",
"custom_int3",
"custom_int4",
"custom_int5",
"custom_str1",
"custom_str2",
"custom_str3",
"custom_str4",
"custom_str5",
"email_confirmation",
"confirmation_address",
"payment_method",
"subscription_type",
"billing_date",
"recurring_amount",
"frequency",
"cycles"
];
// 1. Iterate explicitly by the mandatory positional array sequence instead of the dictionary's internal order
foreach (string key in mandatorySequence)
{ {
if (string.IsNullOrEmpty(kvp.Value)) // Only append if the key exists in your source dictionary and contains data
continue; 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());
if (kvp.Key.Equals("signature", StringComparison.OrdinalIgnoreCase)) // Payfast requires all OTHER percent-encoded hex arrays to be UPPERCASE (e.g., %3A instead of %3a)
continue; string val = Regex.Replace(encodedVal, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant());
string key = kvp.Key; pfOutput.Append($"{key}={val}&");
}
// 1. Payfast requires spaces to be '+' signs. HttpUtility does this perfectly.
string encodedVal = HttpUtility.UrlEncode(kvp.Value.Trim());
// 2. 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 string getString = pfOutput.Length > 0
@@ -172,7 +205,6 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
if (!string.IsNullOrWhiteSpace(passPhrase)) if (!string.IsNullOrWhiteSpace(passPhrase))
{ {
// Apply the exact same encoding rule to your passphrase
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim()); string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
string safePassphrase = Regex.Replace(encodedPassphrase, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant()); string safePassphrase = Regex.Replace(encodedPassphrase, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant());
@@ -1,8 +1,6 @@
using LiteCharms.Features.Abstractions; namespace LiteCharms.Features.Browser;
namespace LiteCharms.Features.Browser; public sealed class LocalStorageService(ProtectedLocalStorage storage)
public sealed class LocalStorageService(ProtectedLocalStorage storage) : IService
{ {
public async ValueTask<Result> DeleteAsync(string key) public async ValueTask<Result> DeleteAsync(string key)
{ {