diff --git a/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/SafaricomPinlessProxyTests.cs b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/SafaricomPinlessProxyTests.cs index d0bcefe3..d6d71df4 100644 --- a/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/SafaricomPinlessProxyTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/SafaricomPinlessProxyTests.cs @@ -32,7 +32,9 @@ public async Task SafaricomPinlessProxy_ProcessSaleMessage_TopupSuccessful_SaleM IOperatorProxy safaricomPinlessproxy = new SafaricomPinlessProxy(safaricomConfiguration, httpClient); - OperatorResponse operatorResponse = await safaricomPinlessproxy.ProcessSaleMessage(TestData.TransactionId, + OperatorResponse operatorResponse = await safaricomPinlessproxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, TestData.Merchant, TestData.TransactionDateTime, TestData.TransactionReference, @@ -59,7 +61,9 @@ public async Task SafaricomPinlessProxy_ProcessSaleMessage_TopupFailed_SaleMessa IOperatorProxy safaricomPinlessproxy = new SafaricomPinlessProxy(safaricomConfiguration, httpClient); - OperatorResponse operatorResponse = await safaricomPinlessproxy.ProcessSaleMessage(TestData.TransactionId, + OperatorResponse operatorResponse = await safaricomPinlessproxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, TestData.Merchant, TestData.TransactionDateTime, TestData.TransactionReference, @@ -87,7 +91,9 @@ public async Task SafaricomPinlessProxy_ProcessSaleMessage_FailedToSend_ErrorThr Should.Throw(async () => { - await safaricomPinlessproxy.ProcessSaleMessage(TestData.TransactionId, + await safaricomPinlessproxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, TestData.Merchant, TestData.TransactionDateTime, TestData.TransactionReference, @@ -123,7 +129,9 @@ public async Task SafaricomPinlessProxy_ProcessSaleMessage_InvalidData_ErrorThro Should.Throw(async () => { - await safaricomPinlessproxy.ProcessSaleMessage(TestData.TransactionId, + await safaricomPinlessproxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, TestData.Merchant, TestData.TransactionDateTime, TestData.TransactionReference, diff --git a/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/VoucherManagementProxyTests.cs b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/VoucherManagementProxyTests.cs new file mode 100644 index 00000000..b36fe759 --- /dev/null +++ b/TransactionProcessor.BusinessLogic.Tests/OperatorInterfaces/VoucherManagementProxyTests.cs @@ -0,0 +1,128 @@ +namespace TransactionProcessor.BusinessLogic.Tests.OperatorInterfaces +{ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using BusinessLogic.OperatorInterfaces; + using BusinessLogic.OperatorInterfaces.VoucherManagement; + using Moq; + using Shouldly; + using Testing; + using VoucherManagement.Client; + using VoucherManagement.DataTransferObjects; + using Xunit; + + public class VoucherManagementProxyTests + { + [Fact] + public async Task VoucherManagementProxy_ProcessSaleMessage_VoucherIssueSuccessful_SaleMessageIsProcessed() + { + Mock voucherManagementClient = new Mock(); + voucherManagementClient.Setup(v => v.IssueVoucher(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(TestData.IssueVoucherResponse); + IOperatorProxy voucherManagementProxy = new VoucherManagementProxy(voucherManagementClient.Object); + + OperatorResponse operatorResponse = await voucherManagementProxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + TestData.AdditionalTransactionMetaDataForVoucher, + CancellationToken.None); + + operatorResponse.ShouldNotBeNull(); + operatorResponse.IsSuccessful.ShouldBeTrue(); + operatorResponse.ResponseCode.ShouldBe("0000"); + operatorResponse.ResponseMessage.ShouldBe("SUCCESS"); + operatorResponse.AdditionalTransactionResponseMetadata.ShouldContainKey("VoucherCode"); + operatorResponse.AdditionalTransactionResponseMetadata.ShouldContainKey("VoucherExpiryDate"); + } + + [Fact] + public async Task VoucherManagementProxy_ProcessSaleMessage_FailedToSend_ErrorThrown() + { + Mock voucherManagementClient = new Mock(); + voucherManagementClient.Setup(v => v.IssueVoucher(It.IsAny(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new Exception()); + IOperatorProxy voucherManagementProxy = new VoucherManagementProxy(voucherManagementClient.Object); + + Should.Throw(async () => + { + await voucherManagementProxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + TestData.AdditionalTransactionMetaDataForVoucher, + CancellationToken.None); + + + }); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + [InlineData("A")] + public async Task VoucherManagementProxy_ProcessSaleMessage_InvalidData_TransactionAmount_ErrorThrown(String transactionAmount) + { + Mock voucherManagementClient = new Mock(); + + Dictionary additionalMetatdata = TestData.AdditionalTransactionMetaDataForVoucher; + additionalMetatdata["Amount"] = transactionAmount; + + IOperatorProxy voucherManagementProxy = new VoucherManagementProxy(voucherManagementClient.Object); + + Should.Throw(async () => + { + await voucherManagementProxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + additionalMetatdata, + CancellationToken.None); + + + }); + } + + [Theory] + [InlineData("", "")] + [InlineData("", null)] + [InlineData(null, "")] + [InlineData(null, null)] + public async Task VoucherManagementProxy_ProcessSaleMessage_InvalidData_RecipientDetails_ErrorThrown(String recipientEmail, String recipientMobile) + { + Mock voucherManagementClient = new Mock(); + + Dictionary additionalMetatdata = new Dictionary + { + {"Amount", "10.00"}, + {"RecipientEmail", recipientEmail }, + {"RecipientMobile", recipientMobile} + + }; + + IOperatorProxy voucherManagementProxy = new VoucherManagementProxy(voucherManagementClient.Object); + + Should.Throw(async () => + { + await voucherManagementProxy.ProcessSaleMessage(TestData.TokenResponse().AccessToken, + TestData.TransactionId, + TestData.OperatorIdentifier1, + TestData.Merchant, + TestData.TransactionDateTime, + TestData.TransactionReference, + additionalMetatdata, + CancellationToken.None); + + + }); + } + } +} \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index d14e1309..f37361b7 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -491,7 +491,9 @@ public async Task TransactionDomainService_ProcessSaleTransaction_SuccesfulOpera .ReturnsAsync(TestData.GetCompletedAuthorisedSaleTransactionAggregate); Mock operatorProxy = new Mock(); - operatorProxy.Setup(o => o.ProcessSaleMessage(It.IsAny(), + operatorProxy.Setup(o => o.ProcessSaleMessage(It.IsAny(), + It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -549,7 +551,9 @@ public async Task TransactionDomainService_ProcessSaleTransaction_FailedOperator .ReturnsAsync(TestData.GetDeclinedTransactionAggregate(TransactionResponseCode.TransactionDeclinedByOperator)); Mock operatorProxy = new Mock(); - operatorProxy.Setup(o => o.ProcessSaleMessage(It.IsAny(), + operatorProxy.Setup(o => o.ProcessSaleMessage(It.IsAny(), + It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/IOperatorProxy.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/IOperatorProxy.cs index 39ed4abf..b6c0bf14 100644 --- a/TransactionProcessor.BusinessLogic/OperatorInterfaces/IOperatorProxy.cs +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/IOperatorProxy.cs @@ -16,14 +16,18 @@ public interface IOperatorProxy /// /// Processes the sale message. /// + /// The access token. /// The transaction identifier. + /// The operator identifier. /// The merchant. /// The transaction date time. /// The transaction reference. /// The additional transaction metadata. /// The cancellation token. /// - Task ProcessSaleMessage(Guid transactionId, + Task ProcessSaleMessage(String accessToken, + Guid transactionId, + String operatorIdentifier, MerchantResponse merchant, DateTime transactionDateTime, String transactionReference, diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/SafaricomPinless/SafaricomPinlessProxy.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/SafaricomPinless/SafaricomPinlessProxy.cs index 71d65e41..bc9a2337 100644 --- a/TransactionProcessor.BusinessLogic/OperatorInterfaces/SafaricomPinless/SafaricomPinlessProxy.cs +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/SafaricomPinless/SafaricomPinlessProxy.cs @@ -49,21 +49,23 @@ public SafaricomPinlessProxy(SafaricomConfiguration safaricomConfiguration, /// /// Processes the sale message. /// + /// The access token. /// The transaction identifier. + /// The operator identifier. /// The merchant. /// The transaction date time. /// The transaction reference. /// The additional transaction metadata. /// The cancellation token. /// - /// - /// Amount is a required field for this transaction type + /// Amount is a required field for this transaction type /// or /// CustomerAccountNumber is a required field for this transaction type /// or - /// Error sending request [{requestUrl}] to Safaricom. Status Code [{responseMessage.StatusCode}] - /// - public async Task ProcessSaleMessage(Guid transactionId, + /// Error sending request [{requestUrl}] to Safaricom. Status Code [{responseMessage.StatusCode}] + public async Task ProcessSaleMessage(String accessToken, + Guid transactionId, + String operatorIdentifier, MerchantResponse merchant, DateTime transactionDateTime, String transactionReference, diff --git a/TransactionProcessor.BusinessLogic/OperatorInterfaces/VoucherManagement/VoucherManagementProxy.cs b/TransactionProcessor.BusinessLogic/OperatorInterfaces/VoucherManagement/VoucherManagementProxy.cs new file mode 100644 index 00000000..11f1d09d --- /dev/null +++ b/TransactionProcessor.BusinessLogic/OperatorInterfaces/VoucherManagement/VoucherManagementProxy.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TransactionProcessor.BusinessLogic.OperatorInterfaces.VoucherManagement +{ + using System.Security.Policy; + using System.Threading; + using System.Threading.Tasks; + using EstateManagement.DataTransferObjects.Responses; + using global::VoucherManagement.Client; + using global::VoucherManagement.DataTransferObjects; + using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; + + /// + /// + /// + /// + public class VoucherManagementProxy : IOperatorProxy + { + /// + /// The voucher management client + /// + private readonly IVoucherManagementClient VoucherManagementClient; + + /// + /// Initializes a new instance of the class. + /// + /// The voucher management client. + public VoucherManagementProxy(IVoucherManagementClient voucherManagementClient) + { + this.VoucherManagementClient = voucherManagementClient; + } + /// + /// Processes the sale message. + /// + /// The access token. + /// The transaction identifier. + /// The operator identifier. + /// The merchant. + /// The transaction date time. + /// The transaction reference. + /// The additional transaction metadata. + /// The cancellation token. + /// + /// + /// Transaction Amount is not a valid decimal value + /// or + /// Recipient details (either email or mobile) is a required field for this transaction type + /// + public async Task ProcessSaleMessage(String accessToken, + Guid transactionId, + String operatorIdentifier, + MerchantResponse merchant, + DateTime transactionDateTime, + String transactionReference, + Dictionary additionalTransactionMetadata, + CancellationToken cancellationToken) + { + // Extract the required fields + String recipientEmail = additionalTransactionMetadata.GetValueOrDefault("RecipientEmail"); + String recipientMobile = additionalTransactionMetadata.GetValueOrDefault("RecipientMobile"); + String transactionAmount = additionalTransactionMetadata.GetValueOrDefault("Amount"); + + // Covert the transaction amount to Decimal and remove decimal places + if (Decimal.TryParse(transactionAmount, out Decimal amountAsDecimal) == false) + { + throw new Exception("Transaction Amount is not a valid decimal value"); + } + + if (String.IsNullOrEmpty(recipientEmail) && String.IsNullOrEmpty(recipientMobile)) + { + throw new Exception("Recipient details (either email or mobile) is a required field for this transaction type"); + } + + IssueVoucherRequest apiRequest = new IssueVoucherRequest + { + EstateId = merchant.EstateId, + OperatorIdentifier = operatorIdentifier, + RecipientEmail = recipientEmail, + RecipientMobile = recipientMobile, + TransactionId = transactionId, + Value = amountAsDecimal + }; + + IssueVoucherResponse apiResponse = await this.VoucherManagementClient.IssueVoucher(accessToken, apiRequest, cancellationToken); + + // Build the response metadata + Dictionary additionalTransactionResponseMetadata = new Dictionary(); + additionalTransactionResponseMetadata.Add("VoucherCode", apiResponse.VoucherCode); + additionalTransactionResponseMetadata.Add("VoucherMessage", apiResponse.Message); + additionalTransactionResponseMetadata.Add("VoucherExpiryDate", apiResponse.ExpiryDate.ToString("yyyy-MM-dd")); + + return new OperatorResponse + { + TransactionId = transactionId.ToString("N"), + ResponseCode = "0000", + ResponseMessage = "SUCCESS", + // This may contain the voucher details to be logged with the transaction, and for possible receipt email/print + AdditionalTransactionResponseMetadata = additionalTransactionResponseMetadata, + AuthorisationCode = "ABCD1234", + IsSuccessful = true + }; + } + } +} diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index 546c3c7c..6827a144 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -220,7 +220,9 @@ await this.TransactionAggregateManager.RecordAdditionalRequestData(estateId, MerchantResponse merchant = await this.GetMerchant(estateId, merchantId, cancellationToken); IOperatorProxy operatorProxy = this.OperatorProxyResolver(operatorIdentifier); OperatorResponse operatorResponse = - await operatorProxy.ProcessSaleMessage(transactionId, + await operatorProxy.ProcessSaleMessage(this.TokenResponse.AccessToken, + transactionId, + operatorIdentifier, merchant, transactionDateTime, transactionReference, @@ -277,13 +279,16 @@ await this.TransactionAggregateManager.RecordAdditionalResponseData(estateId, TransactionAggregate transactionAggregate = await this.TransactionAggregateManager.GetAggregate(estateId, transactionId, cancellationToken); + // Get the model from the aggregate + Transaction transaction = transactionAggregate.GetTransaction(); + return new ProcessSaleTransactionResponse { - ResponseMessage = transactionAggregate.ResponseMessage, - ResponseCode = transactionAggregate.ResponseCode, + ResponseMessage = transaction.ResponseMessage, + ResponseCode = transaction.ResponseCode, EstateId = estateId, MerchantId = merchantId, - AdditionalTransactionMetadata = new Dictionary() + AdditionalTransactionMetadata = transaction.AdditionalResponseMetadata }; } diff --git a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj index a2d983a8..3445e867 100644 --- a/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj +++ b/TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj @@ -13,6 +13,7 @@ + diff --git a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs index 212ca09a..69e43634 100644 --- a/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs +++ b/TransactionProcessor.IntegrationTests/Common/DockerHelper.cs @@ -121,6 +121,60 @@ public DockerHelper(NlogLogger logger, TestingContext testingContext) #endregion public const Int32 TestHostPort = 9000; + private Int32 VoucherManagementPort; + protected String VoucherManagementContainerName; + public const Int32 VoucherManagementDockerPort = 5007; + + public static IContainerService SetupVoucherManagementContainer(String containerName, ILogger logger, String imageName, + List networkServices, + String hostFolder, + (String URL, String UserName, String Password)? dockerCredentials, + String securityServiceContainerName, + String estateManagementContainerName, + String eventStoreContainerName, + (string clientId, string clientSecret) clientDetails, + Boolean forceLatestImage = false, + Int32 securityServicePort = DockerHelper.SecurityServiceDockerPort, + List additionalEnvironmentVariables = null) + { + logger.LogInformation("About to Start Voucher Management Container"); + + List environmentVariables = new List(); + environmentVariables.Add($"EventStoreSettings:ConnectionString=https://{eventStoreContainerName}:{DockerHelper.EventStoreHttpDockerPort}"); + environmentVariables.Add($"AppSettings:SecurityService=http://{securityServiceContainerName}:{securityServicePort}"); + environmentVariables.Add($"AppSettings:EstateManagementApi=http://{estateManagementContainerName}:{DockerHelper.EstateManagementDockerPort}"); + environmentVariables.Add($"SecurityConfiguration:Authority=http://{securityServiceContainerName}:{securityServicePort}"); + environmentVariables.Add($"urls=http://*:{DockerHelper.VoucherManagementDockerPort}"); + environmentVariables.Add($"AppSettings:ClientId={clientDetails.clientId}"); + environmentVariables.Add($"AppSettings:ClientSecret={clientDetails.clientSecret}"); + + if (additionalEnvironmentVariables != null) + { + environmentVariables.AddRange(additionalEnvironmentVariables); + } + + ContainerBuilder voucherManagementContainer = new Builder().UseContainer().WithName(containerName) + .WithEnvironment(environmentVariables.ToArray()) + .UseImage(imageName, forceLatestImage).ExposePort(DockerHelper.VoucherManagementDockerPort) + .UseNetwork(networkServices.ToArray()).Mount(hostFolder, "/home", MountType.ReadWrite); + + if (String.IsNullOrEmpty(hostFolder) == false) + { + voucherManagementContainer = voucherManagementContainer.Mount(hostFolder, "/home/txnproc/trace", MountType.ReadWrite); + } + + if (dockerCredentials.HasValue) + { + voucherManagementContainer.WithCredential(dockerCredentials.Value.URL, dockerCredentials.Value.UserName, dockerCredentials.Value.Password); + } + + // Now build and return the container + IContainerService builtContainer = voucherManagementContainer.Build().Start().WaitForPort($"{DockerHelper.VoucherManagementDockerPort}/tcp", 30000); + + logger.LogInformation("Voucher Management Container Started"); + + return builtContainer; + } public static IContainerService SetupTestHostContainer(String containerName, ILogger logger, String imageName, List networkServices, @@ -228,6 +282,7 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.SubscriptionServiceContainerName = $"subscription{testGuid:N}"; this.TransactionProcessorContainerName = $"txnprocessor{testGuid:N}"; this.TestHostContainerName = $"testhosts{testGuid:N}"; + this.VoucherManagementContainerName = $"vouchermanagement{testGuid:N}"; (String, String, String) dockerCredentials = ("https://www.docker.com", "stuartferguson", "Sc0tland"); @@ -257,6 +312,25 @@ public override async Task StartContainersForScenarioRun(String scenarioName) dockerCredentials, true); + IContainerService voucherManagementContainer = SetupVoucherManagementContainer(this.VoucherManagementContainerName, + this.Logger, + "stuartferguson/vouchermanagement", + new List + { + testNetwork + }, + traceFolder, + dockerCredentials, + this.SecurityServiceContainerName, + this.EstateManagementContainerName, + this.EventStoreContainerName, + ("serviceClient", "Secret1"), + true); + + List additionalVariables = new List() + { + $"AppSettings:VoucherManagementApi=http://{this.VoucherManagementContainerName}:{DockerHelper.VoucherManagementDockerPort}" + }; IContainerService transactionProcessorContainer = DockerHelper.SetupTransactionProcessorContainer(this.TransactionProcessorContainerName, this.Logger, "transactionprocessor", @@ -270,7 +344,8 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.EstateManagementContainerName, this.EventStoreContainerName, ("serviceClient", "Secret1"), - this.TestHostContainerName); + this.TestHostContainerName, + additionalEnvironmentVariables:additionalVariables); IContainerService estateReportingContainer = DockerHelper.SetupEstateReportingContainer(this.EstateReportingContainerName, this.Logger, @@ -307,7 +382,8 @@ public override async Task StartContainersForScenarioRun(String scenarioName) securityServiceContainer, transactionProcessorContainer, estateReportingContainer, - testhostContainer + testhostContainer, + voucherManagementContainer }); // Cache the ports diff --git a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature index 3c60551a..9841de8a 100644 --- a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature +++ b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature @@ -4,13 +4,14 @@ Feature: SaleTransaction Background: Given the following api resources exist - | ResourceName | DisplayName | Secret | Scopes | UserClaims | - | estateManagement | Estate Managememt REST | Secret1 | estateManagement | MerchantId, EstateId, role | - | transactionProcessor | Transaction Processor REST | Secret1 | transactionProcessor | | + | ResourceName | DisplayName | Secret | Scopes | UserClaims | + | estateManagement | Estate Managememt REST | Secret1 | estateManagement | MerchantId, EstateId, role | + | transactionProcessor | Transaction Processor REST | Secret1 | transactionProcessor | | + | voucherManagement | Voucher Management REST | Secret1 | voucherManagement | | Given the following clients exist | ClientId | ClientName | Secret | AllowedScopes | AllowedGrantTypes | - | serviceClient | Service Client | Secret1 | estateManagement,transactionProcessor | client_credentials | + | serviceClient | Service Client | Secret1 | estateManagement,transactionProcessor,voucherManagement | client_credentials | Given I have a token to access the estate management and transaction processor resources | ClientId | @@ -24,17 +25,23 @@ Background: Given I have created the following operators | EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber | | Test Estate 1 | Safaricom | True | True | + | Test Estate 1 | Voucher | True | True | | Test Estate 2 | Safaricom | True | True | + | Test Estate 2 | Voucher | True | True | Given I create a contract with the following values - | EstateName | OperatorName | ContractDescription | - | Test Estate 1 | Safaricom | Safaricom Contract | - | Test Estate 2 | Safaricom | Safaricom Contract | + | EstateName | OperatorName | ContractDescription | + | Test Estate 1 | Safaricom | Safaricom Contract | + | Test Estate 1 | Voucher | Hospital 1 Contract | + | Test Estate 2 | Safaricom | Safaricom Contract | + | Test Estate 2 | Voucher | Hospital 1 Contract | When I create the following Products - | EstateName | OperatorName | ContractDescription | ProductName | DisplayText | Value | - | Test Estate 1 | Safaricom | Safaricom Contract | Variable Topup | Custom | | - | Test Estate 2 | Safaricom | Safaricom Contract | Variable Topup | Custom | | + | EstateName | OperatorName | ContractDescription | ProductName | DisplayText | Value | + | Test Estate 1 | Safaricom | Safaricom Contract | Variable Topup | Custom | | + | Test Estate 1 | Voucher | Hospital 1 Contract | 10 KES | 10 KES | | + | Test Estate 2 | Safaricom | Safaricom Contract | Variable Topup | Custom | | + | Test Estate 2 | Voucher | Hospital 1 Contract | 10 KES | 10 KES | | When I add the following Transaction Fees | EstateName | OperatorName | ContractDescription | ProductName | CalculationType | FeeDescription | Value | @@ -50,8 +57,11 @@ Background: Given I have assigned the following operator to the merchants | OperatorName | MerchantName | MerchantNumber | TerminalNumber | EstateName | | Safaricom | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | + | Voucher | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | | Safaricom | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | + | Voucher | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | | Safaricom | Test Merchant 3 | 00000003 | 10000003 | Test Estate 2 | + | Voucher | Test Merchant 3 | 00000003 | 10000003 | Test Estate 2 | Given I have assigned the following devices to the merchants | DeviceIdentifier | MerchantName | EstateName | @@ -61,19 +71,22 @@ Background: Given I make the following manual merchant deposits | Reference | Amount | DateTime | MerchantName | EstateName | - | Deposit1 | 200.00 | Today | Test Merchant 1 | Test Estate 1 | - | Deposit1 | 100.00 | Today | Test Merchant 2 | Test Estate 1 | - | Deposit1 | 100.00 | Today | Test Merchant 3 | Test Estate 2 | + | Deposit1 | 210.00 | Today | Test Merchant 1 | Test Estate 1 | + | Deposit1 | 110.00 | Today | Test Merchant 2 | Test Estate 1 | + | Deposit1 | 110.00 | Today | Test Merchant 3 | Test Estate 2 | @PRTest Scenario: Sale Transactions When I perform the following transactions - | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | - | Today | 1 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | - | Today | 2 | Sale | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | - | Today | 3 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | - | Today | 4 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | CustomerEmailAddress | ContractDescription | ProductName | RecipientEmail | RecipientMobile | + | Today | 1 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | + | Today | 2 | Sale | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | + | Today | 3 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Safaricom | 100.00 | 123456789 | | Safaricom Contract | Variable Topup | | | + | Today | 4 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | testcustomer@customer.co.uk | Safaricom Contract | Variable Topup | | | + | Today | 5 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | + | Today | 6 | Sale | Test Merchant 2 | 123456781 | Test Estate 1 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | | 123456789 | + | Today | 7 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Voucher | 10.00 | | | Hospital 1 Contract | 10 KES | test@recipient.co.uk | | Then transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | @@ -81,6 +94,9 @@ Scenario: Sale Transactions | Test Estate 1 | Test Merchant 2 | 2 | 0000 | SUCCESS | | Test Estate 2 | Test Merchant 3 | 3 | 0000 | SUCCESS | | Test Estate 1 | Test Merchant 1 | 4 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 1 | 5 | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 2 | 6 | 0000 | SUCCESS | + | Test Estate 2 | Test Merchant 3 | 7 | 0000 | SUCCESS | @PRTest Scenario: Sale Transaction with Invalid Device @@ -123,4 +139,4 @@ Scenario: Sale Transaction with Not Enough Credit Available Then transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 1 | 1 | 1009 | Merchant [Test Merchant 1] does not have enough credit available [200.0] to perform transaction amount [300.00] | + | Test Estate 1 | Test Merchant 1 | 1 | 1009 | Merchant [Test Merchant 1] does not have enough credit available [210.0] to perform transaction amount [300.00] | diff --git a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs index 23df45bb..5aa74e27 100644 --- a/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs +++ b/TransactionProcessor.IntegrationTests/SaleTransaction/SaleTransactionFeature.feature.cs @@ -1,822 +1,3 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by SpecFlow (https://www.specflow.org/). -// SpecFlow Version:3.3.0.0 -// SpecFlow Generator Version:3.1.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -namespace TransactionProcessor.IntegrationTests.SaleTransaction -{ - using TechTalk.SpecFlow; - using System; - using System.Linq; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.3.0.0")] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [Xunit.TraitAttribute("Category", "base")] - [Xunit.TraitAttribute("Category", "shared")] - public partial class SaleTransactionFeature : object, Xunit.IClassFixture, System.IDisposable - { - - private static TechTalk.SpecFlow.ITestRunner testRunner; - - private string[] _featureTags = new string[] { - "base", - "shared"}; - - private Xunit.Abstractions.ITestOutputHelper _testOutputHelper; - -#line 1 "SaleTransactionFeature.feature" -#line hidden - - public SaleTransactionFeature(SaleTransactionFeature.FixtureData fixtureData, TransactionProcessor_IntegrationTests_XUnitAssemblyFixture assemblyFixture, Xunit.Abstractions.ITestOutputHelper testOutputHelper) - { - this._testOutputHelper = testOutputHelper; - this.TestInitialize(); - } - - public static void FeatureSetup() - { - testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "SaleTransaction", null, ProgrammingLanguage.CSharp, new string[] { - "base", - "shared"}); - testRunner.OnFeatureStart(featureInfo); - } - - public static void FeatureTearDown() - { - testRunner.OnFeatureEnd(); - testRunner = null; - } - - public virtual void TestInitialize() - { - } - - public virtual void TestTearDown() - { - testRunner.OnScenarioEnd(); - } - - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(_testOutputHelper); - } - - public virtual void ScenarioStart() - { - testRunner.OnScenarioStart(); - } - - public virtual void ScenarioCleanup() - { - testRunner.CollectScenarioErrors(); - } - - public virtual void FeatureBackground() - { -#line 4 -#line hidden - TechTalk.SpecFlow.Table table36 = new TechTalk.SpecFlow.Table(new string[] { - "ResourceName", - "DisplayName", - "Secret", - "Scopes", - "UserClaims"}); - table36.AddRow(new string[] { - "estateManagement", - "Estate Managememt REST", - "Secret1", - "estateManagement", - "MerchantId, EstateId, role"}); - table36.AddRow(new string[] { - "transactionProcessor", - "Transaction Processor REST", - "Secret1", - "transactionProcessor", - ""}); -#line 6 - testRunner.Given("the following api resources exist", ((string)(null)), table36, "Given "); -#line hidden - TechTalk.SpecFlow.Table table37 = new TechTalk.SpecFlow.Table(new string[] { - "ClientId", - "ClientName", - "Secret", - "AllowedScopes", - "AllowedGrantTypes"}); - table37.AddRow(new string[] { - "serviceClient", - "Service Client", - "Secret1", - "estateManagement,transactionProcessor", - "client_credentials"}); -#line 11 - testRunner.Given("the following clients exist", ((string)(null)), table37, "Given "); -#line hidden - TechTalk.SpecFlow.Table table38 = new TechTalk.SpecFlow.Table(new string[] { - "ClientId"}); - table38.AddRow(new string[] { - "serviceClient"}); -#line 15 - testRunner.Given("I have a token to access the estate management and transaction processor resource" + - "s", ((string)(null)), table38, "Given "); -#line hidden - TechTalk.SpecFlow.Table table39 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName"}); - table39.AddRow(new string[] { - "Test Estate 1"}); - table39.AddRow(new string[] { - "Test Estate 2"}); -#line 19 - testRunner.Given("I have created the following estates", ((string)(null)), table39, "Given "); -#line hidden - TechTalk.SpecFlow.Table table40 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "OperatorName", - "RequireCustomMerchantNumber", - "RequireCustomTerminalNumber"}); - table40.AddRow(new string[] { - "Test Estate 1", - "Safaricom", - "True", - "True"}); - table40.AddRow(new string[] { - "Test Estate 2", - "Safaricom", - "True", - "True"}); -#line 24 - testRunner.Given("I have created the following operators", ((string)(null)), table40, "Given "); -#line hidden - TechTalk.SpecFlow.Table table41 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "OperatorName", - "ContractDescription"}); - table41.AddRow(new string[] { - "Test Estate 1", - "Safaricom", - "Safaricom Contract"}); - table41.AddRow(new string[] { - "Test Estate 2", - "Safaricom", - "Safaricom Contract"}); -#line 29 - testRunner.Given("I create a contract with the following values", ((string)(null)), table41, "Given "); -#line hidden - TechTalk.SpecFlow.Table table42 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "OperatorName", - "ContractDescription", - "ProductName", - "DisplayText", - "Value"}); - table42.AddRow(new string[] { - "Test Estate 1", - "Safaricom", - "Safaricom Contract", - "Variable Topup", - "Custom", - ""}); - table42.AddRow(new string[] { - "Test Estate 2", - "Safaricom", - "Safaricom Contract", - "Variable Topup", - "Custom", - ""}); -#line 34 - testRunner.When("I create the following Products", ((string)(null)), table42, "When "); -#line hidden - TechTalk.SpecFlow.Table table43 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "OperatorName", - "ContractDescription", - "ProductName", - "CalculationType", - "FeeDescription", - "Value"}); - table43.AddRow(new string[] { - "Test Estate 1", - "Safaricom", - "Safaricom Contract", - "Variable Topup", - "Fixed", - "Merchant Commission", - "2.50"}); - table43.AddRow(new string[] { - "Test Estate 2", - "Safaricom", - "Safaricom Contract", - "Variable Topup", - "Percentage", - "Merchant Commission", - "0.85"}); -#line 39 - testRunner.When("I add the following Transaction Fees", ((string)(null)), table43, "When "); -#line hidden - TechTalk.SpecFlow.Table table44 = new TechTalk.SpecFlow.Table(new string[] { - "MerchantName", - "AddressLine1", - "Town", - "Region", - "Country", - "ContactName", - "EmailAddress", - "EstateName"}); - table44.AddRow(new string[] { - "Test Merchant 1", - "Address Line 1", - "TestTown", - "Test Region", - "United Kingdom", - "Test Contact 1", - "testcontact1@merchant1.co.uk", - "Test Estate 1"}); - table44.AddRow(new string[] { - "Test Merchant 2", - "Address Line 1", - "TestTown", - "Test Region", - "United Kingdom", - "Test Contact 2", - "testcontact2@merchant2.co.uk", - "Test Estate 1"}); - table44.AddRow(new string[] { - "Test Merchant 3", - "Address Line 1", - "TestTown", - "Test Region", - "United Kingdom", - "Test Contact 3", - "testcontact3@merchant2.co.uk", - "Test Estate 2"}); -#line 44 - testRunner.Given("I create the following merchants", ((string)(null)), table44, "Given "); -#line hidden - TechTalk.SpecFlow.Table table45 = new TechTalk.SpecFlow.Table(new string[] { - "OperatorName", - "MerchantName", - "MerchantNumber", - "TerminalNumber", - "EstateName"}); - table45.AddRow(new string[] { - "Safaricom", - "Test Merchant 1", - "00000001", - "10000001", - "Test Estate 1"}); - table45.AddRow(new string[] { - "Safaricom", - "Test Merchant 2", - "00000002", - "10000002", - "Test Estate 1"}); - table45.AddRow(new string[] { - "Safaricom", - "Test Merchant 3", - "00000003", - "10000003", - "Test Estate 2"}); -#line 50 - testRunner.Given("I have assigned the following operator to the merchants", ((string)(null)), table45, "Given "); -#line hidden - TechTalk.SpecFlow.Table table46 = new TechTalk.SpecFlow.Table(new string[] { - "DeviceIdentifier", - "MerchantName", - "EstateName"}); - table46.AddRow(new string[] { - "123456780", - "Test Merchant 1", - "Test Estate 1"}); - table46.AddRow(new string[] { - "123456781", - "Test Merchant 2", - "Test Estate 1"}); - table46.AddRow(new string[] { - "123456782", - "Test Merchant 3", - "Test Estate 2"}); -#line 56 - testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table46, "Given "); -#line hidden - TechTalk.SpecFlow.Table table47 = new TechTalk.SpecFlow.Table(new string[] { - "Reference", - "Amount", - "DateTime", - "MerchantName", - "EstateName"}); - table47.AddRow(new string[] { - "Deposit1", - "200.00", - "Today", - "Test Merchant 1", - "Test Estate 1"}); - table47.AddRow(new string[] { - "Deposit1", - "100.00", - "Today", - "Test Merchant 2", - "Test Estate 1"}); - table47.AddRow(new string[] { - "Deposit1", - "100.00", - "Today", - "Test Merchant 3", - "Test Estate 2"}); -#line 62 - testRunner.Given("I make the following manual merchant deposits", ((string)(null)), table47, "Given "); -#line hidden - } - - void System.IDisposable.Dispose() - { - this.TestTearDown(); - } - - [Xunit.SkippableFactAttribute(DisplayName="Sale Transactions")] - [Xunit.TraitAttribute("FeatureTitle", "SaleTransaction")] - [Xunit.TraitAttribute("Description", "Sale Transactions")] - [Xunit.TraitAttribute("Category", "PRTest")] - public virtual void SaleTransactions() - { - string[] tagsOfScenario = new string[] { - "PRTest"}; - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transactions", null, tagsOfScenario, argumentsOfScenario); -#line 69 -this.ScenarioInitialize(scenarioInfo); -#line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) - { - testRunner.SkipScenario(); - } - else - { - this.ScenarioStart(); -#line 4 -this.FeatureBackground(); -#line hidden - TechTalk.SpecFlow.Table table48 = new TechTalk.SpecFlow.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table48.AddRow(new string[] { - "Today", - "1", - "Sale", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "", - "Safaricom Contract", - "Variable Topup"}); - table48.AddRow(new string[] { - "Today", - "2", - "Sale", - "Test Merchant 2", - "123456781", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "", - "Safaricom Contract", - "Variable Topup"}); - table48.AddRow(new string[] { - "Today", - "3", - "Sale", - "Test Merchant 3", - "123456782", - "Test Estate 2", - "Safaricom", - "100.00", - "123456789", - "", - "Safaricom Contract", - "Variable Topup"}); - table48.AddRow(new string[] { - "Today", - "4", - "Sale", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 71 - testRunner.When("I perform the following transactions", ((string)(null)), table48, "When "); -#line hidden - TechTalk.SpecFlow.Table table49 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table49.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "1", - "0000", - "SUCCESS"}); - table49.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 2", - "2", - "0000", - "SUCCESS"}); - table49.AddRow(new string[] { - "Test Estate 2", - "Test Merchant 3", - "3", - "0000", - "SUCCESS"}); - table49.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "4", - "0000", - "SUCCESS"}); -#line 78 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table49, "Then "); -#line hidden - } - this.ScenarioCleanup(); - } - - [Xunit.SkippableFactAttribute(DisplayName="Sale Transaction with Invalid Device")] - [Xunit.TraitAttribute("FeatureTitle", "SaleTransaction")] - [Xunit.TraitAttribute("Description", "Sale Transaction with Invalid Device")] - [Xunit.TraitAttribute("Category", "PRTest")] - public virtual void SaleTransactionWithInvalidDevice() - { - string[] tagsOfScenario = new string[] { - "PRTest"}; - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Device", null, tagsOfScenario, argumentsOfScenario); -#line 86 -this.ScenarioInitialize(scenarioInfo); -#line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) - { - testRunner.SkipScenario(); - } - else - { - this.ScenarioStart(); -#line 4 -this.FeatureBackground(); -#line hidden - TechTalk.SpecFlow.Table table50 = new TechTalk.SpecFlow.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table50.AddRow(new string[] { - "Today", - "1", - "Sale", - "Test Merchant 1", - "123456781", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 88 - testRunner.When("I perform the following transactions", ((string)(null)), table50, "When "); -#line hidden - TechTalk.SpecFlow.Table table51 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table51.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "1", - "1000", - "Device Identifier 123456781 not valid for Merchant Test Merchant 1"}); -#line 92 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table51, "Then "); -#line hidden - } - this.ScenarioCleanup(); - } - - [Xunit.SkippableFactAttribute(DisplayName="Sale Transaction with Invalid Estate")] - [Xunit.TraitAttribute("FeatureTitle", "SaleTransaction")] - [Xunit.TraitAttribute("Description", "Sale Transaction with Invalid Estate")] - public virtual void SaleTransactionWithInvalidEstate() - { - string[] tagsOfScenario = ((string[])(null)); - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Estate", null, tagsOfScenario, argumentsOfScenario); -#line 96 -this.ScenarioInitialize(scenarioInfo); -#line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) - { - testRunner.SkipScenario(); - } - else - { - this.ScenarioStart(); -#line 4 -this.FeatureBackground(); -#line hidden - TechTalk.SpecFlow.Table table52 = new TechTalk.SpecFlow.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table52.AddRow(new string[] { - "Today", - "1", - "Sale", - "Test Merchant 1", - "123456780", - "InvalidEstate", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 98 - testRunner.When("I perform the following transactions", ((string)(null)), table52, "When "); -#line hidden - TechTalk.SpecFlow.Table table53 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table53.AddRow(new string[] { - "InvalidEstate", - "Test Merchant 1", - "1", - "1001", - "Estate Id [79902550-64df-4491-b0c1-4e78943928a3] is not a valid estate"}); -#line 102 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table53, "Then "); -#line hidden - } - this.ScenarioCleanup(); - } - - [Xunit.SkippableFactAttribute(DisplayName="Sale Transaction with Invalid Merchant")] - [Xunit.TraitAttribute("FeatureTitle", "SaleTransaction")] - [Xunit.TraitAttribute("Description", "Sale Transaction with Invalid Merchant")] - public virtual void SaleTransactionWithInvalidMerchant() - { - string[] tagsOfScenario = ((string[])(null)); - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Invalid Merchant", null, tagsOfScenario, argumentsOfScenario); -#line 106 -this.ScenarioInitialize(scenarioInfo); -#line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) - { - testRunner.SkipScenario(); - } - else - { - this.ScenarioStart(); -#line 4 -this.FeatureBackground(); -#line hidden - TechTalk.SpecFlow.Table table54 = new TechTalk.SpecFlow.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table54.AddRow(new string[] { - "Today", - "1", - "Sale", - "InvalidMerchant", - "123456780", - "Test Estate 1", - "Safaricom", - "100.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 108 - testRunner.When("I perform the following transactions", ((string)(null)), table54, "When "); -#line hidden - TechTalk.SpecFlow.Table table55 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table55.AddRow(new string[] { - "Test Estate 1", - "InvalidMerchant", - "1", - "1002", - "Merchant Id [d59320fa-4c3e-4900-a999-483f6a10c69a] is not a valid merchant for es" + - "tate [Test Estate 1]"}); -#line 112 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table55, "Then "); -#line hidden - } - this.ScenarioCleanup(); - } - - [Xunit.SkippableFactAttribute(DisplayName="Sale Transaction with Not Enough Credit Available")] - [Xunit.TraitAttribute("FeatureTitle", "SaleTransaction")] - [Xunit.TraitAttribute("Description", "Sale Transaction with Not Enough Credit Available")] - [Xunit.TraitAttribute("Category", "PRTest")] - public virtual void SaleTransactionWithNotEnoughCreditAvailable() - { - string[] tagsOfScenario = new string[] { - "PRTest"}; - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction with Not Enough Credit Available", null, tagsOfScenario, argumentsOfScenario); -#line 117 -this.ScenarioInitialize(scenarioInfo); -#line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) - { - testRunner.SkipScenario(); - } - else - { - this.ScenarioStart(); -#line 4 -this.FeatureBackground(); -#line hidden - TechTalk.SpecFlow.Table table56 = new TechTalk.SpecFlow.Table(new string[] { - "DateTime", - "TransactionNumber", - "TransactionType", - "MerchantName", - "DeviceIdentifier", - "EstateName", - "OperatorName", - "TransactionAmount", - "CustomerAccountNumber", - "CustomerEmailAddress", - "ContractDescription", - "ProductName"}); - table56.AddRow(new string[] { - "Today", - "1", - "Sale", - "Test Merchant 1", - "123456780", - "Test Estate 1", - "Safaricom", - "300.00", - "123456789", - "testcustomer@customer.co.uk", - "Safaricom Contract", - "Variable Topup"}); -#line 119 - testRunner.When("I perform the following transactions", ((string)(null)), table56, "When "); -#line hidden - TechTalk.SpecFlow.Table table57 = new TechTalk.SpecFlow.Table(new string[] { - "EstateName", - "MerchantName", - "TransactionNumber", - "ResponseCode", - "ResponseMessage"}); - table57.AddRow(new string[] { - "Test Estate 1", - "Test Merchant 1", - "1", - "1009", - "Merchant [Test Merchant 1] does not have enough credit available [200.0] to perfo" + - "rm transaction amount [300.00]"}); -#line 124 - testRunner.Then("transaction response should contain the following information", ((string)(null)), table57, "Then "); -#line hidden - } - this.ScenarioCleanup(); - } - - [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.3.0.0")] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class FixtureData : System.IDisposable - { - - public FixtureData() - { - SaleTransactionFeature.FeatureSetup(); - } - - void System.IDisposable.Dispose() - { - SaleTransactionFeature.FeatureTearDown(); - } - } - } -} -#pragma warning restore -#endregion +#error Could not find a reference to SpecFlow in project 'TransactionProcessor.IntegrationTests'. +#error Please add the 'TechTalk.SpecFlow' package to the project and use MSBuild generation instead of using SpecFlowSingleFileGenerator. +#error For more information see https://specflow.org/documentation/Generate-Tests-from-MsBuild/ \ No newline at end of file diff --git a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs index c48ec39b..8d6d90e6 100644 --- a/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessor.IntegrationTests/Shared/SharedSteps.cs @@ -15,6 +15,7 @@ namespace TransactionProcessor.IntegrationTests.Shared using EstateManagement.DataTransferObjects.Requests; using EstateManagement.DataTransferObjects.Responses; using Microsoft.EntityFrameworkCore.ValueGeneration.Internal; + using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SecurityService.DataTransferObjects; @@ -264,13 +265,15 @@ public async Task WhenIPerformTheFollowingTransactions(Table table) Guid contractId = Guid.Empty; Guid productId = Guid.Empty; - var contract = estateDetails.GetContract(contractDescription); + Contract contract = estateDetails.GetContract(contractDescription); if (contract != null) { contractId = contract.ContractId; - var product = contract.GetProduct(productName); + Product product = contract.GetProduct(productName); productId = product.ProductId; } + String recipientEmail = SpecflowTableHelper.GetStringRowValue(tableRow, "RecipientEmail"); + String recipientMobile = SpecflowTableHelper.GetStringRowValue(tableRow, "RecipientMobile"); transactionResponse = await this.PerformSaleTransaction(estateDetails.EstateId, merchantId, @@ -284,6 +287,8 @@ public async Task WhenIPerformTheFollowingTransactions(Table table) customerEmailAddress, contractId, productId, + recipientEmail, + recipientMobile, CancellationToken.None); break; @@ -451,6 +456,8 @@ await this.TestingContext.DockerHelper.TransactionProcessorClient.PerformTransac private async Task PerformSaleTransaction(Guid estateId, Guid merchantId, DateTime transactionDateTime, String transactionType, String transactionNumber, String deviceIdentifier, String operatorIdentifier, Decimal transactionAmount, String customerAccountNumber, String customerEmailAddress, Guid contractId, Guid productId, + String recipientEmail, + String recipientMobile, CancellationToken cancellationToken) { SaleTransactionRequest saleTransactionRequest = new SaleTransactionRequest @@ -462,16 +469,37 @@ private async Task PerformSaleTransaction(Guid estateId, Guid DeviceIdentifier = deviceIdentifier, TransactionType = transactionType, OperatorIdentifier = operatorIdentifier, - AdditionalTransactionMetadata = new Dictionary - { - {"Amount", transactionAmount.ToString()}, - {"CustomerAccountNumber", customerAccountNumber} - }, CustomerEmailAddress = customerEmailAddress, ProductId = productId, ContractId = contractId }; + if (operatorIdentifier == "Voucher") + { + saleTransactionRequest.AdditionalTransactionMetadata = new Dictionary + { + {"Amount", transactionAmount.ToString()}, + }; + + if (String.IsNullOrEmpty(recipientEmail) == false) + { + saleTransactionRequest.AdditionalTransactionMetadata.Add("RecipientEmail", recipientEmail); + } + + if (String.IsNullOrEmpty(recipientMobile) == false) + { + saleTransactionRequest.AdditionalTransactionMetadata.Add("RecipientMobile", recipientMobile); + } + } + else + { + saleTransactionRequest.AdditionalTransactionMetadata = new Dictionary + { + {"Amount", transactionAmount.ToString()}, + {"CustomerAccountNumber", customerAccountNumber} + }; + } + SerialisedMessage serialisedMessage = new SerialisedMessage(); serialisedMessage.Metadata.Add(MetadataContants.KeyNameEstateId, estateId.ToString()); serialisedMessage.Metadata.Add(MetadataContants.KeyNameMerchantId, merchantId.ToString()); diff --git a/TransactionProcessor.Models/Transaction.cs b/TransactionProcessor.Models/Transaction.cs index fb7eb43a..5bb2360e 100644 --- a/TransactionProcessor.Models/Transaction.cs +++ b/TransactionProcessor.Models/Transaction.cs @@ -1,6 +1,7 @@ namespace TransactionProcessor.Models { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; /// @@ -11,6 +12,30 @@ public class Transaction { #region Properties + /// + /// Gets or sets the additional request metadata. + /// + /// + /// The additional request metadata. + /// + public Dictionary AdditionalRequestMetadata { get; set; } + + /// + /// Gets or sets the additional response metadata. + /// + /// + /// The additional response metadata. + /// + public Dictionary AdditionalResponseMetadata { get; set; } + + /// + /// Gets or sets the response code. + /// + /// + /// The response code. + /// + public String ResponseCode { get; set; } + /// /// Gets or sets the authorisation code. /// diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 0ca34494..bbbe51d1 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Reflection.Metadata; using BusinessLogic.OperatorInterfaces; using BusinessLogic.OperatorInterfaces.SafaricomPinless; using BusinessLogic.Requests; @@ -13,6 +14,7 @@ using SecurityService.DataTransferObjects.Responses; using Transaction.DomainEvents; using TransactionAggregate; + using VoucherManagement.DataTransferObjects; using CalculationType = Models.CalculationType; using FeeType = Models.FeeType; @@ -146,6 +148,14 @@ public class TestData {"CustomerAccountNumber", "123456789" } }; + public static Dictionary AdditionalTransactionMetaDataForVoucher => + new Dictionary + { + {"Amount", "100.00"}, + {"RecipientEmail", "test@testvoucher.co.uk" } + + }; + public static IReadOnlyDictionary DefaultAppSettings => new Dictionary { @@ -694,6 +704,22 @@ public static TokenResponse TokenResponse() public static Decimal ReconciliationTransactionValue = 100; + public static IssueVoucherResponse IssueVoucherResponse => + new IssueVoucherResponse + { + ExpiryDate = TestData.VoucherExpiryDate, + Message = TestData.VoucherMessage, + VoucherCode = TestData.VoucherCode, + VoucherId = TestData.VoucherId + }; + + public static DateTime VoucherExpiryDate = new DateTime(2021,1,8); + public static String VoucherMessage = String.Empty; + + public static String VoucherCode = "ABCDE1234"; + public static Guid VoucherId = Guid.Parse("ED744C18-1F45-47E7-A9FC-7AAC1D9E9D8A"); + + public static List ContractProductTransactionFees => new List { diff --git a/TransactionProcessor.TransactionAgrgegate/TransactionAggregate.cs b/TransactionProcessor.TransactionAgrgegate/TransactionAggregate.cs index 7f529c42..7fe86346 100644 --- a/TransactionProcessor.TransactionAgrgegate/TransactionAggregate.cs +++ b/TransactionProcessor.TransactionAgrgegate/TransactionAggregate.cs @@ -994,7 +994,10 @@ public Transaction GetTransaction() TransactionDateTime = this.TransactionDateTime, TransactionNumber = this.TransactionNumber, TransactionReference = this.TransactionReference, - OperatorIdentifier = this.OperatorIdentifier + OperatorIdentifier = this.OperatorIdentifier, + AdditionalRequestMetadata = this.AdditionalTransactionRequestMetadata, + AdditionalResponseMetadata = this.AdditionalTransactionResponseMetadata, + ResponseCode = this.ResponseCode }; } } diff --git a/TransactionProcessor/Startup.cs b/TransactionProcessor/Startup.cs index 27227410..32864930 100644 --- a/TransactionProcessor/Startup.cs +++ b/TransactionProcessor/Startup.cs @@ -21,6 +21,7 @@ namespace TransactionProcessor using BusinessLogic.Manager; using BusinessLogic.OperatorInterfaces; using BusinessLogic.OperatorInterfaces.SafaricomPinless; + using BusinessLogic.OperatorInterfaces.VoucherManagement; using BusinessLogic.RequestHandlers; using BusinessLogic.Requests; using BusinessLogic.Services; @@ -52,6 +53,7 @@ namespace TransactionProcessor using Shared.Repositories; using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.SwaggerGen; + using VoucherManagement.Client; using ILogger = Microsoft.Extensions.Logging.ILogger; /// @@ -160,6 +162,8 @@ public void ConfigureServices(IServiceCollection services) }); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + // request & notification handlers services.AddTransient(context => { @@ -172,9 +176,19 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient>(context => (operatorIdentifier) => { - SafaricomConfiguration configuration = context.GetRequiredService(); - HttpClient client = context.GetRequiredService(); - return new SafaricomPinlessProxy(configuration, client); + if (String.Compare(operatorIdentifier, "Safaricom", StringComparison.CurrentCultureIgnoreCase) == 0) + { + SafaricomConfiguration configuration = context.GetRequiredService(); + HttpClient client = context.GetRequiredService(); + return new SafaricomPinlessProxy(configuration, client); + } + else + { + // Voucher + IVoucherManagementClient voucherManagementClient = context.GetRequiredService(); + return new VoucherManagementProxy(voucherManagementClient); + + } }); Dictionary eventHandlersConfiguration = new Dictionary(); diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index e70e7bfa..2fbb1942 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -24,6 +24,7 @@ "SecurityService": "http://192.168.1.133:5001", "EstateManagementApi": "http://192.168.1.133:5000", "MessagingServiceApi": "http://192.168.1.133:5006", + "VoucherManagementApi": "http://192.168.1.133:5007", "ClientId": "serviceClient", "ClientSecret": "d192cbc46d834d0da90e8a9d50ded543", "EventHandlerConfiguration": {