Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6248d03ead | |||
| 9b474a398b | |||
| 3deae15f5a | |||
| 8e1df7938b | |||
| d9f2d32c76 | |||
| 9296f0331e | |||
| 1ace61baa5 | |||
| e3e49b8db2 | |||
| 2ed15b548f | |||
| 7d2bc7f1f2 |
@@ -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,21 +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}&");
|
||||||
|
}
|
||||||
string encodedVal = Uri.EscapeDataString(kvp.Value.Trim());
|
|
||||||
|
|
||||||
string val = PercentEncodingRegex.Replace(encodedVal, m => m.Value.ToLowerInvariant());
|
|
||||||
|
|
||||||
pfOutput.Append($"{key}={val}&");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string getString = pfOutput.Length > 0
|
string getString = pfOutput.Length > 0
|
||||||
@@ -170,8 +205,8 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(passPhrase))
|
if (!string.IsNullOrWhiteSpace(passPhrase))
|
||||||
{
|
{
|
||||||
string encodedPassphrase = Uri.EscapeDataString(passPhrase.Trim());
|
string encodedPassphrase = HttpUtility.UrlEncode(passPhrase.Trim());
|
||||||
string safePassphrase = PercentEncodingRegex.Replace(encodedPassphrase, m => m.Value.ToLowerInvariant());
|
string safePassphrase = Regex.Replace(encodedPassphrase, "%[0-9A-Fa-f]{2}", m => m.Value.ToUpperInvariant());
|
||||||
|
|
||||||
getString += $"&passphrase={safePassphrase}";
|
getString += $"&passphrase={safePassphrase}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user