113 lines
4.1 KiB
C#
113 lines
4.1 KiB
C#
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
|
|
}
|
|
} |