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 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+