Added payment gateway ledger service to payments feature
This commit is contained in:
@@ -24,7 +24,8 @@ public class Fixture : IDisposable
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
Services = new ServiceCollection()
|
||||
Services = new ServiceCollection()
|
||||
.AddHttpClient()
|
||||
.AddMediator()
|
||||
.AddLogging()
|
||||
.AddEmailServiceBus()
|
||||
@@ -33,6 +34,7 @@ public class Fixture : IDisposable
|
||||
.AddEmailServices(Configuration)
|
||||
.AddSingleton(Configuration)
|
||||
.AddShopServices()
|
||||
.AddHashServices(Configuration)
|
||||
.BuildServiceProvider();
|
||||
|
||||
Mediator = Services.GetRequiredService<IMediator>();
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="System.Net" />
|
||||
<Using Include="System.Text.Json" />
|
||||
<Using Include="System.Diagnostics" />
|
||||
<Using Include="Xunit" />
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
using LiteCharms.Features.MidrandBooks.Payments;
|
||||
using LiteCharms.Features.MidrandBooks.Payments.Models;
|
||||
using LiteCharms.Features.MidrandBooks.Tests.Common;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Tests;
|
||||
|
||||
public sealed class PayfastServiceFeatureTests(Fixture fixture) : IClassFixture<Fixture>
|
||||
{
|
||||
private readonly PayfastService payfastService = fixture.Services.GetRequiredService<PayfastService>();
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task WriteLedgerEntryAsync_ShouldReturn_ResultWithGatewayLedgerId()
|
||||
{
|
||||
var request = new CreateGatewayLedgerEntry
|
||||
{
|
||||
OrderId = 1,
|
||||
PaymentId = 1,
|
||||
MerchantPaymentId = "M_REF_TEST_99",
|
||||
PayfastPaymentId = "PF_SYS_ID_10023",
|
||||
CustomerEmail = "buyer@litecharms.co.za",
|
||||
AmountGross = 350.00m,
|
||||
AmountFee = 12.50m,
|
||||
AmountNet = 337.50m,
|
||||
PaymentStatus = "COMPLETE"
|
||||
};
|
||||
|
||||
var result = await payfastService.WriteLedgerEntryAsync(request, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.True(result.Value > 0);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task ValidateReferrerIpAsync_WithValidPayfastHostIp_ShouldReturnTrue()
|
||||
{
|
||||
var addresses = await Dns.GetHostAddressesAsync("sandbox.payfast.co.za", fixture.CancellationToken);
|
||||
|
||||
string liveTargetIp = addresses.First().ToString();
|
||||
|
||||
var result = await payfastService.ValidateReferrerIpAsync(liveTargetIp, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.True(result.Value);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task ValidateReferrerIpAsync_WithUntrustedIp_ShouldReturnFalse()
|
||||
{
|
||||
string rogueIp = "8.8.8.8";
|
||||
|
||||
var result = await payfastService.ValidateReferrerIpAsync(rogueIp, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.False(result.Value);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public void ValidatePaymentAmount_WhenWithinAllowableDelta_ShouldReturnTrue()
|
||||
{
|
||||
decimal systemExpectedTotal = 199.99m;
|
||||
string gatewayClearedGross = "200.00"; // Variance is exactly R0.01
|
||||
|
||||
var result = payfastService.ValidatePaymentAmount(systemExpectedTotal, gatewayClearedGross);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.True(result.Value);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public void ValidatePaymentAmount_WhenVarianceBreachesDeltaBounds_ShouldReturnFalse()
|
||||
{
|
||||
decimal systemExpectedTotal = 199.99m;
|
||||
string gatewayClearedGross = "150.00";
|
||||
|
||||
var result = payfastService.ValidatePaymentAmount(systemExpectedTotal, gatewayClearedGross);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.False(result.Value);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task ValidateServerConfirmationAsync_WithUnrecognizedPayload_ShouldReturnFalseFromCentralGateway()
|
||||
{
|
||||
// Arrange - Execute against actual Payfast servers using raw mock parameters.
|
||||
// The server handshake will return 200 OK with string payload 'INVALID'
|
||||
string arbitraryParameters = "merchant_id=10000000&payment_status=COMPLETE";
|
||||
|
||||
var result = await payfastService.ValidateServerConfirmationAsync(arbitraryParameters, isSandbox: true, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.False(result.Value); // Handshake data rejected as fraudulent/unrecognized
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public void GenerateSignature_WithStandardTelemetryData_ShouldSucceedAndHashString()
|
||||
{
|
||||
var telemetryPayload = new Dictionary<string, string?>
|
||||
{
|
||||
{ "merchant_id", "10049307" },
|
||||
{ "merchant_key", "ju6navn0jcbf0" },
|
||||
{ "amount_gross", "250.00" },
|
||||
{ "item_name", "Midrand School Textbook Variant A" }
|
||||
};
|
||||
|
||||
string passphrase = "oauth_test_signature_pass";
|
||||
|
||||
var result = PayfastService.GenerateSignature(telemetryPayload, passphrase);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.False(string.IsNullOrWhiteSpace(result.Value));
|
||||
Assert.Equal(32, result.Value.Length); // MD5 outputs hex representations totaling 32 characters
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
{
|
||||
"ValidPayfastHosts": [
|
||||
"www.payfast.co.za",
|
||||
"sandbox.payfast.co.za",
|
||||
"w1w.payfast.co.za",
|
||||
"w2w.payfast.co.za",
|
||||
"ips.payfast.co.za",
|
||||
"api.payfast.co.za",
|
||||
"payment.payfast.io"
|
||||
],
|
||||
"HasherSettings": {
|
||||
"MinHashLength": 11
|
||||
},
|
||||
"BookshopS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
|
||||
Reference in New Issue
Block a user