diff --git a/TransactionProcessorACL.BusinessLogic/RequestHandlers/ProcessLogonTransactionRequestHandler.cs b/TransactionProcessorACL.BusinessLogic/RequestHandlers/ProcessLogonTransactionRequestHandler.cs index 24ccf5f..f1fb984 100644 --- a/TransactionProcessorACL.BusinessLogic/RequestHandlers/ProcessLogonTransactionRequestHandler.cs +++ b/TransactionProcessorACL.BusinessLogic/RequestHandlers/ProcessLogonTransactionRequestHandler.cs @@ -10,16 +10,32 @@ /// /// /// + /// /// public class ProcessLogonTransactionRequestHandler : IRequestHandler { + #region Fields + + /// + /// The application service + /// private readonly ITransactionProcessorACLApplicationService ApplicationService; + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The application service. public ProcessLogonTransactionRequestHandler(ITransactionProcessorACLApplicationService applicationService) { this.ApplicationService = applicationService; } + #endregion + #region Methods /// @@ -27,7 +43,9 @@ public ProcessLogonTransactionRequestHandler(ITransactionProcessorACLApplication /// /// The request. /// The cancellation token. - /// + /// + /// Response from the request + /// public async Task Handle(ProcessLogonTransactionRequest request, CancellationToken cancellationToken) { @@ -41,4 +59,55 @@ public async Task Handle(ProcessLogonTransactio #endregion } + + public class ProcessSaleTransactionRequestHandler : IRequestHandler + { + #region Fields + + /// + /// The application service + /// + private readonly ITransactionProcessorACLApplicationService ApplicationService; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The application service. + public ProcessSaleTransactionRequestHandler(ITransactionProcessorACLApplicationService applicationService) + { + this.ApplicationService = applicationService; + } + + #endregion + + #region Methods + + /// + /// Handles the specified request. + /// + /// The request. + /// The cancellation token. + /// + /// Response from the request + /// + public async Task Handle(ProcessSaleTransactionRequest request, + CancellationToken cancellationToken) + { + return await this.ApplicationService.ProcessSaleTransaction(request.EstateId, + request.MerchantId, + request.TransactionDateTime, + request.TransactionNumber, + request.DeviceIdentifier, + request.OperatorIdentifier, + request.Amount, + request.CustomerAccountNumber, + cancellationToken); + } + + #endregion + } } \ No newline at end of file diff --git a/TransactionProcessorACL.BusinessLogic/Requests/ProcessSaleTransactionRequest.cs b/TransactionProcessorACL.BusinessLogic/Requests/ProcessSaleTransactionRequest.cs new file mode 100644 index 0000000..bf508eb --- /dev/null +++ b/TransactionProcessorACL.BusinessLogic/Requests/ProcessSaleTransactionRequest.cs @@ -0,0 +1,151 @@ +namespace TransactionProcessorACL.BusinessLogic.Requests +{ + using System; + using MediatR; + using Models; + + public class ProcessSaleTransactionRequest : IRequest + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The estate identifier. + /// The merchant identifier. + /// The transaction date time. + /// The transaction number. + /// The device identifier. + /// The operator identifier. + /// The amount. + /// The customer account number. + private ProcessSaleTransactionRequest(Guid estateId, + Guid merchantId, + DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String operatorIdentifier, + Decimal amount, + String customerAccountNumber) + { + this.EstateId = estateId; + this.MerchantId = merchantId; + this.DeviceIdentifier = deviceIdentifier; + this.OperatorIdentifier = operatorIdentifier; + this.Amount = amount; + this.CustomerAccountNumber = customerAccountNumber; + this.TransactionDateTime = transactionDateTime; + this.TransactionNumber = transactionNumber; + } + + public ProcessSaleTransactionRequest() + { + // This constructor is only required for IoC tests :| + } + + #endregion + + #region Properties + + /// + /// Gets the amount. + /// + /// + /// The amount. + /// + public Decimal Amount { get; } + + /// + /// Gets the customer account number. + /// + /// + /// The customer account number. + /// + public String CustomerAccountNumber { get; } + + /// + /// Gets the device identifier. + /// + /// + /// The device identifier. + /// + public String DeviceIdentifier { get; } + + /// + /// Gets the estate identifier. + /// + /// + /// The estate identifier. + /// + public Guid EstateId { get; } + + /// + /// Gets the merchant identifier. + /// + /// + /// The merchant identifier. + /// + public Guid MerchantId { get; } + + /// + /// Gets the operator identifier. + /// + /// + /// The operator identifier. + /// + public String OperatorIdentifier { get; } + + /// + /// Gets the transaction date time. + /// + /// + /// The transaction date time. + /// + public DateTime TransactionDateTime { get; } + + /// + /// Gets the transaction number. + /// + /// + /// The transaction number. + /// + public String TransactionNumber { get; } + + #endregion + + #region Methods + + /// + /// Creates the specified estate identifier. + /// + /// The estate identifier. + /// The merchant identifier. + /// The transaction date time. + /// The transaction number. + /// The device identifier. + /// The operator identifier. + /// The amount. + /// The customer account number. + /// + public static ProcessSaleTransactionRequest Create(Guid estateId, + Guid merchantId, + DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String operatorIdentifier, + Decimal amount, + String customerAccountNumber) + { + return new ProcessSaleTransactionRequest(estateId, + merchantId, + transactionDateTime, + transactionNumber, + deviceIdentifier, + operatorIdentifier, + amount, + customerAccountNumber); + } + + #endregion + } +} \ No newline at end of file diff --git a/TransactionProcessorACL.BusinessLogic/Services/ITransactionProcessorACLApplicationService.cs b/TransactionProcessorACL.BusinessLogic/Services/ITransactionProcessorACLApplicationService.cs index 5e9a337..ba56336 100644 --- a/TransactionProcessorACL.BusinessLogic/Services/ITransactionProcessorACLApplicationService.cs +++ b/TransactionProcessorACL.BusinessLogic/Services/ITransactionProcessorACLApplicationService.cs @@ -27,5 +27,28 @@ Task ProcessLogonTransaction(Guid estateId, String transactionNumber, String deviceIdentifier, CancellationToken cancellationToken); + + /// + /// Processes the sale transaction. + /// + /// The estate identifier. + /// The merchant identifier. + /// The transaction date time. + /// The transaction number. + /// The device identifier. + /// The operator identifier. + /// The amount. + /// The customer account number. + /// The cancellation token. + /// + Task ProcessSaleTransaction(Guid estateId, + Guid merchantId, + DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String operatorIdentifier, + Decimal amount, + String customerAccountNumber, + CancellationToken cancellationToken); } } diff --git a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs index 9a0e607..5650715 100644 --- a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs +++ b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs @@ -1,6 +1,7 @@ namespace TransactionProcessorACL.BusinessLogic.Services { using System; + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Models; @@ -118,6 +119,87 @@ public async Task ProcessLogonTransaction(Guid return response; } + /// + /// Processes the sale transaction. + /// + /// The estate identifier. + /// The merchant identifier. + /// The transaction date time. + /// The transaction number. + /// The device identifier. + /// The operator identifier. + /// The amount. + /// The customer account number. + /// The cancellation token. + /// + public async Task ProcessSaleTransaction(Guid estateId, + Guid merchantId, + DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String operatorIdentifier, + Decimal amount, + String customerAccountNumber, + CancellationToken cancellationToken) + { + // Get a client token to call the Transaction Processor + String clientId = ConfigurationReader.GetValue("AppSettings", "ClientId"); + String clientSecret = ConfigurationReader.GetValue("AppSettings", "ClientSecret"); + + TokenResponse accessToken = await this.SecurityServiceClient.GetToken(clientId, clientSecret, cancellationToken); + + SaleTransactionRequest saleTransactionRequest = new SaleTransactionRequest(); + saleTransactionRequest.TransactionNumber = transactionNumber; + saleTransactionRequest.DeviceIdentifier = deviceIdentifier; + saleTransactionRequest.TransactionDateTime = transactionDateTime; + saleTransactionRequest.TransactionType = "SALE"; + saleTransactionRequest.OperatorIdentifier = operatorIdentifier; + + // Build up the metadata + saleTransactionRequest.AdditionalTransactionMetadata = new Dictionary(); + saleTransactionRequest.AdditionalTransactionMetadata.Add("Amount", amount.ToString()); + saleTransactionRequest.AdditionalTransactionMetadata.Add("CustomerAccountNumber", customerAccountNumber); + + SerialisedMessage requestSerialisedMessage = new SerialisedMessage(); + requestSerialisedMessage.Metadata.Add("EstateId", estateId.ToString()); + requestSerialisedMessage.Metadata.Add("MerchantId", merchantId.ToString()); + requestSerialisedMessage.SerialisedData = JsonConvert.SerializeObject(saleTransactionRequest, + new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + ProcessSaleTransactionResponse response = null; + + try + { + SerialisedMessage responseSerialisedMessage = + await this.TransactionProcessorClient.PerformTransaction(accessToken.AccessToken, requestSerialisedMessage, cancellationToken); + + SaleTransactionResponse saleTransactionResponse = JsonConvert.DeserializeObject(responseSerialisedMessage.SerialisedData); + + response = new ProcessSaleTransactionResponse + { + ResponseCode = saleTransactionResponse.ResponseCode, + ResponseMessage = saleTransactionResponse.ResponseMessage + }; + } + catch (Exception ex) + { + if (ex.InnerException is InvalidOperationException) + { + // This means there is an error in the request + response = new ProcessSaleTransactionResponse + { + ResponseCode = "0001", // Request Message error + ResponseMessage = ex.InnerException.Message + }; + } + } + + return response; + } + #endregion } } \ No newline at end of file diff --git a/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj b/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj index 6acc0cd..f166f1a 100644 --- a/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj +++ b/TransactionProcessorACL.BusinessLogic/TransactionProcessorACL.BusinessLogic.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs index af4183e..4c1351b 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/SaleTransactionRequestMessage.cs @@ -20,6 +20,22 @@ public class SaleTransactionRequestMessage : TransactionRequestMessage /// public Decimal Amount { get; set; } + /// + /// Gets or sets the customer account number. + /// + /// + /// The customer account number. + /// + public String CustomerAccountNumber { get; set; } + + /// + /// Gets or sets the operator identifier. + /// + /// + /// The operator identifier. + /// + public String OperatorIdentifier { get; set; } + #endregion } } \ No newline at end of file diff --git a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs index 6a69897..aba935c 100644 --- a/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs +++ b/TransactionProcessorACL.DataTransferObjects/Responses/TransactionResponseMessage.cs @@ -31,4 +31,10 @@ public class LogonTransactionResponseMessage : TransactionResponseMessage { } + + [ExcludeFromCodeCoverage] + public class SaleTransactionResponseMessage : TransactionResponseMessage + { + + } } diff --git a/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs b/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs index 9b43acd..61f8813 100644 --- a/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs +++ b/TransactionProcessorACL.IntegrationTests/Common/DockerHelper.cs @@ -8,7 +8,9 @@ using System.Threading; using System.Threading.Tasks; using Client; + using Ductus.FluentDocker.Builders; using Ductus.FluentDocker.Common; + using Ductus.FluentDocker.Model.Builders; using Ductus.FluentDocker.Services; using Ductus.FluentDocker.Services.Extensions; using EstateManagement.Client; @@ -91,8 +93,38 @@ public class DockerHelper : global::Shared.IntegrationTesting.DockerHelper protected String SubscriptionServiceContainerName; protected String TransactionProcessorContainerName; + protected String TransactionProcessorACLContainerName; + protected String TestHostContainerName; + + public const Int32 TestHostPort = 9000; + + public static IContainerService SetupTestHostContainer(String containerName, ILogger logger, String imageName, + List networkServices, + String hostFolder, + (String URL, String UserName, String Password)? dockerCredentials, + Boolean forceLatestImage = false) + { + logger.LogInformation("About to Start Test Hosts Container"); + + ContainerBuilder testHostContainer = new Builder().UseContainer().WithName(containerName) + .UseImage(imageName, forceLatestImage).ExposePort(DockerHelper.TestHostPort) + .UseNetwork(networkServices.ToArray()).Mount(hostFolder, "/home", MountType.ReadWrite); + + if (dockerCredentials.HasValue) + { + testHostContainer.WithCredential(dockerCredentials.Value.URL, dockerCredentials.Value.UserName, dockerCredentials.Value.Password); + } + + // Now build and return the container + IContainerService builtContainer = testHostContainer.Build().Start().WaitForPort($"{DockerHelper.TestHostPort}/tcp", 30000); + + logger.LogInformation("Test Hosts Container Started"); + + return builtContainer; + } + /// /// The transaction processor port /// @@ -148,6 +180,7 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.SubscriptionServiceContainerName = $"subscription{testGuid:N}"; this.TransactionProcessorContainerName = $"txnprocessor{testGuid:N}"; this.TransactionProcessorACLContainerName = $"txnprocessoracl{testGuid:N}"; + this.TestHostContainerName = $"testhosts{testGuid:N}"; (String, String, String) dockerCredentials = ("https://www.docker.com", "stuartferguson", "Sc0tland"); @@ -191,6 +224,7 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.EstateManagementContainerName, this.EventStoreContainerName, ("serviceClient", "Secret1"), + this.TestHostContainerName, true); IContainerService estateReportingContainer = DockerHelper.SetupEstateReportingContainer(this.EstateReportingContainerName, @@ -220,6 +254,17 @@ public override async Task StartContainersForScenarioRun(String scenarioName) this.TransactionProcessorContainerName, ("serviceClient", "Secret1")); + IContainerService testhostContainer = SetupTestHostContainer(this.TestHostContainerName, + this.Logger, + "stuartferguson/testhosts", + new List + { + testNetwork + }, + traceFolder, + dockerCredentials, + true); + this.Containers.AddRange(new List { eventStoreContainer, @@ -227,7 +272,8 @@ public override async Task StartContainersForScenarioRun(String scenarioName) securityServiceContainer, transactionProcessorContainer, transactionProcessorACLContainer, - estateReportingContainer + estateReportingContainer, + testhostContainer }); // Cache the ports diff --git a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs index b2b5500..058ec79 100644 --- a/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs +++ b/TransactionProcessorACL.IntegrationTests/LogonTransaction/LogonTransaction.feature.cs @@ -21,7 +21,7 @@ namespace TransactionProcessorACL.IntegrationTests.LogonTransaction [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [Xunit.TraitAttribute("Category", "base")] [Xunit.TraitAttribute("Category", "shared")] - public partial class LogonTransactionFeature : Xunit.IClassFixture, System.IDisposable + public partial class LogonTransactionFeature : object, Xunit.IClassFixture, System.IDisposable { private static TechTalk.SpecFlow.ITestRunner testRunner; diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature new file mode 100644 index 0000000..334d882 --- /dev/null +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature @@ -0,0 +1,80 @@ +@base @shared +Feature: SalesTransaction + +Background: + + Given the following security roles exist + | RoleName | + | Merchant | + + 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 | | + | transactionProcessorAcl | Transaction Processor ACL REST | Secret1 | transactionProcessorAcl | MerchantId, EstateId, role | + + Given the following clients exist + | ClientId | ClientName | Secret | AllowedScopes | AllowedGrantTypes | + | serviceClient | Service Client | Secret1 | estateManagement,transactionProcessor,transactionProcessorAcl | client_credentials | + | merchantClient | Merchant Client | Secret1 | transactionProcessorAcl | password | + + Given I have a token to access the estate management and transaction processor acl resources + | ClientId | + | serviceClient | + + Given I have created the following estates + | EstateName | + | Test Estate 1 | + | Test Estate 2 | + + Given I have created the following operators + | EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber | + | Test Estate 1 | Safaricom | True | True | + | Test Estate 2 | Safaricom | True | True | + + Given I create the following merchants + | MerchantName | AddressLine1 | Town | Region | Country | ContactName | EmailAddress | EstateName | + | Test Merchant 1 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 1 | testcontact1@merchant1.co.uk | Test Estate 1 | + | Test Merchant 2 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 2 | testcontact2@merchant2.co.uk | Test Estate 1 | + | Test Merchant 3 | Address Line 1 | TestTown | Test Region | United Kingdom | Test Contact 3 | testcontact3@merchant2.co.uk | Test Estate 2 | + + Given I have created the following security users + | EmailAddress | Password | GivenName | FamilyName | EstateName | MerchantName | + | merchantuser@testmerchant1.co.uk | 123456 | TestMerchant | User1 | Test Estate 1 | Test Merchant 1 | + | merchantuser@testmerchant2.co.uk | 123456 | TestMerchant | User2 | Test Estate 1 | Test Merchant 2 | + | merchantuser@testmerchant3.co.uk | 123456 | TestMerchant | User3 | Test Estate 2 | Test Merchant 3 | + + Given I have assigned the following operator to the merchants + | OperatorName | MerchantName | MerchantNumber | TerminalNumber | EstateName | + | Safaricom | Test Merchant 1 | 00000001 | 10000001 | Test Estate 1 | + | Safaricom | Test Merchant 2 | 00000002 | 10000002 | Test Estate 1 | + | Safaricom | Test Merchant 3 | 00000003 | 10000003 | Test Estate 2 | + + Given I have assigned the following devices to the merchants + | DeviceIdentifier | MerchantName | EstateName | + | 123456780 | Test Merchant 1 | Test Estate 1 | + | 123456781 | Test Merchant 2 | Test Estate 1 | + | 123456782 | Test Merchant 3 | Test Estate 2 | + +@PRTest +Scenario: Sale Transaction + Given I am logged in as "merchantuser@testmerchant1.co.uk" with password "123456" for Merchant "Test Merchant 1" for Estate "Test Estate 1" with client "merchantClient" + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | + | Today | 1 | Sale | Test Merchant 1 | 123456780 | Test Estate 1 | Safaricom | 100.00 | 123456789 | + + Given I am logged in as "merchantuser@testmerchant2.co.uk" with password "123456" for Merchant "Test Merchant 2" for Estate "Test Estate 1" with client "merchantClient" + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | + | Today | 2 | Sale | Test Merchant 2 | 123456781 | Test Estate 1 | Safaricom | 100.00 | 123456789 | + + Given I am logged in as "merchantuser@testmerchant3.co.uk" with password "123456" for Merchant "Test Merchant 3" for Estate "Test Estate 2" with client "merchantClient" + When I perform the following transactions + | DateTime | TransactionNumber | TransactionType | MerchantName | DeviceIdentifier | EstateName | OperatorName | TransactionAmount | CustomerAccountNumber | + | Today | 3 | Sale | Test Merchant 3 | 123456782 | Test Estate 2 | Safaricom | 100.00 | 123456789 | + + Then transaction response should contain the following information + | EstateName | MerchantName | TransactionNumber | TransactionType | ResponseCode | ResponseMessage | + | Test Estate 1 | Test Merchant 1 | 1 | Sale | 0000 | SUCCESS | + | Test Estate 1 | Test Merchant 2 | 2 | Sale | 0000 | SUCCESS | + | Test Estate 2 | Test Merchant 3 | 3 | Sale | 0000 | SUCCESS | diff --git a/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs new file mode 100644 index 0000000..5db1a29 --- /dev/null +++ b/TransactionProcessorACL.IntegrationTests/SaleTransaction/SalesTransaction.feature.cs @@ -0,0 +1,471 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (http://www.specflow.org/). +// SpecFlow Version:3.1.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 TransactionProcessorACL.IntegrationTests.SaleTransaction +{ + using TechTalk.SpecFlow; + using System; + using System.Linq; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [Xunit.TraitAttribute("Category", "base")] + [Xunit.TraitAttribute("Category", "shared")] + public partial class SalesTransactionFeature : 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 "SalesTransaction.feature" +#line hidden + + public SalesTransactionFeature(SalesTransactionFeature.FixtureData fixtureData, TransactionProcessorACL_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"), "SalesTransaction", 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 table14 = new TechTalk.SpecFlow.Table(new string[] { + "RoleName"}); + table14.AddRow(new string[] { + "Merchant"}); +#line 6 + testRunner.Given("the following security roles exist", ((string)(null)), table14, "Given "); +#line hidden + TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] { + "ResourceName", + "DisplayName", + "Secret", + "Scopes", + "UserClaims"}); + table15.AddRow(new string[] { + "estateManagement", + "Estate Managememt REST", + "Secret1", + "estateManagement", + "MerchantId, EstateId, role"}); + table15.AddRow(new string[] { + "transactionProcessor", + "Transaction Processor REST", + "Secret1", + "transactionProcessor", + ""}); + table15.AddRow(new string[] { + "transactionProcessorAcl", + "Transaction Processor ACL REST", + "Secret1", + "transactionProcessorAcl", + "MerchantId, EstateId, role"}); +#line 10 + testRunner.Given("the following api resources exist", ((string)(null)), table15, "Given "); +#line hidden + TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] { + "ClientId", + "ClientName", + "Secret", + "AllowedScopes", + "AllowedGrantTypes"}); + table16.AddRow(new string[] { + "serviceClient", + "Service Client", + "Secret1", + "estateManagement,transactionProcessor,transactionProcessorAcl", + "client_credentials"}); + table16.AddRow(new string[] { + "merchantClient", + "Merchant Client", + "Secret1", + "transactionProcessorAcl", + "password"}); +#line 16 + testRunner.Given("the following clients exist", ((string)(null)), table16, "Given "); +#line hidden + TechTalk.SpecFlow.Table table17 = new TechTalk.SpecFlow.Table(new string[] { + "ClientId"}); + table17.AddRow(new string[] { + "serviceClient"}); +#line 21 + testRunner.Given("I have a token to access the estate management and transaction processor acl reso" + + "urces", ((string)(null)), table17, "Given "); +#line hidden + TechTalk.SpecFlow.Table table18 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName"}); + table18.AddRow(new string[] { + "Test Estate 1"}); + table18.AddRow(new string[] { + "Test Estate 2"}); +#line 25 + testRunner.Given("I have created the following estates", ((string)(null)), table18, "Given "); +#line hidden + TechTalk.SpecFlow.Table table19 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "OperatorName", + "RequireCustomMerchantNumber", + "RequireCustomTerminalNumber"}); + table19.AddRow(new string[] { + "Test Estate 1", + "Safaricom", + "True", + "True"}); + table19.AddRow(new string[] { + "Test Estate 2", + "Safaricom", + "True", + "True"}); +#line 30 + testRunner.Given("I have created the following operators", ((string)(null)), table19, "Given "); +#line hidden + TechTalk.SpecFlow.Table table20 = new TechTalk.SpecFlow.Table(new string[] { + "MerchantName", + "AddressLine1", + "Town", + "Region", + "Country", + "ContactName", + "EmailAddress", + "EstateName"}); + table20.AddRow(new string[] { + "Test Merchant 1", + "Address Line 1", + "TestTown", + "Test Region", + "United Kingdom", + "Test Contact 1", + "testcontact1@merchant1.co.uk", + "Test Estate 1"}); + table20.AddRow(new string[] { + "Test Merchant 2", + "Address Line 1", + "TestTown", + "Test Region", + "United Kingdom", + "Test Contact 2", + "testcontact2@merchant2.co.uk", + "Test Estate 1"}); + table20.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 35 + testRunner.Given("I create the following merchants", ((string)(null)), table20, "Given "); +#line hidden + TechTalk.SpecFlow.Table table21 = new TechTalk.SpecFlow.Table(new string[] { + "EmailAddress", + "Password", + "GivenName", + "FamilyName", + "EstateName", + "MerchantName"}); + table21.AddRow(new string[] { + "merchantuser@testmerchant1.co.uk", + "123456", + "TestMerchant", + "User1", + "Test Estate 1", + "Test Merchant 1"}); + table21.AddRow(new string[] { + "merchantuser@testmerchant2.co.uk", + "123456", + "TestMerchant", + "User2", + "Test Estate 1", + "Test Merchant 2"}); + table21.AddRow(new string[] { + "merchantuser@testmerchant3.co.uk", + "123456", + "TestMerchant", + "User3", + "Test Estate 2", + "Test Merchant 3"}); +#line 41 + testRunner.Given("I have created the following security users", ((string)(null)), table21, "Given "); +#line hidden + TechTalk.SpecFlow.Table table22 = new TechTalk.SpecFlow.Table(new string[] { + "OperatorName", + "MerchantName", + "MerchantNumber", + "TerminalNumber", + "EstateName"}); + table22.AddRow(new string[] { + "Safaricom", + "Test Merchant 1", + "00000001", + "10000001", + "Test Estate 1"}); + table22.AddRow(new string[] { + "Safaricom", + "Test Merchant 2", + "00000002", + "10000002", + "Test Estate 1"}); + table22.AddRow(new string[] { + "Safaricom", + "Test Merchant 3", + "00000003", + "10000003", + "Test Estate 2"}); +#line 47 + testRunner.Given("I have assigned the following operator to the merchants", ((string)(null)), table22, "Given "); +#line hidden + TechTalk.SpecFlow.Table table23 = new TechTalk.SpecFlow.Table(new string[] { + "DeviceIdentifier", + "MerchantName", + "EstateName"}); + table23.AddRow(new string[] { + "123456780", + "Test Merchant 1", + "Test Estate 1"}); + table23.AddRow(new string[] { + "123456781", + "Test Merchant 2", + "Test Estate 1"}); + table23.AddRow(new string[] { + "123456782", + "Test Merchant 3", + "Test Estate 2"}); +#line 53 + testRunner.Given("I have assigned the following devices to the merchants", ((string)(null)), table23, "Given "); +#line hidden + } + + void System.IDisposable.Dispose() + { + this.TestTearDown(); + } + + [Xunit.SkippableFactAttribute(DisplayName="Sale Transaction")] + [Xunit.TraitAttribute("FeatureTitle", "SalesTransaction")] + [Xunit.TraitAttribute("Description", "Sale Transaction")] + [Xunit.TraitAttribute("Category", "PRTest")] + public virtual void SaleTransaction() + { + string[] tagsOfScenario = new string[] { + "PRTest"}; + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Sale Transaction", null, new string[] { + "PRTest"}); +#line 60 +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 +#line 61 + testRunner.Given("I am logged in as \"merchantuser@testmerchant1.co.uk\" with password \"123456\" for M" + + "erchant \"Test Merchant 1\" for Estate \"Test Estate 1\" with client \"merchantClient" + + "\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table24 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "OperatorName", + "TransactionAmount", + "CustomerAccountNumber"}); + table24.AddRow(new string[] { + "Today", + "1", + "Sale", + "Test Merchant 1", + "123456780", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789"}); +#line 62 + testRunner.When("I perform the following transactions", ((string)(null)), table24, "When "); +#line hidden +#line 66 + testRunner.Given("I am logged in as \"merchantuser@testmerchant2.co.uk\" with password \"123456\" for M" + + "erchant \"Test Merchant 2\" for Estate \"Test Estate 1\" with client \"merchantClient" + + "\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table25 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "OperatorName", + "TransactionAmount", + "CustomerAccountNumber"}); + table25.AddRow(new string[] { + "Today", + "2", + "Sale", + "Test Merchant 2", + "123456781", + "Test Estate 1", + "Safaricom", + "100.00", + "123456789"}); +#line 67 + testRunner.When("I perform the following transactions", ((string)(null)), table25, "When "); +#line hidden +#line 71 + testRunner.Given("I am logged in as \"merchantuser@testmerchant3.co.uk\" with password \"123456\" for M" + + "erchant \"Test Merchant 3\" for Estate \"Test Estate 2\" with client \"merchantClient" + + "\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + TechTalk.SpecFlow.Table table26 = new TechTalk.SpecFlow.Table(new string[] { + "DateTime", + "TransactionNumber", + "TransactionType", + "MerchantName", + "DeviceIdentifier", + "EstateName", + "OperatorName", + "TransactionAmount", + "CustomerAccountNumber"}); + table26.AddRow(new string[] { + "Today", + "3", + "Sale", + "Test Merchant 3", + "123456782", + "Test Estate 2", + "Safaricom", + "100.00", + "123456789"}); +#line 72 + testRunner.When("I perform the following transactions", ((string)(null)), table26, "When "); +#line hidden + TechTalk.SpecFlow.Table table27 = new TechTalk.SpecFlow.Table(new string[] { + "EstateName", + "MerchantName", + "TransactionNumber", + "TransactionType", + "ResponseCode", + "ResponseMessage"}); + table27.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 1", + "1", + "Sale", + "0000", + "SUCCESS"}); + table27.AddRow(new string[] { + "Test Estate 1", + "Test Merchant 2", + "2", + "Sale", + "0000", + "SUCCESS"}); + table27.AddRow(new string[] { + "Test Estate 2", + "Test Merchant 3", + "3", + "Sale", + "0000", + "SUCCESS"}); +#line 76 + testRunner.Then("transaction response should contain the following information", ((string)(null)), table27, "Then "); +#line hidden + } + this.ScenarioCleanup(); + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FixtureData : System.IDisposable + { + + public FixtureData() + { + SalesTransactionFeature.FeatureSetup(); + } + + void System.IDisposable.Dispose() + { + SalesTransactionFeature.FeatureTearDown(); + } + } + } +} +#pragma warning restore +#endregion diff --git a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs index 130ebd8..0ab2742 100644 --- a/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Shared/SharedSteps.cs @@ -311,21 +311,40 @@ public async Task WhenIPerformTheFollowingTransactions(Table table) String transactionType = SpecflowTableHelper.GetStringRowValue(tableRow, "TransactionType"); String deviceIdentifier = SpecflowTableHelper.GetStringRowValue(tableRow, "DeviceIdentifier"); + String responseMessage = null; switch (transactionType) { case "Logon": - String responseMessage = await this.PerformLogonTransaction(merchantToken, + responseMessage = await this.PerformLogonTransaction(merchantToken, transactionDateTime, transactionType, transactionNumber, deviceIdentifier, CancellationToken.None); - estateDetails.AddTransactionResponse(merchantId, transactionNumber, transactionType, responseMessage); - + + break; + case "Sale": + String operatorIdentifier = SpecflowTableHelper.GetStringRowValue(tableRow, "OperatorName"); + Decimal transactionAmount = SpecflowTableHelper.GetDecimalValue(tableRow, "TransactionAmount"); + String customerAccountNumber = SpecflowTableHelper.GetStringRowValue(tableRow, "CustomerAccountNumber"); + + responseMessage = await this.PerformSaleTransaction(merchantToken, + transactionDateTime, + transactionType, + transactionNumber, + deviceIdentifier, + operatorIdentifier, + transactionAmount, + customerAccountNumber, + CancellationToken.None); break; } + + responseMessage.ShouldNotBeNullOrEmpty("No response message received"); + + estateDetails.AddTransactionResponse(merchantId, transactionNumber, transactionType, responseMessage); } } @@ -461,6 +480,36 @@ private async Task PerformLogonTransaction(String merchantToken, DateTim return responseContent; } + private async Task PerformSaleTransaction(String merchantToken, DateTime transactionDateTime, String transactionType, String transactionNumber, String deviceIdentifier, String operatorIdentifier, Decimal transactionAmount, String customerAccountNumber, CancellationToken cancellationToken) + { + SaleTransactionRequestMessage saleTransactionRequestMessage = new SaleTransactionRequestMessage + { + DeviceIdentifier = deviceIdentifier, + TransactionDateTime = transactionDateTime, + TransactionNumber = transactionNumber, + OperatorIdentifier = operatorIdentifier, + Amount = transactionAmount, + CustomerAccountNumber = customerAccountNumber + }; + + String uri = "api/transactions"; + + StringContent content = new StringContent(JsonConvert.SerializeObject(saleTransactionRequestMessage, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }), Encoding.UTF8, "application/json"); + + this.TestingContext.DockerHelper.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", merchantToken); + + HttpResponseMessage response = await this.TestingContext.DockerHelper.HttpClient.PostAsync(uri, content, cancellationToken).ConfigureAwait(false); + + response.IsSuccessStatusCode.ShouldBeTrue(); + + String responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + return responseContent; + } + [Then(@"transaction response should contain the following information")] public void ThenTransactionResponseShouldContainTheFollowingInformation(Table table) { @@ -485,6 +534,41 @@ public void ThenTransactionResponseShouldContainTheFollowingInformation(Table ta } } + [Given(@"I have assigned the following devices to the merchants")] + public async Task GivenIHaveAssignedTheFollowingDevicesToTheMerchants(Table table) + { + foreach (TableRow tableRow in table.Rows) + { + EstateDetails estateDetails = this.TestingContext.GetEstateDetails(tableRow); + + String token = this.TestingContext.AccessToken; + if (String.IsNullOrEmpty(estateDetails.AccessToken) == false) + { + token = estateDetails.AccessToken; + } + + // Lookup the merchant id + String merchantName = SpecflowTableHelper.GetStringRowValue(tableRow, "MerchantName"); + Guid merchantId = estateDetails.GetMerchantId(merchantName); + + // Lookup the operator id + String deviceIdentifier = SpecflowTableHelper.GetStringRowValue(tableRow, "DeviceIdentifier"); + + AddMerchantDeviceRequest addMerchantDeviceRequest = new AddMerchantDeviceRequest + { + DeviceIdentifier = deviceIdentifier + }; + + AddMerchantDeviceResponse addMerchantDeviceResponse = await this.TestingContext.DockerHelper.EstateClient.AddDeviceToMerchant(token, estateDetails.EstateId, merchantId, addMerchantDeviceRequest, CancellationToken.None).ConfigureAwait(false); + + addMerchantDeviceResponse.EstateId.ShouldBe(estateDetails.EstateId); + addMerchantDeviceResponse.MerchantId.ShouldBe(merchantId); + addMerchantDeviceResponse.DeviceId.ShouldNotBe(Guid.Empty); + + this.TestingContext.Logger.LogInformation($"Device {deviceIdentifier} assigned to Merchant {merchantName} Estate {estateDetails.EstateName}"); + } + } + private void ValidateTransactionResponse(LogonTransactionResponseMessage logonTransactionResponseMessage, TableRow tableRow) { @@ -495,6 +579,16 @@ private void ValidateTransactionResponse(LogonTransactionResponseMessage logonTr logonTransactionResponseMessage.ResponseMessage.ShouldBe(expectedResponseMessage); } + private void ValidateTransactionResponse(SaleTransactionResponseMessage saleTransactionResponseMessage, + TableRow tableRow) + { + String expectedResponseCode = SpecflowTableHelper.GetStringRowValue(tableRow, "ResponseCode"); + String expectedResponseMessage = SpecflowTableHelper.GetStringRowValue(tableRow, "ResponseMessage"); + + saleTransactionResponseMessage.ResponseCode.ShouldBe(expectedResponseCode); + saleTransactionResponseMessage.ResponseMessage.ShouldBe(expectedResponseMessage); + } + [Given(@"I have a token to access the estate management and transaction processor acl resources")] public async Task GivenIHaveATokenToAccessTheEstateManagementAndTransactionProcessorAclResources(Table table) { diff --git a/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj b/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj index f5a0ec5..d71be2f 100644 --- a/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj +++ b/TransactionProcessorACL.IntegrationTests/TransactionProcessorACL.IntegrationTests.csproj @@ -2,24 +2,24 @@ netcoreapp3.1 - None + Full false - + - - - + + + - + - - - - + + + + all diff --git a/TransactionProcessorACL.Models/ProcessSaleTransactionResponse.cs b/TransactionProcessorACL.Models/ProcessSaleTransactionResponse.cs new file mode 100644 index 0000000..c31a03b --- /dev/null +++ b/TransactionProcessorACL.Models/ProcessSaleTransactionResponse.cs @@ -0,0 +1,25 @@ +namespace TransactionProcessorACL.Models +{ + using System; + using System.Diagnostics.CodeAnalysis; + + [ExcludeFromCodeCoverage] + public class ProcessSaleTransactionResponse + { + /// + /// Gets or sets the response code. + /// + /// + /// The response code. + /// + public String ResponseCode { get; set; } + + /// + /// Gets or sets the response message. + /// + /// + /// The response message. + /// + public String ResponseMessage { get; set; } + } +} \ No newline at end of file diff --git a/TransactionProcessorACL.Tests/TransactionProcessorACL.Tests.csproj b/TransactionProcessorACL.Tests/TransactionProcessorACL.Tests.csproj index 414c041..1b4e7c1 100644 --- a/TransactionProcessorACL.Tests/TransactionProcessorACL.Tests.csproj +++ b/TransactionProcessorACL.Tests/TransactionProcessorACL.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/TransactionProcessorACL/Controllers/TransactionController.cs b/TransactionProcessorACL/Controllers/TransactionController.cs index dbde6eb..500ed85 100644 --- a/TransactionProcessorACL/Controllers/TransactionController.cs +++ b/TransactionProcessorACL/Controllers/TransactionController.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace TransactionProcessorACL.Controllers +namespace TransactionProcessorACL.Controllers { + using System; using System.Diagnostics.CodeAnalysis; using System.Threading; + using System.Threading.Tasks; using BusinessLogic.Requests; using Common; using DataTransferObjects; @@ -14,8 +11,11 @@ namespace TransactionProcessorACL.Controllers using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; - using Newtonsoft.Json; + /// + /// + /// + /// [ExcludeFromCodeCoverage] [Route(TransactionController.ControllerRoute)] [ApiController] @@ -23,16 +23,44 @@ namespace TransactionProcessorACL.Controllers [Authorize] public class TransactionController : ControllerBase { + #region Fields + + /// + /// The mediator + /// private readonly IMediator Mediator; + /// + /// The model factory + /// private readonly IModelFactory ModelFactory; - public TransactionController(IMediator mediator, IModelFactory modelFactory) + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The mediator. + /// The model factory. + public TransactionController(IMediator mediator, + IModelFactory modelFactory) { this.Mediator = mediator; this.ModelFactory = modelFactory; } + #endregion + + #region Methods + + /// + /// Performs the transaction. + /// + /// The transaction request. + /// The cancellation token. + /// [HttpPost] [Route("")] public async Task PerformTransaction([FromBody] TransactionRequestMessage transactionRequest, @@ -45,12 +73,17 @@ public async Task PerformTransaction([FromBody] TransactionReques dynamic request = this.CreateCommandFromRequest((dynamic)transactionRequest); dynamic response = await this.Mediator.Send(request, cancellationToken); - + return this.Ok(this.ModelFactory.ConvertFrom(response)); // TODO: Populate the GET route //return this.Created("", transactionResponse); } + /// + /// Creates the command from request. + /// + /// The logon transaction request message. + /// private ProcessLogonTransactionRequest CreateCommandFromRequest(LogonTransactionRequestMessage logonTransactionRequestMessage) { Guid estateId = Guid.Parse(ClaimsHelper.GetUserClaim(this.User, "EstateId").Value); @@ -66,6 +99,29 @@ private ProcessLogonTransactionRequest CreateCommandFromRequest(LogonTransaction return request; } + /// + /// Creates the command from request. + /// + /// The sale transaction request message. + /// + private ProcessSaleTransactionRequest CreateCommandFromRequest(SaleTransactionRequestMessage saleTransactionRequestMessage) + { + Guid estateId = Guid.Parse(ClaimsHelper.GetUserClaim(this.User, "EstateId").Value); + Guid merchantId = Guid.Parse(ClaimsHelper.GetUserClaim(this.User, "MerchantId").Value); + + ProcessSaleTransactionRequest request = ProcessSaleTransactionRequest.Create(estateId, + merchantId, + saleTransactionRequestMessage.TransactionDateTime, + saleTransactionRequestMessage.TransactionNumber, + saleTransactionRequestMessage.DeviceIdentifier, + saleTransactionRequestMessage.OperatorIdentifier, + saleTransactionRequestMessage.Amount, + saleTransactionRequestMessage.CustomerAccountNumber); + + return request; + } + + #endregion #region Others @@ -81,4 +137,4 @@ private ProcessLogonTransactionRequest CreateCommandFromRequest(LogonTransaction #endregion } -} +} \ No newline at end of file diff --git a/TransactionProcessorACL/Factories/IModelFactory.cs b/TransactionProcessorACL/Factories/IModelFactory.cs index e71740c..7922d28 100644 --- a/TransactionProcessorACL/Factories/IModelFactory.cs +++ b/TransactionProcessorACL/Factories/IModelFactory.cs @@ -17,6 +17,13 @@ public interface IModelFactory /// LogonTransactionResponseMessage ConvertFrom(ProcessLogonTransactionResponse processLogonTransactionResponse); + /// + /// Converts from. + /// + /// The process sale transaction response. + /// + SaleTransactionResponseMessage ConvertFrom(ProcessSaleTransactionResponse processSaleTransactionResponse); + #endregion } } \ No newline at end of file diff --git a/TransactionProcessorACL/Factories/ModelFactory.cs b/TransactionProcessorACL/Factories/ModelFactory.cs index f77cda1..e3a665f 100644 --- a/TransactionProcessorACL/Factories/ModelFactory.cs +++ b/TransactionProcessorACL/Factories/ModelFactory.cs @@ -31,6 +31,26 @@ public LogonTransactionResponseMessage ConvertFrom(ProcessLogonTransactionRespon return logonTransactionResponseMessage; } + /// + /// Converts from. + /// + /// The process sale transaction response. + /// + public SaleTransactionResponseMessage ConvertFrom(ProcessSaleTransactionResponse processSaleTransactionResponse) + { + if (processSaleTransactionResponse == null) + { + return null; + } + + SaleTransactionResponseMessage saleTransactionResponseMessage = new SaleTransactionResponseMessage(); + + saleTransactionResponseMessage.ResponseMessage = processSaleTransactionResponse.ResponseMessage; + saleTransactionResponseMessage.ResponseCode = processSaleTransactionResponse.ResponseCode; + + return saleTransactionResponseMessage; + } + #endregion } } \ No newline at end of file diff --git a/TransactionProcessorACL/Startup.cs b/TransactionProcessorACL/Startup.cs index 5d5265b..cd66c74 100644 --- a/TransactionProcessorACL/Startup.cs +++ b/TransactionProcessorACL/Startup.cs @@ -69,7 +69,9 @@ public void ConfigureServices(IServiceCollection services) }); services.AddSingleton(); services.AddSingleton, ProcessLogonTransactionRequestHandler>(); + services.AddSingleton, ProcessSaleTransactionRequestHandler>(); services.AddSingleton, ProcessLogonTransactionRequest>(); + services.AddSingleton, ProcessSaleTransactionRequest>(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/TransactionProcessorACL/TransactionProcessorACL.csproj b/TransactionProcessorACL/TransactionProcessorACL.csproj index 782d5d1..3f0df4d 100644 --- a/TransactionProcessorACL/TransactionProcessorACL.csproj +++ b/TransactionProcessorACL/TransactionProcessorACL.csproj @@ -10,18 +10,18 @@ - - + + - - - - + + + + - - - + + +