diff --git a/MidrandBooksApi/MidrandBooksApi.csproj b/MidrandBooksApi/MidrandBooksApi.csproj index 70c4a5a..5c3e951 100644 --- a/MidrandBooksApi/MidrandBooksApi.csproj +++ b/MidrandBooksApi/MidrandBooksApi.csproj @@ -54,13 +54,13 @@ - + - + diff --git a/MidrandBooksApi/Payments/Payfast/PayfastConfirmationEndpoint.cs b/MidrandBooksApi/Payments/Payfast/PayfastConfirmationEndpoint.cs index 333af9d..1921260 100644 --- a/MidrandBooksApi/Payments/Payfast/PayfastConfirmationEndpoint.cs +++ b/MidrandBooksApi/Payments/Payfast/PayfastConfirmationEndpoint.cs @@ -24,8 +24,6 @@ public sealed class PayfastConfirmationEndpoint : IEndpoint activity?.SetTag("messaging.system", "payfast"); activity?.SetTag("messaging.destination.name", "payments/payfast/confirm"); - string? remoteIp = request.HttpContext.Connection.RemoteIpAddress?.ToString(); - var formCollection = await request.ReadFormAsync(cancellationToken); if (!formCollection.TryGetValue("signature", out var signatureValues) || string.IsNullOrWhiteSpace(signatureValues.ToString())) @@ -34,29 +32,27 @@ public sealed class PayfastConfirmationEndpoint : IEndpoint string incomingSignature = signatureValues.ToString().Trim(); var payload = ParseForm(formCollection, incomingSignature); - var paramDictionary = payload.ToParamDictionary(); - string? passphrase = configuration["HasherSettings:PayfastPassphrase"]; + string? passphrase = configuration["PayfastSettings:Passphrase"]; - var signatureCheck = PayfastService.GenerateSignature(paramDictionary, passphrase); - - if (signatureCheck.IsFailed || !string.Equals(signatureCheck.Value, incomingSignature, StringComparison.OrdinalIgnoreCase)) + if (!PayfastService.VerifyIncomingSignatureFromForm(formCollection, passphrase!)) { - logger.LogCritical($"Incoming sugnature failed validation: {incomingSignature}, {signatureCheck.Errors.Select(e => e.Message).ToList()}"); - + logger.LogCritical($"Incoming signature failed validation: {incomingSignature}"); return Results.Unauthorized(); } - var formPairs = formCollection.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString())}"); + var sortedPairs = formCollection + .Where(kvp => !kvp.Key.Equals("signature", StringComparison.OrdinalIgnoreCase)) + .OrderBy(kvp => kvp.Key, StringComparer.Ordinal) + .Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString().Trim())}"); - string rawQueryParamString = string.Join("&", formPairs); + string rawQueryParamString = string.Join("&", sortedPairs); bool isSandbox = !hostEnvironment.IsProduction(); - var serverConfirmation = await payfastService.ValidateServerConfirmationAsync(rawQueryParamString, isSandbox, cancellationToken); if (serverConfirmation.IsFailed || !serverConfirmation.Value) { - logger.LogCritical($"Server confirmation failed: {rawQueryParamString}, {serverConfirmation.Errors.Select(e => e.Message).ToList()}"); + logger.LogCritical($"Payfast server validation ping rejected the request data: {rawQueryParamString}"); return Results.Unauthorized(); } @@ -67,7 +63,6 @@ public sealed class PayfastConfirmationEndpoint : IEndpoint await jobOrchestrator.SendAsync(notification, cancellationToken); activity?.SetStatus(ActivityStatusCode.Ok); - return Results.Ok(); }) .WithDescription("Securely confirm and process an incoming Payfast merchant payment callback.")