From dbbaa410687643d5dd2cef76160aa5295652b70e Mon Sep 17 00:00:00 2001 From: peter-yanong Date: Mon, 2 Mar 2020 21:29:53 +0800 Subject: [PATCH 001/120] Setup project file Update target framework from 1.3 to 2.0 Include System.IO.Ports to PackageReference. Update NetStandard.Library to version 2.0.3 --- src/GlobalPayments.Api/GlobalPayments.Api.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.csproj index 468dde4a..d3fb9b96 100644 --- a/src/GlobalPayments.Api/GlobalPayments.Api.csproj +++ b/src/GlobalPayments.Api/GlobalPayments.Api.csproj @@ -2,7 +2,7 @@ 3.0.0 - netstandard1.3 + netstandard2.0 GlobalPayments.Api GlobalPayments.Api 1.6.1 @@ -19,6 +19,7 @@ + @@ -29,4 +30,8 @@ + + + + From bc0dbe0b8f2d8cdccf797c93730bd1f28aba9c30 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:27:17 +0800 Subject: [PATCH 002/120] Update enum entities Include Ingenico termial to DeviceType enum. Include Cancel and Duplicate in TransactionType --- src/GlobalPayments.Api/Entities/Enums.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs index 77ae4d74..d8dd744f 100644 --- a/src/GlobalPayments.Api/Entities/Enums.cs +++ b/src/GlobalPayments.Api/Entities/Enums.cs @@ -55,7 +55,12 @@ public enum DeviceType { /// /// Indicates a genius terminal /// - GENIUS + GENIUS, + + /// + /// Indicates a Ingenico terminal + /// + INGENICO } /// @@ -353,7 +358,17 @@ public enum TransactionType : long{ /// DccRateLookup = 1 << 31, - Increment = 1L << 32 + Increment = 1L << 32, + + /// + /// Indicate that latest transaction will be duplicate. + /// + Duplicate = 1L << 33, + + /// + /// Indicate that the current transaction will be cancelled. + /// + Cancel = 1L << 34 } /// From cfc3b473e9596cdf6feeb38601eb35f5d4143421 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:47:42 +0800 Subject: [PATCH 003/120] Update some code due to Build failed Add Reporting to namespace of this file, cause 'GlobalPayments.Api.Entities' already contains a definition for 'LodgingData'. Build failed due to 'GatewayConfig' does not contain a constructor that takes 1 arguments GlobalPayments.Api. Build failed due to 'AcceptorConfig' is a type, which is not valid in the given context --- src/GlobalPayments.Api/Entities/Reporting/LodgingData.cs | 2 +- src/GlobalPayments.Api/ServiceConfigs/GatewayConfig.cs | 3 +++ .../ServiceConfigs/Gateways/GatewayConfig.cs | 2 +- .../ServiceConfigs/Gateways/TransitConfig.cs | 6 ++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Entities/Reporting/LodgingData.cs b/src/GlobalPayments.Api/Entities/Reporting/LodgingData.cs index 5cd30788..d984f5c2 100644 --- a/src/GlobalPayments.Api/Entities/Reporting/LodgingData.cs +++ b/src/GlobalPayments.Api/Entities/Reporting/LodgingData.cs @@ -1,4 +1,4 @@ -namespace GlobalPayments.Api.Entities { +namespace GlobalPayments.Api.Entities.Reporting { public class LodgingData { public string PrestigiousPropertyLimit { get; set; } public bool NoShow { get; set; } diff --git a/src/GlobalPayments.Api/ServiceConfigs/GatewayConfig.cs b/src/GlobalPayments.Api/ServiceConfigs/GatewayConfig.cs index 2066e2e7..2f59ab35 100644 --- a/src/GlobalPayments.Api/ServiceConfigs/GatewayConfig.cs +++ b/src/GlobalPayments.Api/ServiceConfigs/GatewayConfig.cs @@ -4,6 +4,9 @@ namespace GlobalPayments.Api { public class GatewayConfig : Configuration { + public GatewayConfig(GatewayProvider transIT) { + } + // Portico /// /// Account's site ID diff --git a/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs b/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs index ddd0e50e..38a30e41 100644 --- a/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs +++ b/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs @@ -1,7 +1,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Gateways; -namespace GlobalPayments.Api { +namespace GlobalPayments.Api.Gateway { public abstract class GatewayConfig : Configuration { public AcceptorConfig AcceptorConfig { get; set; } protected GatewayProvider GatewayProvider { get; set; } diff --git a/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs b/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs index d5a9aa61..b82f9e25 100644 --- a/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs +++ b/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs @@ -8,7 +8,9 @@ public class TransitConfig : GatewayConfig { public string DeviceId { get;set; } public string MerchantId { get; set; } public string TransactionKey { get; set; } - + + private AcceptorConfig AcceptorConfig; + public TransitConfig() : base(GatewayProvider.TransIT) { } internal override void ConfigureContainer(ConfiguredServices services) { @@ -20,7 +22,7 @@ internal override void ConfigureContainer(ConfiguredServices services) { } else ServiceUrl = ServiceEndpoints.TRANSIT_MULTIPASS_PRODUCTION; } - + AcceptorConfig = new AcceptorConfig(); var gateway = new TransitConnector() { AcceptorConfig = AcceptorConfig, DeveloperId = DeveloperId, From c63aed783cc8b75bd5e96cecdc8951982966fbc7 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:51:16 +0800 Subject: [PATCH 004/120] Update for Ingenico needed configuration Added TCP_IP_SERVER in ConnectionModes enum because SDK will run as a Server. Added 9600 Baudrate and Handshake. Added case block for handling Ingeinco devices. --- .../Terminals/ConnectionConfig.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs index f200899b..6294f614 100644 --- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs +++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs @@ -3,16 +3,20 @@ using GlobalPayments.Api.Terminals.HPA; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Genius; +using System.IO.Ports; +using GlobalPayments.Api.Terminals.INGENICO; namespace GlobalPayments.Api.Terminals { public enum ConnectionModes { SERIAL, TCP_IP, SSL_TCP, - HTTP + HTTP, + TCP_IP_SERVER } public enum BaudRate { + r9600 = 9600, r38400 = 38400, r57600 = 57600, r19200 = 19200, @@ -47,6 +51,7 @@ public interface ITerminalConfiguration { Parity Parity { get; set; } StopBits StopBits { get; set; } DataBits DataBits { get; set; } + Handshake Handshake { get; set; } // Timeout int Timeout { get; set; } @@ -62,6 +67,7 @@ public class ConnectionConfig : Configuration, ITerminalConfiguration { public Parity Parity { get; set; } public StopBits StopBits { get; set; } public DataBits DataBits { get; set; } + public Handshake Handshake { get; set; } public string IpAddress { get; set; } public string Port { get; set; } public IRequestIdProvider RequestIdProvider { get; set; } @@ -84,8 +90,11 @@ internal override void ConfigureContainer(ConfiguredServices services) { services.DeviceController = new HpaController(this); break; //case DeviceType.GENIUS: - //services.DeviceController = new GeniusController(this); - //break; + //services.DeviceController = new GeniusController(this); + //break; + case DeviceType.INGENICO: + services.DeviceController = new IngenicoController(this); + break; default: break; } From 0b3e75d56f769eb18fcef538f6964263c2250aca Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:51:45 +0800 Subject: [PATCH 005/120] Handle BroadcastMessageEventHandler Invoke OnBroadcastMessage event to handle broadcast message from terminal. --- src/GlobalPayments.Api/Terminals/DeviceController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/DeviceController.cs b/src/GlobalPayments.Api/Terminals/DeviceController.cs index c494ca55..cb2c5686 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceController.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceController.cs @@ -33,6 +33,7 @@ public IRequestIdProvider RequestIdProvider { } public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; internal DeviceController(ITerminalConfiguration settings) { _settings = settings; @@ -40,6 +41,10 @@ internal DeviceController(ITerminalConfiguration settings) { _connector.OnMessageSent += (message) => { OnMessageSent?.Invoke(message); }; + + _connector.OnBroadcastMessage += (code, message) => { + OnBroadcastMessage?.Invoke(code, message); + }; } public byte[] Send(IDeviceMessage message) { From 9bfe8a87e16a9bf35441af0cbc0d26f35263c5e0 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:52:28 +0800 Subject: [PATCH 006/120] Added methods in line with IDeviceInterface which is extended for this class. Added GetLastReceipt, GetReport, Completion and enhanced Verify methods. --- .../Terminals/DeviceInterface.cs | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 859d6020..bb9f40f6 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -2,6 +2,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; +using GlobalPayments.Api.Terminals.INGENICO; using GlobalPayments.Api.Terminals.Messaging; namespace GlobalPayments.Api.Terminals { @@ -10,18 +11,26 @@ public abstract class DeviceInterface : IDeviceInterface where T : DeviceCont protected IRequestIdProvider _requestIdProvider; public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; internal DeviceInterface(T controller) { _controller = controller; _controller.OnMessageSent += (message) => { OnMessageSent?.Invoke(message); }; + + _controller.OnBroadcastMessage += (code, message) => { + OnBroadcastMessage?.Invoke(code, message); + }; + + _requestIdProvider = _controller.RequestIdProvider; } #region Admin Methods - public virtual void Cancel() { - throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + public virtual TerminalManageBuilder Cancel(decimal? amount = null) { + return new TerminalManageBuilder(TransactionType.Cancel, PaymentMethodType.Credit) + .WithAmount(amount); } public virtual IDeviceResponse CloseLane() { @@ -93,8 +102,16 @@ public virtual IEODResponse EndOfDay() { #region Reporting Methods public virtual TerminalReportBuilder LocalDetailReport() { - throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + return new TerminalReportBuilder(TerminalReportType.LocalDetailReport); + } + public virtual TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptType.TICKET) { + return new TerminalReportBuilder(type); + } + + public virtual TerminalAuthBuilder GetReport(INGENICO.ReportType type) { + return new TerminalAuthBuilder(TransactionType.Create, PaymentMethodType.Other).WithReportType(type); } + #endregion #region Transactions @@ -121,8 +138,15 @@ public virtual TerminalAuthBuilder Sale(decimal? amount = null) { return new TerminalAuthBuilder(TransactionType.Sale, PaymentMethodType.Credit) .WithAmount(amount); } + + public virtual TerminalAuthBuilder Completion(decimal? amount = null) { + return new TerminalAuthBuilder(TransactionType.PreAuthCompletion, PaymentMethodType.Credit) + .WithAmount(amount); + } + public virtual TerminalAuthBuilder Verify() { - return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit); + return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit) + .WithAmount(6.18m); } public virtual TerminalManageBuilder Void() { return new TerminalManageBuilder(TransactionType.Void, PaymentMethodType.Credit); @@ -138,5 +162,20 @@ public void Dispose() { _controller.Dispose(); } #endregion + + #region For clarification + + #region Transaction Management + public virtual TerminalManageBuilder Reverse(decimal? amount = null) { + return new TerminalManageBuilder(TransactionType.Reversal, PaymentMethodType.Credit) + .WithAmount(amount); + } + public virtual TerminalManageBuilder Duplicate(decimal? amount = null) { + return new TerminalManageBuilder(TransactionType.Duplicate, PaymentMethodType.Credit) + .WithAmount(amount); + } + #endregion + + #endregion } } From 0d17a6aee6aa9337d0768dcf448235187270ce82 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:53:54 +0800 Subject: [PATCH 007/120] Added methods for request parsing. Added BuildIngenicoRequest, CalculateLRC methods. Create method for calculating header for TCP communication. Create method to get header from terminal data and parsed to int. --- .../Terminals/TerminalUtilities.cs | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs index c2647ec6..f3af2040 100644 --- a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs +++ b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs @@ -7,6 +7,7 @@ using System.IO; using System.Drawing.Drawing2D; using System.Drawing.Imaging; +using GlobalPayments.Api.Entities; namespace GlobalPayments.Api.Terminals { public class TerminalUtilities { @@ -30,6 +31,35 @@ private static string GetElementString(object[] elements) { return sb.ToString(); } + public static DeviceMessage BuildIngenicoRequest(string message, ConnectionModes? settings) { + var buffer = new List(); + byte[] lrc; + + switch (settings) { + case ConnectionModes.SERIAL: + buffer.Add((byte)ControlCodes.STX); + foreach (char c in message) + buffer.Add((byte)c); + buffer.Add((byte)ControlCodes.ETX); + lrc = CalculateLRC(message); + buffer.Add(lrc[0]); + break; + case ConnectionModes.TCP_IP_SERVER: + var _msg = CalculateHeader(Encoding.UTF8.GetBytes(message)) + message; + + foreach (char c in _msg) + buffer.Add((byte)c); + + break; + case ConnectionModes.TCP_IP: + break; + default: + throw new BuilderException("Failed to build request message. Unknown Connection mode."); + } + + return new DeviceMessage(buffer.ToArray()); + } + private static DeviceMessage BuildMessage(string messageId, string message) { var buffer = new List(); @@ -118,7 +148,7 @@ public static byte[] BuildSignatureImage(string pathData, int width = 150) { Bitmap bmp = new Bitmap(width, 100); var gfx = Graphics.FromImage(bmp); - gfx.Clear(Color.White); + //gfx.Clear(Color.White); var index = 0; var coordinate = coordinates[index++]; @@ -142,5 +172,40 @@ public static byte[] BuildSignatureImage(string pathData, int width = 150) { return ms.ToArray(); } } + + public static string CalculateHeader(byte[] buffer) { + //The Header contains the data length in hexadecimal format on two digits + var hex = buffer.Length.ToString("X4"); + hex = hex.PadLeft(4, '0'); + + // Get total value per two char. + var fDigit = hex[0].ToString() + hex[1]; + var sDigit = hex[2].ToString() + hex[3]; + + return string.Format("{0}{1}", Convert.ToChar(Convert.ToUInt32(fDigit, 16)), + Convert.ToChar(Convert.ToUInt32(sDigit, 16))); + } + + public static int HeaderLength(byte[] buffer) { + // Conversion from decimal to hex value + var fHex = Convert.ToInt64(buffer[0]).ToString("X2"); + var sHex = Convert.ToInt64(buffer[1]).ToString("X2"); + + // Concat two hex value + var _hex = fHex + sHex; + + // Get decimal value of concatenated hex + return int.Parse(_hex, System.Globalization.NumberStyles.HexNumber); + } + + public static byte[] CalculateLRC(string requestMessage) { + byte[] bytes = Encoding.ASCII.GetBytes((requestMessage + (char)ControlCodes.ETX)); + byte lrc = 0; + for (int i = 0; i < bytes.Length; i++) { + lrc ^= bytes[i]; + } + bytes = new byte[] { lrc }; + return bytes; + } } } From 4d7d64458c7a48f1cc1fb097a8d44a227edf4e02 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:54:10 +0800 Subject: [PATCH 008/120] Broadcase message event hanlder declaration. --- .../Terminals/Abstractions/IDeviceCommInterface.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs index 2113d5bb..cb82d647 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs @@ -9,5 +9,7 @@ public interface IDeviceCommInterface { byte[] Send(IDeviceMessage message); event MessageSentEventHandler OnMessageSent; + + event BroadcastMessageEventHandler OnBroadcastMessage; } } From 55fff4847568c7dc7a85e85df47884cdafa8ae9a Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:54:47 +0800 Subject: [PATCH 009/120] Added interface methods. Added GetLastReceipt, GetReport, Completion, methods and added summary/description for the methods; GetLastReceipt, GetReport, Sale, Refund, Completion, Verify. --- .../Abstractions/IDeviceInterface.cs | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 17ec5cf4..18d6c4d6 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -2,14 +2,22 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; +using GlobalPayments.Api.Terminals.INGENICO; using GlobalPayments.Api.Terminals.Messaging; namespace GlobalPayments.Api.Terminals { public interface IDeviceInterface : IDisposable { event MessageSentEventHandler OnMessageSent; + event BroadcastMessageEventHandler OnBroadcastMessage; #region Admin Calls - void Cancel(); + + /// + /// A method for Cancelling a live transaction. + /// + /// Amount to be passed for cancel request. + /// TerminalManageBuilder + TerminalManageBuilder Cancel(decimal? amount = null); IDeviceResponse CloseLane(); IDeviceResponse DisableHostResponseBeep(); ISignatureResponse GetSignatureFile(); @@ -28,6 +36,19 @@ public interface IDeviceInterface : IDisposable { #region reporting TerminalReportBuilder LocalDetailReport(); + /// + /// Instruct the terminal to print the receipt of the last completed transaction. + /// + /// Receipt Type + /// + TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptType.TICKET); + + /// + /// Instruct the terminal to get the report in XML format of all the transactions. + /// + /// Report Type + /// + TerminalAuthBuilder GetReport(INGENICO.ReportType type); #endregion #region Batch Calls @@ -65,14 +86,61 @@ public interface IDeviceInterface : IDisposable { #region Generic Calls TerminalAuthBuilder AddValue(decimal? amount = null); + + /// + /// Intructs the terminal to transact hotel mode pre-auth. + /// + /// Pre-auth Amount + /// TerminalAuthBuilder Authorize(decimal? amount = null); TerminalAuthBuilder Balance(); TerminalManageBuilder Capture(decimal? amount = null); + + /// + /// Intructs the terminal to complete the completed hotel mode pre-auth transaction. + /// + /// Complete Amount/param> + /// + TerminalAuthBuilder Completion(decimal? amount = null); + + /// + /// Instruct the temrinal to refund the last completed transaction. + /// + /// Refund Amount + /// TerminalAuthBuilder Refund(decimal? amount = null); + + /// + /// Instruct the terminal to process sale transaction. + /// + /// Sale Amount + /// TerminalAuthBuilder Sale(decimal? amount = null); + + /// + /// Verify the account of the card holder. + /// + /// TerminalAuthBuilder Verify(); TerminalManageBuilder Void(); TerminalAuthBuilder Withdrawal(decimal? amount = null); #endregion + + #region Terminal Management + + /// + /// The terminal immediately performs a reversal of the last completed transaction if no Transaction Id is set. + /// + /// Amount to be passed for cancel request. + /// TerminalManageBuilder + TerminalManageBuilder Reverse(decimal? amount = null); + + /// + /// The terminal immediately initiates a duplicate of the last completed transaction + /// + /// Amount to be passed for cancel request. + /// TerminalManageBuilder + TerminalManageBuilder Duplicate(decimal? amount = null); + #endregion } } From 7bb0aeb424659a978434b39cd2affbc4306311d4 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:55:34 +0800 Subject: [PATCH 010/120] Added property for the interface inside this class named; ITerminalReport Added string ReportData as property. --- .../Terminals/Abstractions/IDeviceResponse.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs index dee676d3..3485eb21 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs @@ -48,8 +48,9 @@ public interface ITerminalResponse : IDeviceResponse { string ApplicationCryptogram { get; set; } string CardHolderVerificationMethod { get; set; } string TerminalVerificationResults { get; set; } - decimal? MerchantFee { get; set; } } - public interface ITerminalReport : IDeviceResponse { } + public interface ITerminalReport : IDeviceResponse { + string ReportData { get; set; } + } } From e9af3bf7a7602e8a139f59e64cbd0b2276648565 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:56:23 +0800 Subject: [PATCH 011/120] Added necessary properties and methods to fulfill the request message frame 3 needed for the Ingenico parameters. Properties; ReportType, CurrencyCode, PaymentMode, TableNumber, ExtendedDataTags. Methods; WithReportType, WithCurrencyCode, WithPaymentMode, WithTableNumber. --- .../Terminals/Builders/TerminalAuthBuilder.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index 99058151..513727b8 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -2,6 +2,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.PaymentMethods; using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; namespace GlobalPayments.Api.Terminals.Builders { public class TerminalAuthBuilder : TerminalBuilder { @@ -35,6 +36,17 @@ internal string TransactionId { return null; } } + internal INGENICO.ReportType? ReportType { get; set; } = null; + + /// + /// Sets the report type for the transaction. + /// + /// Report Type + /// + public TerminalAuthBuilder WithReportType(INGENICO.ReportType reportType) { + ReportType = reportType; + return this; + } public TerminalAuthBuilder WithAddress(Address address) { Address = address; @@ -48,10 +60,17 @@ public TerminalAuthBuilder WithAmount(decimal? amount) { Amount = amount; return this; } + + /// + /// Sets the authorization code for the transaction. + /// + /// Authorization Code + /// public TerminalAuthBuilder WithAuthCode(string value) { if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) PaymentMethod = new TransactionReference(); (PaymentMethod as TransactionReference).AuthCode = value; + ExtendedDataTags = ExtendedDataTags.AUTHCODE; return this; } @@ -64,8 +83,15 @@ public TerminalAuthBuilder WithAutoSubstantiation(AutoSubstantiation value) { AutoSubstantiation = value; return this; } + + /// + /// Sets the cash back for the transaction. + /// + /// + /// public TerminalAuthBuilder WithCashBack(decimal? amount) { CashBackAmount = amount; + ExtendedDataTags = ExtendedDataTags.CASHB; return this; } public TerminalAuthBuilder WithClientTransactionId(string value) { @@ -126,9 +152,56 @@ public TerminalAuthBuilder WithTransactionId(string value) { return this; } + #region Additional Methods for Ingenico + + internal string CurrencyCode { get; set; } + internal PaymentMode PaymentMode { get; set; } + + internal string TableNumber { get; set; } + internal ExtendedDataTags ExtendedDataTags { get; set; } + + + /// + /// Sets the currency code for the transaction. + /// + /// Currency Code + /// + public TerminalAuthBuilder WithCurrencyCode(string value) { + CurrencyCode = value; + return this; + } + + /// + /// Sets the payment mode for the transaction. + /// + /// Payment Mode + /// + public TerminalAuthBuilder WithPaymentMode(PaymentMode value) { + PaymentMode = value; + return this; + } + + /// + /// Sets the table number for the transaction. + /// + /// Table Number + /// + public TerminalAuthBuilder WithTableNumber(string value) { + TableNumber = value; + ExtendedDataTags = ExtendedDataTags.TABLE_NUMBER; + return this; + } + + #endregion + internal TerminalAuthBuilder(TransactionType type, PaymentMethodType paymentType) : base(type, paymentType) { } + /// + /// Executes the transaction. + /// + /// + /// public override ITerminalResponse Execute(string configName = "default") { base.Execute(configName); From 0e2a9f38813e421cc5e2a142c1a93dd120de99d6 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:57:15 +0800 Subject: [PATCH 012/120] Added necessary properties and methods to fulfill the request message frame 3 needed for the Ingenico parameters for the Capture method (Hotel Mode Completion). Properties; CurrencyCode, PaymentMode, ExtendedDataTags, AuthCode. Methods; WithCurrencyCode, WithAuthCode. --- .../Builders/TerminalManageBuilder.cs | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs index 8fd725c4..a7bdbe84 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs @@ -1,6 +1,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.PaymentMethods; using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; namespace GlobalPayments.Api.Terminals.Builders { public class TerminalManageBuilder : TerminalBuilder { @@ -15,7 +16,27 @@ internal string TransactionId { return null; } } + internal string CurrencyCode { get; set; } + internal PaymentMode PaymentMode { get; set; } + internal ExtendedDataTags ExtendedDataTags { get; set; } + internal string AuthCode { + get { + if (PaymentMethod is TransactionReference) + return (PaymentMethod as TransactionReference).AuthCode; + return null; + } + } + /// + /// Sets the currency code for the transaction. + /// + /// Currency Code + /// + public TerminalManageBuilder WithCurrencyCode(string value) { + CurrencyCode = value; + return this; + } + public TerminalManageBuilder WithAmount(decimal? amount) { Amount = amount; return this; @@ -32,10 +53,25 @@ public TerminalManageBuilder WithGratuity(decimal? amount) { Gratuity = amount; return this; } + + /// + /// Sets the authorization code for the transaction. + /// + /// Authorization Code + /// + public TerminalManageBuilder WithAuthCode(string value) { + if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) + PaymentMethod = new TransactionReference(); + (PaymentMethod as TransactionReference).AuthCode = value; + ExtendedDataTags = ExtendedDataTags.AUTHCODE; + return this; + } + public TerminalManageBuilder WithTransactionId(string value) { if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) PaymentMethod = new TransactionReference(); (PaymentMethod as TransactionReference).TransactionId = value; + return this; } @@ -57,9 +93,10 @@ public override byte[] Serialize(string configName = "default") { } protected override void SetupValidations() { - Validations.For(TransactionType.Capture).Check(() => TransactionId).IsNotNull(); + Validations.For(TransactionType.Capture).When(() => AuthCode).IsNull().Check(() => TransactionId).IsNotNull(); Validations.For(TransactionType.Void).When(() => ClientTransactionId).IsNull().Check(() => TransactionId).IsNotNull(); Validations.For(PaymentMethodType.Gift).Check(() => Currency).IsNotNull(); + Validations.For(TransactionType.Cancel).Check(() => Amount).IsNotNull(); } } } From 3b8dec5061c478e681ea20c4e63e3eb1256ed335 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 22:57:42 +0800 Subject: [PATCH 013/120] Added properties and methods to identify what command from XML Management will be use Added ReceiptType as property and added builder load method to get what receipt type is use/selected. --- .../Terminals/Builders/TerminalReportBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs index 56156875..bd8adf8b 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs @@ -1,4 +1,5 @@ using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; using GlobalPayments.Api.Terminals.PAX; using System; using System.Linq; @@ -7,6 +8,7 @@ namespace GlobalPayments.Api.Terminals.Builders { public class TerminalReportBuilder { internal TerminalReportType ReportType { get; set; } + internal ReceiptType ReceiptType { get; set; } private TerminalSearchBuilder _searchBuilder; internal TerminalSearchBuilder SearchBuilder { @@ -22,6 +24,10 @@ public TerminalReportBuilder(TerminalReportType reportType) { ReportType = reportType; } + public TerminalReportBuilder(ReceiptType receiptType) { + ReceiptType = receiptType; + } + public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) { return SearchBuilder.And(criteria, value); } From 0aa5213bd6a2897043bc2795f8a387c81a186a1d Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:02:34 +0800 Subject: [PATCH 014/120] Handle BroadcastMessageEventHandler Update event to accept code and message parameter. Invoke OnBroadcastMessage event to handle broadcast message from terminal. Implement OnBroadcastMessage event in affected terminal communication interface. --- .../Terminals/Genius/Interfaces/GeniusHttpInterface.cs | 1 + .../Terminals/HPA/Interfaces/HpaTcpInterface.cs | 1 + .../Terminals/Messaging/BroadcastMessageEventHandler.cs | 2 +- .../Terminals/PAX/Interfaces/PaxHttpInterface.cs | 1 + .../Terminals/PAX/Interfaces/PaxTcpInterface.cs | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs index 52515891..e9d2266e 100644 --- a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs @@ -17,6 +17,7 @@ internal class GeniusHttpInterface : IDeviceCommInterface { private GeniusConfig _gatewayConfig; public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; public GeniusHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs index 8ebdb03b..42886756 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs @@ -16,6 +16,7 @@ internal class HpaTcpInterface : IDeviceCommInterface { List message_queue; public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; public HpaTcpInterface(ITerminalConfiguration settings) { this._settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/Messaging/BroadcastMessageEventHandler.cs b/src/GlobalPayments.Api/Terminals/Messaging/BroadcastMessageEventHandler.cs index d7cdab72..14fb0ba6 100644 --- a/src/GlobalPayments.Api/Terminals/Messaging/BroadcastMessageEventHandler.cs +++ b/src/GlobalPayments.Api/Terminals/Messaging/BroadcastMessageEventHandler.cs @@ -1,3 +1,3 @@ namespace GlobalPayments.Api.Terminals.Messaging { - public delegate void BroadcastMessageEventHandler(); + public delegate void BroadcastMessageEventHandler(string code, string message); } diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs index 5f8229ea..6c318d20 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs @@ -13,6 +13,7 @@ internal class PaxHttpInterface : IDeviceCommInterface { WebRequest _client; public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; public PaxHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs index 2f440631..4a4810be 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs @@ -14,6 +14,7 @@ internal class PaxTcpInterface : IDeviceCommInterface { int _connectionCount = 0; public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; public PaxTcpInterface(ITerminalConfiguration settings) { _settings = settings; From d6172e403d397dfc7a59ca3bb48811d08538df06 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:03:38 +0800 Subject: [PATCH 015/120] Update Terminal Interfaces Handle changed return type of Cancel method for terminal interfaces. --- src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs | 3 ++- src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs index 71be05bd..a149b9f9 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs @@ -14,9 +14,10 @@ internal HpaInterface(HpaController controller) : base(controller) { } #region Admin Messages - public override void Cancel() { + public override TerminalManageBuilder Cancel(decimal? amount = null) { // TODO: Cancel for HPA? Reset(); + return null; } public override IDeviceResponse CloseLane() { diff --git a/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs index 29b52734..d96755af 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs @@ -24,18 +24,20 @@ public override ISignatureResponse GetSignatureFile() { return new SignatureResponse(response, _controller.DeviceType.Value); } - public override void Cancel() { + public override TerminalManageBuilder Cancel(decimal? amount = null) { if (_controller.ConnectionMode == ConnectionModes.HTTP) { throw new MessageException("The cancel command is not available in HTTP mode"); } try { _controller.Send(TerminalUtilities.BuildRequest(PAX_MSG_ID.A14_CANCEL)); + return null; } catch (MessageException exc) { if (!exc.Message.Equals("Terminal returned EOT for the current message.")) { throw; } + return null; } } From 91da7c4171e44ee59a4a9d327b1b2de0d3c9b3e4 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:07:21 +0800 Subject: [PATCH 016/120] Update Terminal Report response Implement ReportData property for Terminal report responses. --- .../Terminals/HPA/Responses/SipBaseResponse.cs | 2 ++ .../Terminals/PAX/Responses/DeviceResponse.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs b/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs index a33768a3..11a7f6e4 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs @@ -256,5 +256,7 @@ internal SipTerminalResponse(byte[] buffer, params string[] messageIds) : base(b public class SipTerminalReport : SipBaseResponse, ITerminalReport { internal SipTerminalReport(byte[] buffer, params string[] messageIds) : base(buffer, messageIds) { } + + public string ReportData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } } diff --git a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs index 0e544eba..b41523d8 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs @@ -327,6 +327,8 @@ public class PaxTerminalReport : PaxBaseResponse, ITerminalReport { internal PaxTerminalReport(byte[] buffer, params string[] messageIds) : base(buffer, messageIds) { } + public string ReportData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + protected virtual void MapResponse() { } } } From 23ba14bce9aaaf4b3adb074ac6fc02f1f92dc5ff Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:08:20 +0800 Subject: [PATCH 017/120] Added some methods Added SubArray method, to get sub array data. Added IsNull method for checking of null value. --- src/GlobalPayments.Api/Utils/Extensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/GlobalPayments.Api/Utils/Extensions.cs b/src/GlobalPayments.Api/Utils/Extensions.cs index be53c37b..d8d8d68d 100644 --- a/src/GlobalPayments.Api/Utils/Extensions.cs +++ b/src/GlobalPayments.Api/Utils/Extensions.cs @@ -206,5 +206,14 @@ public static string TrimEnd(this string str, string trimString) { } return rvalue; } + public static T[] SubArray(this T[] data, int index, int length) { + T[] result = new T[length]; + Array.Copy(data, index, result, 0, length); + return result; + } + + public static bool IsNull(this T data) { + return data == null; + } } } From 3db1ae203ac28eebafa474a7b6fc7cbba4c0e7f8 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:09:56 +0800 Subject: [PATCH 018/120] Ingenico TCP comm layer Create a new tcp listener that inherits TcpListener class so that can access Active property of Tcp listener. Added this class to communicate with the terminal/Ingenico using TCP/IP comm. --- .../Interfaces/IngenicoTcpInterface.cs | 207 ++++++++++++++++++ .../INGENICO/Interfaces/TcpListenerEx.cs | 12 + 2 files changed, 219 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs new file mode 100644 index 00000000..5b3ffdae --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -0,0 +1,207 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using GlobalPayments.Api.Utils; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Messaging; +using System.Collections.Generic; +using GlobalPayments.Api.Entities; +using System.Threading; +using System.Threading.Tasks; +//using Serilog; + +namespace GlobalPayments.Api.Terminals.INGENICO { + internal class IngenicoTcpInterface : IDeviceCommInterface { + private TcpClient _client; + private NetworkStream _stream; + private ITerminalConfiguration _settings; + private TcpListenerEx _listener; + private Socket _server; + private List _ipAddresses = new List(); + private BroadcastMessage _broadcastMessage; + private byte[] termResponse; + private Thread dataReceiving; + + public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; + + public IngenicoTcpInterface(ITerminalConfiguration settings) { + _settings = settings; + _client = new TcpClient(); + _ipAddresses = new List(); + + InitializeServer(); + + // Start listening to port. + Connect(); + + // Accepting client connected to port. + AcceptingClient(); + } + + public void Connect() { + try { + if (!_listener.Active) { + _listener.Start(); + } + else { + throw new ConfigurationException("Server already started."); + } + } + catch (Exception ex) { + throw new Exception(ex.Message); + } + } + + public void Disconnect() { + try { + if (_listener.Active) { + + // Closing and disposing current clients + _client.Close(); + _client.Dispose(); + + // Stopping server listening + _listener.Stop(); + + _ipAddresses.Clear(); + } + } + catch (Exception ex) { + throw new Exception(ex.Message); + } + + } + + public byte[] Send(IDeviceMessage message) { + + byte[] buffer = message.GetSendBuffer(); + termResponse = null; + + try { + // Validate if server is starting + if (!_listener.Active) { + throw new ConfigurationException("Server is not running."); + } + + + if (_ipAddresses.Count > 0) { + _stream.WriteAsync(buffer, 0, buffer.Length).Wait(); + // Should be move to Finally block before deployment + OnMessageSent?.Invoke(Encoding.UTF8.GetString(RemoveHeader(buffer))); + + while (termResponse == null) { + Thread.Sleep(100); + if (termResponse != null) { + return termResponse; + } + } + return null; + } + else + throw new ConfigurationException("No terminal connected to server."); + } + catch (Exception ex) { + throw new Exception(ex.Message); + } + } + + #region Interface private Methods + private void InitializeServer() { + if (_listener == null) { + int _port = INGENICO_GLOBALS.IP_PORT; // Default port. + if (!string.IsNullOrWhiteSpace(_settings.Port)) { + if (!int.TryParse(_settings.Port, out _port)) + throw new ConfigurationException("Invalid port number."); + } + + _listener = new TcpListenerEx(IPAddress.Any, _port); + + // Set timeout for client to send data. + _server = _listener.Server; + _server.ReceiveTimeout = _settings.Timeout; + } + else { + throw new ConfigurationException("Server already initialize."); + } + } + + private void AcceptingClient() { + _client = _listener.AcceptTcpClient(); + _stream = _client.GetStream(); + _ipAddresses.Add(((IPEndPoint)_client.Client.RemoteEndPoint).Address); + + // Start thread for handling keep alive request. + if (dataReceiving == null || dataReceiving.ThreadState != ThreadState.Running) { + dataReceiving = new Thread(new ThreadStart(AnalyzeReceivedData)); + dataReceiving.Start(); + } + } + + private bool isBroadcast(byte[] terminalResponse) { + return Encoding.UTF8.GetString(terminalResponse).Contains(INGENICO_GLOBALS.BROADCAST); + } + + private bool isCancel(byte[] buffer) { + return Encoding.UTF8.GetString(buffer).Contains(INGENICO_GLOBALS.CANCEL); + } + + private bool isKeepAlive(byte[] buffer) { + return Encoding.UTF8.GetString(buffer).Contains(INGENICO_GLOBALS.TID_CODE); + } + + private byte[] RemoveHeader(byte[] buffer) { + return buffer.SubArray(2, buffer.Length - 2); + } + + private byte[] KeepAliveResponse(byte[] buffer) { + if (buffer.Length > 0) { + var tIdIndex = Encoding.ASCII.GetString(buffer, 0, buffer.Length).IndexOf(INGENICO_GLOBALS.TID_CODE); + var tId = Encoding.ASCII.GetString(buffer, tIdIndex + 10, 8); + + var respData = INGENICO_GLOBALS.KEEP_ALIVE_RESPONSE.FormatWith(tId); + respData = TerminalUtilities.CalculateHeader(Encoding.ASCII.GetBytes(respData)) + respData; + return Encoding.ASCII.GetBytes(respData); + } + else { + return null; + } + } + + private void AnalyzeReceivedData() { + try { + var headerBuffer = new byte[2]; + while (_stream.CanRead && _listener.Active && _client.Connected) { + + _stream.ReadAsync(headerBuffer, 0, headerBuffer.Length).Wait(); + + var dataLength = Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)).Result; + byte[] dataBuffer = new byte[dataLength + 2]; + + // Read data + _stream.ReadAsync(dataBuffer, 0, dataBuffer.Length).Wait(); + + if (isBroadcast(dataBuffer)) { + _broadcastMessage = new BroadcastMessage(dataBuffer); + OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); + } + else if (isKeepAlive(dataBuffer) && INGENICO_GLOBALS.KeepAlive) { + var keepAliveRep = KeepAliveResponse(dataBuffer); + _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); + } + else { // Receiving request response data. + termResponse = dataBuffer; + } + headerBuffer = new byte[2]; + } + } + catch (Exception ex) { + if (_stream.CanRead && _listener.Active && _client.Connected) { + throw new ApiException("Unable to get data from terminal. " + ex.Message); + } + } + } + #endregion + } +} diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs new file mode 100644 index 00000000..51f83d7b --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs @@ -0,0 +1,12 @@ +using System.Net; +using System.Net.Sockets; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class TcpListenerEx : TcpListener { + public TcpListenerEx(IPAddress localaddr, int port) : base(localaddr, port) { + + } + + public new bool Active { get { return base.Active; } } + } +} From 00295395a5380f72e7c01c27a302f68d6e343557 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:11:07 +0800 Subject: [PATCH 019/120] Create methods and their contents/functionalities Create functionalities for the ManageTransaction method; to build request using TerminalManageBuilder. Create BuildManageTransaction and BuildRequestMessage to parse the request and send it to Ingenico based on what request procedure they expect. Create IsObjectNullOrEmpty to identify whether the object is null or empty, ValidateExtendedData to validate the extended data used by the user, ReportRequest to process the request in /XMLReport transactions. --- .../Terminals/INGENICO/IngenicoController.cs | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs new file mode 100644 index 00000000..a7bf7dcb --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -0,0 +1,288 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.PaymentMethods; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Builders; +using GlobalPayments.Api.Terminals.Messaging; +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class IngenicoController : DeviceController { + IDeviceInterface _device; + + public IngenicoController(ITerminalConfiguration settings) : base(settings) { + } + + internal override IDeviceInterface ConfigureInterface() { + if (_device == null) { + _device = new IngenicoInterface(this); + } + return _device; + } + internal override IDeviceCommInterface ConfigureConnector() { + switch (_settings.ConnectionMode) { + case ConnectionModes.SERIAL: + return new IngenicoSerialInterface(_settings); + case ConnectionModes.TCP_IP: + case ConnectionModes.SSL_TCP: + case ConnectionModes.HTTP: + case ConnectionModes.TCP_IP_SERVER: + return new IngenicoTcpInterface(_settings); + default: + throw new NotImplementedException(); + } + } + + #region overrides + internal override ITerminalResponse ManageTransaction(TerminalManageBuilder builder) { + var request = BuildManageTransaction(builder); + + if (builder.TransactionType == TransactionType.Cancel) { + return DoCancelRequest(request); + } + else if (builder.TransactionType == TransactionType.Reversal) { + return DoReverseRequest(request); + } + else { + return DoRequest(request); + } + } + + internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) { + var request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); + return ReportRequest(request); + } + + internal override ITerminalResponse ProcessTransaction(TerminalAuthBuilder builder) { + var request = BuildRequestMessage(builder); + return DoRequest(request); + } + + internal override byte[] SerializeRequest(TerminalAuthBuilder builder) { + return BuildRequestMessage(builder).GetSendBuffer(); + } + + internal override byte[] SerializeRequest(TerminalManageBuilder builder) { + throw new NotImplementedException(); + } + + internal override byte[] SerializeRequest(TerminalReportBuilder builder) { + throw new NotImplementedException(); + } + #endregion + + #region Methods + internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { + var _refNumber = builder.ReferenceNumber; + var _amount = ValidateAmount(builder.Amount); + var _returnRep = 1; + var _paymentMode = ValidatePaymentMode(builder.PaymentMode); + var _paymentType = (int?)((IngenicoInterface)_device).paymentMethod ?? 0; + var _currencyCode = (string.IsNullOrEmpty(builder.CurrencyCode) ? "826" : builder.CurrencyCode); + var _privData = "EXT0100000"; + var _immediateAns = 0; + var _forceOnline = 0; + var _extendedData = "0000000000"; + + // For Auth code value in extended data + if (!string.IsNullOrEmpty(builder.AuthCode)) + _extendedData = ValidateExtendedData(builder.AuthCode, builder.ExtendedDataTags); + else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) + // For Reversal with Transaction Id value in extended data + _extendedData = ValidateExtendedData(builder.TransactionId, ExtendedDataTags.TXN_COMMANDS_PARAMS); + else + _extendedData = ValidateExtendedData(builder.TransactionType.ToString(), ExtendedDataTags.TXN_COMMANDS); + + string message = string + .Format("{0}{1}{2}{3}{4}{5}{6}A01{7}B01{8}{9}", + builder.ReferenceNumber.ToString("00"), + _amount?.ToString("00000000"), + _returnRep, + _paymentMode, + _paymentType, + _currencyCode, + _privData, + _immediateAns, + _forceOnline, + _extendedData + ); + + return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + } + + internal IDeviceMessage BuildRequestMessage(TerminalAuthBuilder builder) { + string message = string.Empty; + if (!IsObjectNullOrEmpty(builder.ReportType)) + message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); + else { + var _referenceNumber = builder.ReferenceNumber; + var _amount = builder.Amount; + var _returnRep = 1; + var _paymentMode = 0; + var _paymentType = (int)((IngenicoInterface)_device).paymentMethod; + var _currencyCode = "826"; + var _privateData = "EXT0100000"; + var _immediateAnswer = 0; + var _forceOnline = 0; + var _extendedData = "0000000000"; + + var _cashbackAmount = builder.CashBackAmount; + var _authCode = builder.AuthCode; + string tableId = builder.TableNumber; + + // Validations + _amount = ValidateAmount(_amount); + _paymentMode = ValidatePaymentMode(builder.PaymentMode); + _currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? _currencyCode : builder.CurrencyCode)); + + if (!string.IsNullOrEmpty(tableId)) { + bool validateTableId = ValidateTableReference(tableId); + if (validateTableId) + _extendedData = ValidateExtendedData(tableId, builder.ExtendedDataTags); + } + + if (!IsObjectNullOrEmpty(_cashbackAmount)) + _extendedData = ValidateExtendedData(_cashbackAmount.ToString(), builder.ExtendedDataTags); + else if (!string.IsNullOrEmpty(_authCode)) + _extendedData = ValidateExtendedData(_authCode, builder.ExtendedDataTags); + + + + message = string.Format("{0}{1}{2}{3}{4}{5}{6}A01{7}B01{8}{9}", + _referenceNumber.ToString("00"), + _amount?.ToString("00000000"), + _returnRep, + _paymentMode, + _paymentType, + _currencyCode, + _privateData, + _immediateAnswer, + _forceOnline, + _extendedData); + } + + return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + } + + #region Validations + private static bool IsObjectNullOrEmpty(object value) { + bool response = false; + if (value.IsNull() || string.IsNullOrWhiteSpace(value.ToString())) + response = true; + else response = false; + + return response; + } + private static bool ValidateTableReference(string value) { + bool response = false; + if (!string.IsNullOrEmpty(value) && value.Length <= 8) + response = true; + else throw new BuilderException("Table number must not be less than or equal 0 or greater than 8 numerics."); + + return response; + } + + private static int ValidatePaymentMode(PaymentMode _paymentMode) { + if (IsObjectNullOrEmpty(_paymentMode)) { + _paymentMode = PaymentMode.APPLICATION; + } + + return (int)_paymentMode; + } + + private static string ValidateExtendedData(string value, ExtendedDataTags tags) { + string extendedData = string.Empty; + switch (tags) { + case ExtendedDataTags.CASHB: + decimal? cashbackAmount = Convert.ToDecimal(value); + if (cashbackAmount > 0m && cashbackAmount < 1000000m) + cashbackAmount *= 100; + else if (cashbackAmount <= 0m) + throw new BuilderException("Cashback Amount must not be in less than or equal 0 value."); + else throw new BuilderException("Cashback Amount exceed."); + + extendedData = "CASHB={0};".FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); + break; + case ExtendedDataTags.AUTHCODE: + extendedData = "AUTHCODE={0}".FormatWith(value); + break; + case ExtendedDataTags.TABLE_NUMBER: + extendedData = "CMD=ID{0}".FormatWith(value); + break; + case ExtendedDataTags.TXN_COMMANDS: + var transType = (TransactionType)Enum.Parse(typeof(TransactionType), value, true); + switch (transType) { + case TransactionType.Cancel: + extendedData = INGENICO_REQ_CMD.CANCEL; + break; + case TransactionType.Duplicate: + extendedData = INGENICO_REQ_CMD.DUPLICATE; + break; + case TransactionType.Reversal: + extendedData = INGENICO_REQ_CMD.REVERSE; + break; + } + break; + case ExtendedDataTags.TXN_COMMANDS_PARAMS: + extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(value); + break; + } + + return extendedData; + } + + private static string ValidateCurrency(string _currencyCode) { + if (!string.IsNullOrWhiteSpace(_currencyCode)) { + _currencyCode = _currencyCode.PadLeft(3, '0'); + } + else _currencyCode = "826"; + + return _currencyCode; + } + + private decimal? ValidateAmount(decimal? _amount) { + if (_amount == null) + throw new BuilderException("Amount can not be null."); + else if (_amount > 0 && _amount < 1000000m) + _amount *= 100; + else if (_amount >= 1000000m) + throw new BuilderException("Amount exceed."); + else + throw new BuilderException("Invalid input amount."); + return _amount; + } + + #endregion + + public ITerminalConfiguration GetConfiguration() { + return _settings; + } + #endregion + + #region Report Request + internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { + var send = Send(request); + return new IngenicoTerminalReportResponse(send); + } + #endregion + + #region Request + internal IngenicoTerminalResponse DoRequest(IDeviceMessage request) { + var response = Send(request); + return new IngenicoTerminalResponse(response); + } + + private CancelResponse DoCancelRequest(IDeviceMessage request) { + var response = Send(request); + return new CancelResponse(response); + } + + private ReverseResponse DoReverseRequest(IDeviceMessage request) { + var response = Send(request); + return new ReverseResponse(response); + } + #endregion + } +} \ No newline at end of file From 56aafa4578bca688028098c89783e198c26de77f Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:12:18 +0800 Subject: [PATCH 020/120] Override the interface methods created in IDeviceInterface and their contents/functionalities attached in DeviceInterface Added PaymentType for the Sale, Refund, Capture, Authorize, Completion, Verify, and returning the GetLastReceipt and GetReport to their bases --- .../Terminals/INGENICO/IngenicoInterface.cs | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs new file mode 100644 index 00000000..eb7ba4ea --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GlobalPayments.Api.Builders; +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.PaymentMethods; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Builders; +using GlobalPayments.Api.Utils; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class IngenicoInterface : DeviceInterface, IDeviceInterface { + internal PaymentType? paymentMethod = null; + internal IngenicoInterface(IngenicoController controller) : base(controller) { + } + + #region Payment Transaction Management + + public override TerminalAuthBuilder Sale(decimal? amount = null) { + paymentMethod = PaymentType.Sale; + return base.Sale(amount); + } + + public override TerminalAuthBuilder Refund(decimal? amount = null) { + paymentMethod = PaymentType.Refund; + return base.Refund(amount); + } + + public override TerminalManageBuilder Capture(decimal? amount = null) { + paymentMethod = PaymentType.CompletionMode; + return base.Capture(amount); + } + + /// + /// Authorize method is Equivalent of Pre-Authorisation from Ingenico + /// + /// + /// + public override TerminalAuthBuilder Authorize(decimal? amount = null) { + paymentMethod = PaymentType.PreAuthMode; + return base.Authorize(amount); + } + + /// + /// Completion method is the Hotel Mode Completion of Ingenico + /// + /// + /// + public override TerminalAuthBuilder Completion(decimal? amount = null) { + paymentMethod = PaymentType.CompletionMode; + return base.Completion(amount); + } + + public override TerminalAuthBuilder Verify() { + paymentMethod = PaymentType.AccountVerification; + return base.Verify(); + } + #endregion + + #region XML & Report Management + public override TerminalAuthBuilder GetReport(ReportType type) { + return base.GetReport(type); + } + + public override TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptType.TICKET) { + return base.GetLastReceipt(type); + } + #endregion + + #region Transaction Management + + public override TerminalManageBuilder Cancel(decimal? amount = null) { + if (amount != null) { + return base.Cancel(amount); + } + else throw new BuilderException("Amount can't be null."); + } + + public override TerminalManageBuilder Reverse(decimal? amount = null) { + if (amount != null) { + return base.Reverse(amount); + } + else throw new BuilderException("Amount can't be null."); + } + + ///// + ///// Duplicate falls under lost transaction recovery and we have mechanisms for this which we'll need to look into further + ///// + public override TerminalManageBuilder Duplicate(decimal? amount = null) { + return base.Duplicate(amount); + } + + #endregion + } +} \ No newline at end of file From f3ca0932b925a003fa79fa4d5fa2664ae616afb5 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:12:34 +0800 Subject: [PATCH 021/120] Create some constant variable Added REPORT and RECEIPT constant variables in INGENICO_REQ_CMD and added the INGENICO_RESP class. Added ReceiptType, ReportType, PaymentType, PaymentMode, and ExtendedDataTags as enum. --- .../Terminals/INGENICO/IngenicoVariables.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs new file mode 100644 index 00000000..82dd883b --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs @@ -0,0 +1,122 @@ + +using System; + +namespace GlobalPayments.Api.Terminals.INGENICO { + internal class INGENICO_REQ_CMD { + public const string CANCEL = "CMD=CANCEL"; + public const string DUPLICATE = "CMD=DUPLIC"; + public const string REVERSE = "CMD=REVERSE"; + public const string REVERSE_WITH_ID = "CMD=REV{0}"; + public const string REPORT = "0100000001100826EXT0100000A010B010CMD={0}"; + public const string RECEIPT = "0100000001100826EXT0100000A010B010CMD={0}"; + } + + internal class INGENICO_GLOBALS { + public const string BROADCAST = "BROADCAST CODE"; + public const string CANCEL = "CMD=CANCEL"; + public const string TID_CODE = "TID CODE"; + public const string KEEP_ALIVE_RESPONSE = " OK"; + public static bool KeepAlive = true; + public const int IP_PORT = 18101; + } + + internal static class INGENICO_RESP { + public readonly static string ACKNOWLEDGE = ((char)ControlCodes.ACK).ToString(); + public readonly static string ENQUIRY = ((char)ControlCodes.ENQ).ToString(); + public readonly static string NOTACKNOWLEDGE = ((char)ControlCodes.NAK).ToString(); + public readonly static string ENDOFTXN = ((char)ControlCodes.EOT).ToString(); + public readonly static string[] XML = { "", "LF" }; + public readonly static string INVALID = "\u0005\u0004"; + public readonly static string ENDXML = ""; + } + + public enum ReceiptType { + TICKET, + SPLITR, + TAXFREE, + REPORT + } + + public enum ReportType { + EOD, + BANKING, + XBAL, + ZBAL + } + + public enum Environment { + TEST, + PRODUCTION + } + public enum TransactionStatus { + SUCCESS = 0, + REFERRAL = 2, + CANCELLED_BY_USER = 6, + FAILED = 7, + RECEIVED = 9 + } + public enum ReverseStatus { + REVERSAL_SUCCESS = 0, + REVERSAL_FAILED = 7, + NOTHING_TO_REVERSE = 9 + } + public enum CancelStatus { + CANCEL_DONE = 9, + CANCE_FAILED = 7 + } + + public enum DynamicCurrencyStatus { + CONVERSION_APPLIED = 1, + REJECTED = 0 + } + public enum TransactionSubTypes { + SPLIT_SALE_TXN = 0x53, // 0x53 byte for 'S' + DCC_TXN = 0x44, // 0x44 byte for 'D' + REFERRAL_RESULT = 0x82 // 0x52 byte for 'R' + } + + public enum ReportTypes { + BANKING, + EOD, + XBAL, + ZBAL + } + public enum TerminalStatus { + NOT_READY = 0, + READY = 1 + } + public enum TerminalModes { + STANDARD_MODE = 0, + VENDING_MODE = 1 + } + + public enum PaymentMethod { + Keyed = 1, + Swiped = 2, + Chip = 3, + Conctactless = 4 + } + + public enum PaymentType { + Sale = 0, + Refund = 1, + CompletionMode = 2, + PreAuthMode = 3, + TaxFreeCreditRefund = 4, + TaxFreeCashRefund = 5, + AccountVerification = 6, + ReferralConfirmation = 9 + } + public enum PaymentMode { + APPLICATION = 0, + MAILORDER = 1 + } + + public enum ExtendedDataTags { + CASHB, + AUTHCODE, + TABLE_NUMBER, + TXN_COMMANDS, + TXN_COMMANDS_PARAMS + } +} \ No newline at end of file From aa5bb8f930e77931c4bc9663e4d01868ddec7546 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:12:51 +0800 Subject: [PATCH 022/120] Serial Communication Added this class to communicate with the terminal/Ingenico using Serial comm. --- .../Interfaces/IngenicoSerialInterface.cs | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs new file mode 100644 index 00000000..3d7d8c10 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -0,0 +1,230 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Messaging; +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace GlobalPayments.Api.Terminals.INGENICO { + internal class IngenicoSerialInterface : IDeviceCommInterface { + public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; + + ITerminalConfiguration _settings; + + private SerialPort _serial; + private bool complete = false, + isResult = false, + isAcknowledge = false, + broadcast = false, + isXML = false; + private string buffer = string.Empty; + private string appendReport = string.Empty; + private List messageResponse; + + public IngenicoSerialInterface(ITerminalConfiguration settings) { + this._settings = settings; + } + + public void Connect() { + if (_serial == null) { + _serial = new SerialPort() { + PortName = "COM{0}".FormatWith(_settings.Port), + BaudRate = (int)_settings.BaudRate, + DataBits = (int)_settings.DataBits, + StopBits = (System.IO.Ports.StopBits)_settings.StopBits, + Parity = (System.IO.Ports.Parity)_settings.Parity, + Handshake = (Handshake)_settings.Handshake, + RtsEnable = true, + DtrEnable = true, + ReadTimeout = _settings.Timeout + }; + + if (!_serial.IsOpen) { + _serial.DataReceived += new SerialDataReceivedEventHandler(_serial_DataReceived); + _serial.Open(); + } else throw new MessageException("Serial Port is already open."); + } + } + + private void _serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { + SerialPort serial = (SerialPort)sender; + do { + Thread.Sleep(0100); + buffer = serial.ReadExisting(); + + if (!string.IsNullOrEmpty(buffer)) { + _serial.ReadTimeout = _settings.Timeout; + + if (buffer.Equals(INGENICO_RESP.ACKNOWLEDGE)) { + isAcknowledge = true; + break; + } else if (buffer.Equals(INGENICO_RESP.ENQUIRY)) { + _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + break; + } else if (buffer.Contains(INGENICO_GLOBALS.BROADCAST)) { + broadcast = true; + break; + } else if (buffer.Equals(INGENICO_RESP.NOTACKNOWLEDGE)) { + isAcknowledge = false; + break; + } else if (INGENICO_RESP.XML.Any(buffer.Contains)) { + isXML = true; + break; + } else { + if (!buffer.Contains(INGENICO_GLOBALS.BROADCAST) && !buffer.Contains(INGENICO_RESP.INVALID) + && !INGENICO_RESP.XML.Any(buffer.Contains) && !buffer.Contains(INGENICO_RESP.ENDOFTXN) + && !buffer.Contains(INGENICO_RESP.NOTACKNOWLEDGE)) + isResult = true; + break; + } + } + } while (true); + } + + public void Disconnect() { + _serial?.Dispose(); + _serial.Close(); + _serial = null; + buffer = string.Empty; + appendReport = string.Empty; + isResult = false; + complete = false; + isAcknowledge = false; + broadcast = false; + isXML = false; + } + + private bool ValidateResponseLRC(string calculate, string actual) { + bool response = false; + + byte[] calculateLRC = TerminalUtilities.CalculateLRC(calculate); + byte[] actualLRC = TerminalUtilities.CalculateLRC(actual); + + if (BitConverter.ToString(actualLRC) == BitConverter.ToString(calculateLRC)) + response = true; + + return response; + } + + private async Task WriteMessage(IDeviceMessage message) { + return await Task.Run(() => { + try { + int enquiryCount = 0; + messageResponse = new List(); + + if (_serial == null) + return false; + + do { + _serial.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); + if (isAcknowledge) { + do { + byte[] msg = message.GetSendBuffer(); + foreach (byte b in msg) { + byte[] _b = new byte[] { b }; + _serial.Write(_b, 0, 1); + } + + if (isAcknowledge) { + _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + break; + } + } while (true); + + do { + if (broadcast) { + byte[] bMsg = Encoding.ASCII.GetBytes(buffer); + BroadcastMessage broadcastMsg = new BroadcastMessage(bMsg); + OnBroadcastMessage?.Invoke(broadcastMsg.Code, broadcastMsg.Message); + broadcast = false; + } + + if (isXML) { + do { + appendReport += buffer; + if (appendReport.Contains(INGENICO_RESP.ENDXML)) { + string xmlData = appendReport.Substring(1, appendReport.Length - 3); + if (MessageReceived(xmlData)) { + _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + complete = true; + } + } + Thread.Sleep(0500); + } while (!complete); + } + + if (isResult) { + string check = Encoding.UTF8.GetString(message.GetSendBuffer()); + if (buffer.Contains(check.Substring(0, 2))) { + do { + string rData = buffer.Substring(1, buffer.Length - 3); + buffer = buffer.Substring(1, buffer.Length - 3); + bool validateLRC = ValidateResponseLRC(rData, buffer); + if (validateLRC) { + if (MessageReceived(rData)) { + _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + complete = true; + } + } + } while (!complete); + } + } + if (complete) + break; + } while (true); + break; + } else { + Thread.Sleep(1000); + _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + enquiryCount++; + + if (enquiryCount.Equals(3)) { + throw new MessageException("Terminal did not respond in Enquiry for three (3) times. Send aborted."); + } + } + } while (true); + + return complete; + } + catch (MessageException e) { + throw new MessageException(e.Message); + } + }); + } + + public byte[] Send(IDeviceMessage message) { + Connect(); + try { + if (_serial != null) { + string bufferSend = Encoding.ASCII.GetString(message.GetSendBuffer()); + OnMessageSent?.Invoke(bufferSend.Substring(1, bufferSend.Length - 3)); + var task = WriteMessage(message); + if (!task.Wait(_settings.Timeout)) { + throw new MessageException("Terminal did not response within timeout."); + } + string test = Encoding.ASCII.GetString(messageResponse.ToArray()); + return messageResponse.ToArray(); + } else throw new MessageException("Terminal not connected."); + } + finally { + if (_serial != null) { + Disconnect(); + } + } + } + + private bool MessageReceived(string messageData) { + if (messageResponse == null) + return false; + foreach (char b in messageData) + messageResponse.Add((byte)b); + return true; + } + } +} \ No newline at end of file From 9ec4b92c20c6cb253d5806e331beb86f7411464f Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:13:17 +0800 Subject: [PATCH 023/120] Create BroadcastMessage response class Create Dictionary of all broadcast code and its message meaning. Parsed response XML data of terminal. Set code and message to broadcast's properties. --- .../INGENICO/Responses/BroadcastMessage.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs new file mode 100644 index 00000000..8c174570 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs @@ -0,0 +1,60 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Terminals.INGENICO; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class BroadcastMessage { + private byte[] _buffer; + private string _code; + private string _message; + public string Code { + get { return _code; } + } + public string Message { + get { return _message; } + } + + private Dictionary _broadcastData = new Dictionary { + {"A0", "CONNECTING" }, + {"A1", "CONNECTION MADE" }, + {"A2", "APPROVED" }, + {"A3", "DECLINED" }, + {"A4", "INSERT CARD" }, + {"A5", "CARD ERROR" }, + {"A6", "PROCESSING ERROR" }, + {"A7", "REMOVE CARD" }, + {"A8", "TRY AGAIN" }, + {"A9", "PRESENT CARD" }, + {"AA", "RE-PRESENT CARD" }, + {"AB", "CARD NOT SUPPORTED" }, + {"AC", "PRESENT ONLY ONE CARD" }, + {"AD", "PLEASE WAIT" }, + {"AE", "BAD SWIPE" }, + {"AF", "CARD EXPIRED" }, + {"B0", "DECLINED BY CARD" }, + {"B1", "PIN ENTRY" }, + {"B2", "CASHBACK AMOUNT ENTRY" }, + {"B3", "PAPER OUT" }, + }; + + public BroadcastMessage(byte[] buffer) { + _buffer = buffer; + ParseBroadcast(_buffer); + } + + private void ParseBroadcast(byte[] broadBuffer) { + if (broadBuffer.Length > 0) { + var strBroadcast = ASCIIEncoding.UTF8.GetString(broadBuffer); + int findIndex = strBroadcast.IndexOf(INGENICO_GLOBALS.BROADCAST); + int findLen = 14 + 2; // additional 2 is for extra char '="' + _code = strBroadcast.Substring(findIndex + findLen, 2); + _message = _broadcastData[_code]; + } + else { + throw new MessageException("No broadcast message."); + } + } + } +} From e27e85e863823a4d8412252efc8dd6cb8dc6ffbd Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:13:51 +0800 Subject: [PATCH 024/120] Handle Cancel response Create cancel response and inherits IgenicoTerminalResponse. Override ParseResponse so that status will change to Cancel specific status. --- .../INGENICO/Responses/CancelResponse.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs new file mode 100644 index 00000000..2d69f371 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs @@ -0,0 +1,18 @@ +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class CancelResponse : IngenicoTerminalResponse, IDeviceResponse{ + public CancelResponse(byte[] buffer) : base(buffer) { + ParseResponse(buffer); + } + + public override void ParseResponse(byte[] response) { + base.ParseResponse(response); + Status = ((CancelStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); + } + } +} From 51ce2c8d7a1dc3c0740a9ab020797af93e97290c Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:14:08 +0800 Subject: [PATCH 025/120] Handle Reverse response Create reverse response and inherits IgenicoTerminalResponse. Override ParseResponse so that status will change to Reverse specific status. --- .../INGENICO/Responses/ReverseResponse.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs new file mode 100644 index 00000000..3c4aee88 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs @@ -0,0 +1,18 @@ +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class ReverseResponse : IngenicoTerminalResponse, IDeviceResponse{ + public ReverseResponse(byte[] buffer) : base(buffer) { + ParseResponse(buffer); + } + + public override void ParseResponse(byte[] response) { + base.ParseResponse(response); + Status = ((ReverseStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); + } + } +} From 97232a2fb2e83be35b6853b3fa22a2017f8ec7ac Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:14:30 +0800 Subject: [PATCH 026/120] Create data response class Create Class to handle data from terminal which is in Rep field Create property for each data to be parsed. Create method for parsing each data in Rep field. --- .../INGENICO/Responses/DataResponse.cs | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs new file mode 100644 index 00000000..a13e2f0d --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Utils; +using System.Linq; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public class DataResponse { + + private string _authCode; + private decimal? _finalAmount; + private PaymentMethod? _paymentMethod; + private decimal? _cashbackAmount; + private decimal? _gratuityAmount; + private decimal? _availableAmount; + private string _dccCode; + private decimal? _dccAmount; + private TransactionSubTypes? _txnSubType; + private decimal? _splitSaleAmount; + private DynamicCurrencyStatus? _dccStatus; + + private byte[] _buffer; + + #region Byte Code + // For less memory allocation; + private byte _C = 67; + private byte _Z = 90; + private byte _Y = 89; + private byte _M = 77; + private byte _A = 65; + private byte _U = 85; + private byte _O = 79; + private byte _P = 80; + private byte _T = 84; + private byte _S = 83; + private byte _D = 68; + #endregion + + + public DataResponse(byte[] buffer) { + _buffer = buffer; + ParseData(); + } + + #region Property Fields + + public string AuthorizationCode { + get { return _authCode ?? ""; } + private set { } + } + + public decimal? FinalAmount { + get { return _finalAmount.ToString().ToAmount(); } + set { _finalAmount = value; } + } + + public PaymentMethod? PaymentMethod { + get { return _paymentMethod; } + set { _paymentMethod = value; } + } + + public decimal? CashbackAmount { + get { return _cashbackAmount.ToString().ToAmount(); } + set { _cashbackAmount = value; } + } + + public decimal? GratuityAmount { + get { return _gratuityAmount.ToString().ToAmount(); } + set { _gratuityAmount = value; } + } + + public decimal? AvailableAmount { + get { return _availableAmount.ToString().ToAmount(); } + set { _availableAmount = value; } + } + public string DccCode { + get { return _dccCode; } + set { _dccCode = value; } + } + + public decimal? DccAmount { + get { return _dccAmount.ToString().ToAmount(); } + set { _dccAmount = value; } + } + + public TransactionSubTypes? TransactionSubType { + get { return _txnSubType; } + set { _txnSubType = value; } + } + + public decimal? SplitSaleAmount { + get { return _splitSaleAmount.ToString().ToAmount(); } + set { _splitSaleAmount = value; } + } + + public DynamicCurrencyStatus? DccStatus { + get { return _dccStatus; } + set { _dccStatus = value; } + } + + #endregion + + /** + * C = AuthCode + * Z = Cashback Amount + * Y = Gratuity Amount + * M = Final Transaction Amount + * A = Available Amount + * U = DCC Currency + * O = DCC Converted transaction amount + * P = Payment Method + * T = Transaction Sub-Type + * S = Split Sale Paid Amount + * D = DCC Operation Status + */ + + private void ParseData() { + + _authCode = (string)GetValueOfRespField(_C, typeof(string)); + _cashbackAmount = (decimal?)GetValueOfRespField(_Z, typeof(decimal?)); + _gratuityAmount = (decimal?)GetValueOfRespField(_Y, typeof(decimal?)); + _finalAmount = (decimal?)GetValueOfRespField(_M, typeof(decimal?)); + _availableAmount = (decimal?)GetValueOfRespField(_A, typeof(decimal?)); + _dccCode = (string)GetValueOfRespField(_U, typeof(string)); + _dccAmount = (decimal?)GetValueOfRespField(_O, typeof(decimal?)); + _txnSubType = (TransactionSubTypes?)GetValueOfRespField(_T, typeof(TransactionSubTypes?)); + _dccStatus = (DynamicCurrencyStatus?)GetValueOfRespField(_D, typeof(DynamicCurrencyStatus?)); + _splitSaleAmount = (decimal?)GetValueOfRespField(_S, typeof(decimal?)); + _paymentMethod = (PaymentMethod?)GetValueOfRespField(_P, typeof(PaymentMethod?)); + } + + private object GetValueOfRespField(byte toGet, Type returnType) { + var index = Array.FindIndex(_buffer, e => e == toGet); + if (index >= 0) { + // Get the length based on Documention (TLV). + byte[] lengthBuffer = { _buffer[index + 1], _buffer[index + 2] }; + var length = Convert.ToInt32(Encoding.UTF8.GetString(lengthBuffer, 0, lengthBuffer.Length), 16); + + var _arrValue = _buffer.SubArray(index + 3, length); ; + var endLength = index + length + 3; + _buffer = _buffer.SubArray(0, index).Concat(_buffer.SubArray(endLength, _buffer.Length - endLength)).ToArray(); + var strValue = Encoding.ASCII.GetString(_arrValue, 0, _arrValue.Length); + + + if (returnType == typeof(decimal?)) { + return decimal.Parse(strValue); + } + else if (returnType == typeof(string)) { + return strValue; + } + else if (returnType == typeof(TransactionSubTypes?)) { + return (TransactionSubTypes)int.Parse(Convert.ToInt64(_arrValue[0]).ToString("X2"), System.Globalization.NumberStyles.HexNumber); + } + else if (returnType == typeof(DynamicCurrencyStatus?)) { + return (DynamicCurrencyStatus)int.Parse(strValue); + } + else if (returnType == typeof(PaymentMethod?)) { + return (PaymentMethod)int.Parse(strValue); + } + else + throw new Exception("Data type not supported in parsing of response data."); + } + return null; + } + } +} \ No newline at end of file From ab268d14519e7ae15e88bf0f571edd3b9840909f Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:14:43 +0800 Subject: [PATCH 027/120] Added DeviceResponse class Create IgenicoBaseResponse that inherits IDeviceResponse. Create IngenicoTerminalResponse that inherits IngenicoBaseResponse. Create method for parsing data. --- .../INGENICO/Responses/DeviceResponse.cs | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs new file mode 100644 index 00000000..2572ac6f --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -0,0 +1,127 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Extensions; +using System.IO; +using System.Text; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Utils; + +namespace GlobalPayments.Api.Terminals.INGENICO { + public abstract class IngenicoBaseResponse : IDeviceResponse { + public string Status { get; set; } + public string Command { get; set; } + public string Version { get; set; } + public string DeviceResponseCode { get; set; } + public string DeviceResponseText { get; set; } + public string ReferenceNumber { get; set; } + } + + public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse { + + internal string _transactionStatus; + internal decimal _amount; + internal PaymentMode _paymentMode; + internal string _privateData; + internal string terminalRawData; + internal string _currencyCode; + internal DataResponse _respField; + + internal IngenicoTerminalResponse(byte[] buffer) { + ParseResponse(buffer); + } + + #region Added Properties Specific for Ingenico + public string DccCurrency { get { return _respField.DccCode; } set { } } + public DynamicCurrencyStatus? DccStatus { get { return _respField.DccStatus; } set { } } + public TransactionSubTypes? TransactionSubType { get { return _respField.TransactionSubType; } set { } } + public decimal SplitSaleAmount { get { return 0; } set { } } + public PaymentMode PaymentMode { get { return _paymentMode; } set { } } + public string DynamicCurrencyCode { get { return _respField.DccCode; } } + public string CurrencyCode { get { return _currencyCode; } set { } } + public string PrivateData { get { return _privateData; } } + public decimal? FinalTransactionAmount { get { return _respField.FinalAmount; } } + #endregion + + #region Properties + public string ResponseText { get { return terminalRawData; } set { } } + public decimal? TransactionAmount { get { return _amount.ToString().ToAmount(); } set { } } + public decimal? BalanceAmount { get { return _respField.AvailableAmount; } set { } } + public string AuthorizationCode { get { return _respField.AuthorizationCode ?? ""; } set { } } + public decimal? TipAmount { get { return _respField.GratuityAmount; } set { } } + public decimal? CashBackAmount { get { return _respField.CashbackAmount; } set { } } + public string PaymentType { get { return _respField.PaymentMethod.ToString(); } set { } } + public string TerminalRefNumber { get { return ReferenceNumber; } set { } } + + public string ResponseCode { get; set; } + public string TransactionId { get; set; } + public string Token { get; set; } + public string SignatureStatus { get; set; } + public byte[] SignatureData { get; set; } + public string TransactionType { get; set; } + public string MaskedCardNumber { get; set; } + public string EntryMethod { get; set; } + public string ApprovalCode { get; set; } + public decimal? AmountDue { get; set; } + public string CardHolderName { get; set; } + public string CardBIN { get; set; } + public bool CardPresent { get; set; } + public string ExpirationDate { get; set; } + public string AvsResponseCode { get; set; } + public string AvsResponseText { get; set; } + public string CvvResponseCode { get; set; } + public string CvvResponseText { get; set; } + public bool TaxExempt { get; set; } + public string TaxExemptId { get; set; } + public string TicketNumber { get; set; } + public ApplicationCryptogramType ApplicationCryptogramType { get; set; } + public string ApplicationCryptogram { get; set; } + public string CardHolderVerificationMethod { get; set; } + public string TerminalVerificationResults { get; set; } + public string ApplicationPreferredName { get; set; } + public string ApplicationLabel { get; set; } + public string ApplicationId { get; set; } + #endregion + + /** Index + 0 - 1 = Epos Number + 2 - Transaction Status + 3 - 10 = Amount + 11 = Payment Mode + 12 - 20 = AuthCode or starts with (C) + 21 - 34 = Final Transaction Amount or starts with (M) + 35 - 38 = Payment Method or starts with (P) + 67 - 69 = Currency + 70 - 79 = Private Data + */ + public virtual void ParseResponse(byte[] response) { + if (response != null) { + ReferenceNumber = Encoding.UTF8.GetString(response.SubArray(0, 2)); + _transactionStatus = ((TransactionStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); + decimal.TryParse(Encoding.UTF8.GetString(response.SubArray(3, 8)), out _amount); + _paymentMode = (PaymentMode)Encoding.UTF8.GetString(response.SubArray(11, 1)).ToInt32(); + _respField = new DataResponse(response.SubArray(12, 55)); + _currencyCode = Encoding.UTF8.GetString(response.SubArray(67, 3)); + _privateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); + + Status = _transactionStatus; + } + } + } + + public class IngenicoTerminalReportResponse : IngenicoBaseResponse, ITerminalReport { + private byte[] _buffer; + private string _reportData; + + internal IngenicoTerminalReportResponse(byte[] buffer) { + _buffer = buffer; + _reportData = ASCIIEncoding.ASCII.GetString(_buffer); + Status = _buffer.Length > 0 ? "SUCCESS" : "FAILED"; + } + + public string ReportData { get { return _reportData;} set { } } + + public override string ToString() { + return Encoding.ASCII.GetString(_buffer); + } + } +} \ No newline at end of file From 52d8d8153ef6da81d89eff41704c98fb4dede868 Mon Sep 17 00:00:00 2001 From: "peter.yanong.serino" Date: Tue, 3 Mar 2020 23:16:46 +0800 Subject: [PATCH 028/120] Ingenico Terminal Test Create test for each Payment Transaction type using either TCP or Serial connection mode. Create test for each Transcation Management using either TCP or Serial connection mode --- .../Ingenico/IngenicoTransactionTests.cs | 112 +++++++++++ .../Ingenico/SaleTransactionManagement.cs | 140 +++++++++++++ .../Ingenico/SerialPaymentTransactions.cs | 184 ++++++++++++++++++ .../Terminals/Ingenico/TransactionTest.cs | 94 +++++++++ 4 files changed, 530 insertions(+) create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs new file mode 100644 index 00000000..f5bd9b28 --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.INGENICO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class IngenicoTransactionTests { + IDeviceInterface _device; + + public IngenicoTransactionTests() { + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = Entities.DeviceType.INGENICO, + ConnectionMode = ConnectionModes.TCP_IP_SERVER, + Port = "18101", + Timeout = 60000, + RequestIdProvider = new RandomIdProvider() + }); + Assert.IsNotNull(_device); + } + + [TestMethod] + public void SaleTest() { + try { + _device.Dispose(); + Thread.Sleep(25000); + + var resp = _device.Sale(15m) + .WithCashBack(2m) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(resp); + Assert.IsNotNull(resp.AuthorizationCode); + } + catch (Exception ex) { + Assert.Fail(ex.Message); + } + finally { + _device.Dispose(); + } + } + + [TestMethod] + public void CancelTest() { + + var tsk1 = Task.Factory.StartNew(() => { + var respSale = _device.Sale(15.12m) + .WithCashBack(3m) + .WithReferenceNumber(02) + .Execute(); + + Assert.IsNotNull(respSale); + }); + + var tsk2 = Task.Factory.StartNew(() => { + Thread.Sleep(7000); + + var respCancel = _device.Cancel(15.12m) + .WithReferenceNumber(03) + .Execute(); + + Assert.IsNotNull(respCancel); + }); + + Task.WaitAll(tsk1, tsk2); + } + + [TestMethod] + public void ReverseTest() { + + var resSale = _device.Sale(125.12m) + .WithReferenceNumber(55) + .Execute(); + + Thread.Sleep(10000); + + if (resSale != null) { + var resp = _device.Reverse(amount: 6.18m) + .WithReferenceNumber(12) + .Execute(); + + var termId = resp.TerminalRefNumber; + + Assert.IsNotNull(termId); + Assert.IsNotNull(resp); + } + else Assert.IsNull(resSale); + } + + [TestMethod] + public void DuplicateTest() { + var resp = _device.Duplicate(); + Assert.IsNotNull(resp); + } + + [TestMethod] + public void ParsingRawResponseTest() { + string res = "09000015120 826CANCELDONE"; + byte[] buffers = Encoding.ASCII.GetBytes(res); + + var pResp = new DataResponse(ASCIIEncoding.ASCII.GetBytes(res.Substring(12, 55))); + + Assert.IsNotNull(pResp); + } + } +} diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs new file mode 100644 index 00000000..3a97d3de --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class PaymentTransactionManagement { + IDeviceInterface _device; + + public PaymentTransactionManagement() { + + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = DeviceType.INGENICO, + ConnectionMode = ConnectionModes.TCP_IP_SERVER, + Port = "18101", + Timeout = 30000, + RequestIdProvider = new RandomIdProvider() + }); + + Assert.IsNotNull(_device); + } + + [TestMethod] + public void SaleTest1() { + var response = _device.Sale(12m) + .WithReferenceNumber(15) + .WithCashBack(10m) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(response); + } + + + [TestMethod] + public void SaleTest() { + _device.OnMessageSent += (message) => { + Assert.IsNotNull(message); + }; + + ITerminalResponse response = _device.Sale(5.12m) + .WithPaymentMode(PaymentMode.MAILORDER) + .WithCashBack(7.569m) + .WithReferenceNumber(1) + .Execute(); + + var authCode = response.AuthorizationCode; + + Assert.IsNotNull(response.AuthorizationCode); + } + + [TestMethod] + public void RefundTest() { + var response = _device.Refund(1m) + .WithPaymentMethodType(PaymentMethodType.Credit) + .WithReferenceNumber(1) + .Execute(); + + Assert.IsNotNull(response.AuthorizationCode); + } + + + [TestMethod] + public void SaleNegativeTest() { + var response = _device.Sale(5.12m) + .WithCashBack(7.52m) + .WithReferenceNumber(1) + .Execute(); + + Assert.IsNull(response.AuthorizationCode); + } + + [TestMethod] + public void SaleTWithOnMessageSentest() { + _device.OnBroadcastMessage += (code, message) => { + Assert.IsNull(code); + Assert.IsNull(message); + }; + + + var response = _device.Refund(5.12m) + //.WithCashBack(7.5m) + .WithReferenceNumber(1) + .Execute(); + + Assert.IsNotNull(response.AuthorizationCode); + } + + [TestMethod] + public void CancelTest() { + try { + _device.Cancel(); + } + catch (ApiException ex) { + Assert.Fail(ex.Message); + } + } + + [TestMethod] + public void AsyncCancelTest() { + var Task1 = Task.Factory.StartNew(() => { + var response = _device.Sale(5.12m) + .WithCashBack(7.5m) + .WithReferenceNumber(1) + .Execute(); + + Assert.IsNotNull(response); + }); + var Task2 = Task.Factory.StartNew(() => { + try { + Thread.Sleep(5000); + _device.Cancel(); + } + catch (Exception ex) { + Assert.Fail(ex.Message); + } + }); + + Task.WaitAll(Task1, Task2); + } + + + [TestMethod] + public void GetLastReceiptTest() { + var response = _device + .GetLastReceipt() + .Execute(); + + Assert.IsNotNull(response.ReportData); + } + } +} diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs new file mode 100644 index 00000000..a96e5ffe --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs @@ -0,0 +1,184 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using ReportType = GlobalPayments.Api.Terminals.INGENICO.ReportType; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class SerialPaymentTransactions { + IDeviceInterface _device; + + public SerialPaymentTransactions() { + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = DeviceType.INGENICO, + ConnectionMode = ConnectionModes.SERIAL, + Port = "5", + BaudRate = BaudRate.r9600, + DataBits = DataBits.Seven, + StopBits = StopBits.One, + Parity = Parity.Even, + Handshake = System.IO.Ports.Handshake.None, + Timeout = 65000, + RequestIdProvider = new RandomIdProvider() + }); + + Assert.IsNotNull(_device); + } + + [TestMethod] + public void CaptureTest() { + var response = _device.Capture(6.18m) + .WithReferenceNumber(1) + .WithCurrencyCode("826") + .WithTransactionId("011223") + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void SaleTest() { + var response = _device.Sale(6.18m) + .WithReferenceNumber(1) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .WithTableNumber("1") + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void Reverse() { + var response = _device.Reverse(amount: 6.18m) + .WithReferenceNumber(12) + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void SaleRefund() { + var response = _device.Refund(6.18m) + .WithReferenceNumber(1) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .WithTableNumber("1") + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void preAuth() { + var response = _device.Authorize(20.00m) + .WithReferenceNumber(1) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void SaleCancel() { + var task1 = Task.Factory.StartNew(() => { + var response = _device.Sale(523m) + .WithReferenceNumber(1) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(response); + }); + + var task2 = Task.Factory.StartNew(() => { + Thread.Sleep(10000); + _device.Cancel(); + }); + + Task.WaitAll(task1, task2); + } + + [TestMethod] + public void TaxFree() { + + var splitR = _device + .GetLastReceipt(ReceiptType.SPLITR) + .Execute(); + + string test = splitR.ReportData; + + if (splitR.ReportData.Contains("")) + Assert.IsNotNull(splitR); + } + + [TestMethod] + public void Ticket() { + ITerminalReport res = _device + .GetLastReceipt(ReceiptType.TICKET) + .Execute(); + + string test = res.ReportData; + + if (res.ReportData.Contains("")) + Assert.IsNotNull(res); + } + + [TestMethod] + public void EOD() { + + /** This example doesn't return XML/Report Data but it intiate End of Day + Report and the terminal will return EODOK if success. + */ + ITerminalResponse res = _device + .GetReport(ReportType.EOD) + .Execute(); + + Assert.IsNotNull(res); + } + + [TestMethod] + public void Cashback() { + var response = _device.Sale(20.00m) + .WithReferenceNumber(01) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .WithCashBack(2.00m) + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void AccountVerification() { + + var response = _device.Verify() + .WithReferenceNumber(01) + .WithPaymentMode(PaymentMode.MAILORDER) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(response); + } + + [TestMethod] + public void test123() { + var response = _device.Verify() + .WithReferenceNumber(01) + .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithCurrencyCode("826") + .Execute(); + + Assert.IsNotNull(response); + } + } +} diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs new file mode 100644 index 00000000..131e4dc2 --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.INGENICO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class TransactionTest { + IDeviceInterface _device; + + public TransactionTest() { + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = Entities.DeviceType.INGENICO, + ConnectionMode = ConnectionModes.TCP_IP_SERVER, + Port = "18101", + Timeout = 60000, + RequestIdProvider = new RandomIdProvider() + }); + Assert.IsNotNull(_device); + } + + + [TestMethod] + public void AsyncCancelTest() { + var tsk1 = Task.Factory.StartNew(() => { + var respSale = _device.Sale(15.12m) + .WithCashBack(3m) + .WithReferenceNumber(02) + .Execute(); + + Assert.IsNotNull(respSale); + }); + + var tsk2 = Task.Factory.StartNew(() => { + Thread.Sleep(7000); + + var respCancel = _device.Cancel(15.12m) + .WithReferenceNumber(03) + .Execute(); + + Assert.IsNotNull(respCancel); + Assert.AreEqual(respCancel.Status, "CANCEL_DONE"); + }); + + Thread.Sleep(10000); + _device.Dispose(); + + Task.WaitAll(tsk1, tsk2); + } + + [TestMethod] + public void ReverseTest() { + + Thread.Sleep(10000); + + var resSale = _device.Sale(125.12m) + .WithReferenceNumber(55) + .Execute(); + + Thread.Sleep(10000); + + if (resSale != null) { + var resp = _device.Reverse(amount: 6.18m) + .WithReferenceNumber(12) + .Execute(); + + var termId = resp.TerminalRefNumber; + + Assert.IsNotNull(termId); + Assert.AreEqual(resp.Status, "REVERSAL_SUCCESS"); + } + else Assert.IsNull(resSale); + } + + [TestMethod] + public void DuplicTest() { + + var duplicate = _device.Duplicate(12.5m) + .WithReferenceNumber(39) + .Execute(); + + _device.Dispose(); + Assert.IsNotNull(duplicate); + Assert.AreEqual(duplicate.TransactionAmount, 12.5m); + } + } +} From 7f59e0682f4860e17f96fc5123f420c8344e9b6a Mon Sep 17 00:00:00 2001 From: peter-yanong Date: Fri, 27 Mar 2020 22:31:13 +0800 Subject: [PATCH 029/120] Update device type Make Device type more descriptive and update Controller configuration --- src/GlobalPayments.Api/Entities/Enums.cs | 16 +++++++++++++--- .../Terminals/ConnectionConfig.cs | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs index d8dd744f..fd138758 100644 --- a/src/GlobalPayments.Api/Entities/Enums.cs +++ b/src/GlobalPayments.Api/Entities/Enums.cs @@ -57,10 +57,20 @@ public enum DeviceType { /// GENIUS, - /// - /// Indicates a Ingenico terminal + /// + /// Indicates an Ingenico Desk/5000 terminal underlying in Epos software package. + /// + Ingenico_EPOS_Desk5000, + + /// + /// Indicates an Ingenico Lane/3000 terminal underlying in Epos software package. + /// + Ingenico_EPOS_Lane3000, + + /// + /// Indicates an Ingenico Move/3500 terminal underlying in Epos software package. /// - INGENICO + Ingenico_EPOS_Move3500 } /// diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs index 6294f614..7321baa0 100644 --- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs +++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs @@ -92,7 +92,9 @@ internal override void ConfigureContainer(ConfiguredServices services) { //case DeviceType.GENIUS: //services.DeviceController = new GeniusController(this); //break; - case DeviceType.INGENICO: + case DeviceType.Ingenico_EPOS_Desk5000: + case DeviceType.Ingenico_EPOS_Lane3000: + case DeviceType.Ingenico_EPOS_Move3500: services.DeviceController = new IngenicoController(this); break; default: From cef3293215121bb8848f243b33bb4985581d2710 Mon Sep 17 00:00:00 2001 From: peter-yanong Date: Sat, 28 Mar 2020 18:09:30 +0800 Subject: [PATCH 030/120] Ingenico Controller Add brackets to single line if statements. Change var to exact data type for ambiguous data. Use StringBuilder class for building mass string data instead of string format method --- .../Terminals/INGENICO/IngenicoController.cs | 230 +++++++++--------- 1 file changed, 121 insertions(+), 109 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index a7bf7dcb..7e2f8406 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -1,11 +1,8 @@ using GlobalPayments.Api.Entities; -using GlobalPayments.Api.PaymentMethods; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; -using GlobalPayments.Api.Terminals.Messaging; using GlobalPayments.Api.Utils; using System; -using System.Collections.Generic; using System.Text; namespace GlobalPayments.Api.Terminals.INGENICO { @@ -21,6 +18,7 @@ internal override IDeviceInterface ConfigureInterface() { } return _device; } + internal override IDeviceCommInterface ConfigureConnector() { switch (_settings.ConnectionMode) { case ConnectionModes.SERIAL: @@ -37,7 +35,7 @@ internal override IDeviceCommInterface ConfigureConnector() { #region overrides internal override ITerminalResponse ManageTransaction(TerminalManageBuilder builder) { - var request = BuildManageTransaction(builder); + IDeviceMessage request = BuildManageTransaction(builder); if (builder.TransactionType == TransactionType.Cancel) { return DoCancelRequest(request); @@ -51,21 +49,21 @@ internal override ITerminalResponse ManageTransaction(TerminalManageBuilder buil } internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) { - var request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); + IDeviceMessage request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); return ReportRequest(request); } internal override ITerminalResponse ProcessTransaction(TerminalAuthBuilder builder) { - var request = BuildRequestMessage(builder); + IDeviceMessage request = BuildProcessTransaction(builder); return DoRequest(request); } internal override byte[] SerializeRequest(TerminalAuthBuilder builder) { - return BuildRequestMessage(builder).GetSendBuffer(); + return BuildProcessTransaction(builder).GetSendBuffer(); } internal override byte[] SerializeRequest(TerminalManageBuilder builder) { - throw new NotImplementedException(); + return BuildManageTransaction(builder).GetSendBuffer(); } internal override byte[] SerializeRequest(TerminalReportBuilder builder) { @@ -75,121 +73,135 @@ internal override byte[] SerializeRequest(TerminalReportBuilder builder) { #region Methods internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { - var _refNumber = builder.ReferenceNumber; - var _amount = ValidateAmount(builder.Amount); - var _returnRep = 1; - var _paymentMode = ValidatePaymentMode(builder.PaymentMode); - var _paymentType = (int?)((IngenicoInterface)_device).paymentMethod ?? 0; - var _currencyCode = (string.IsNullOrEmpty(builder.CurrencyCode) ? "826" : builder.CurrencyCode); - var _privData = "EXT0100000"; - var _immediateAns = 0; - var _forceOnline = 0; - var _extendedData = "0000000000"; + int refNumber = builder.ReferenceNumber; + decimal? amount = ValidateAmount(builder.Amount); + int returnRep = 1; + int paymentMode = ValidatePaymentMode(builder.PaymentMode); + int? paymentType = (int?)((IngenicoInterface)_device).paymentMethod ?? 0; + string currencyCode = (string.IsNullOrEmpty(builder.CurrencyCode) ? "826" : builder.CurrencyCode); + string privData = "EXT0100000"; + int immediateAns = 0; + int forceOnline = 0; + string extendedData = "0000000000"; // For Auth code value in extended data if (!string.IsNullOrEmpty(builder.AuthCode)) - _extendedData = ValidateExtendedData(builder.AuthCode, builder.ExtendedDataTags); + extendedData = ValidateExtendedData(builder.AuthCode, builder.ExtendedDataTags); else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) // For Reversal with Transaction Id value in extended data - _extendedData = ValidateExtendedData(builder.TransactionId, ExtendedDataTags.TXN_COMMANDS_PARAMS); + extendedData = ValidateExtendedData(builder.TransactionId, ExtendedDataTags.TXN_COMMANDS_PARAMS); else - _extendedData = ValidateExtendedData(builder.TransactionType.ToString(), ExtendedDataTags.TXN_COMMANDS); - - string message = string - .Format("{0}{1}{2}{3}{4}{5}{6}A01{7}B01{8}{9}", - builder.ReferenceNumber.ToString("00"), - _amount?.ToString("00000000"), - _returnRep, - _paymentMode, - _paymentType, - _currencyCode, - _privData, - _immediateAns, - _forceOnline, - _extendedData - ); - - return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + extendedData = ValidateExtendedData(builder.TransactionType.ToString(), ExtendedDataTags.TXN_COMMANDS); + + // Concat all data to create a request string. + var sb = new StringBuilder(); + + sb.Append(builder.ReferenceNumber.ToString("00")); + sb.Append(amount?.ToString("00000000")); + sb.Append(returnRep); + sb.Append(paymentMode); + sb.Append(paymentType); + sb.Append(currencyCode); + sb.Append(privData); + sb.Append("A01" + immediateAns); + sb.Append("B01" + forceOnline); + sb.Append(extendedData); + + return TerminalUtilities.BuildIngenicoRequest(sb.ToString(), _settings.ConnectionMode); } - internal IDeviceMessage BuildRequestMessage(TerminalAuthBuilder builder) { + internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { string message = string.Empty; if (!IsObjectNullOrEmpty(builder.ReportType)) message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); else { - var _referenceNumber = builder.ReferenceNumber; - var _amount = builder.Amount; - var _returnRep = 1; - var _paymentMode = 0; - var _paymentType = (int)((IngenicoInterface)_device).paymentMethod; - var _currencyCode = "826"; - var _privateData = "EXT0100000"; - var _immediateAnswer = 0; - var _forceOnline = 0; - var _extendedData = "0000000000"; - - var _cashbackAmount = builder.CashBackAmount; - var _authCode = builder.AuthCode; + int referenceNumber = builder.ReferenceNumber; + decimal? amount = builder.Amount; + int returnRep = 1; + int paymentMode = 0; + int paymentType = (int)((IngenicoInterface)_device).paymentMethod; + string currencyCode = "826"; + string privateData = "EXT0100000"; + int immediateAnswer = 0; + int forceOnline = 0; + string extendedData = "0000000000"; + + decimal? cashbackAmount = builder.CashBackAmount; + string authCode = builder.AuthCode; string tableId = builder.TableNumber; // Validations - _amount = ValidateAmount(_amount); - _paymentMode = ValidatePaymentMode(builder.PaymentMode); - _currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? _currencyCode : builder.CurrencyCode)); + amount = ValidateAmount(amount); + paymentMode = ValidatePaymentMode(builder.PaymentMode); + currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? currencyCode : builder.CurrencyCode)); if (!string.IsNullOrEmpty(tableId)) { bool validateTableId = ValidateTableReference(tableId); if (validateTableId) - _extendedData = ValidateExtendedData(tableId, builder.ExtendedDataTags); + extendedData = ValidateExtendedData(tableId, builder.ExtendedDataTags); } - if (!IsObjectNullOrEmpty(_cashbackAmount)) - _extendedData = ValidateExtendedData(_cashbackAmount.ToString(), builder.ExtendedDataTags); - else if (!string.IsNullOrEmpty(_authCode)) - _extendedData = ValidateExtendedData(_authCode, builder.ExtendedDataTags); - - - - message = string.Format("{0}{1}{2}{3}{4}{5}{6}A01{7}B01{8}{9}", - _referenceNumber.ToString("00"), - _amount?.ToString("00000000"), - _returnRep, - _paymentMode, - _paymentType, - _currencyCode, - _privateData, - _immediateAnswer, - _forceOnline, - _extendedData); + if (!IsObjectNullOrEmpty(cashbackAmount)) + extendedData = ValidateExtendedData(cashbackAmount.ToString(), builder.ExtendedDataTags); + else if (!string.IsNullOrEmpty(authCode)) + extendedData = ValidateExtendedData(authCode, builder.ExtendedDataTags); + + // Concat all data to create a request string. + var sb = new StringBuilder(); + + sb.Append(referenceNumber.ToString("00")); + sb.Append(amount?.ToString("00000000")); + sb.Append(returnRep); + sb.Append(paymentMode); + sb.Append(paymentType); + sb.Append(currencyCode); + sb.Append(privateData); + sb.Append("A01" + immediateAnswer); + sb.Append("B01" + forceOnline); + sb.Append(extendedData); + + message = sb.ToString(); } return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); } + internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { + var send = Send(request); + return new IngenicoTerminalReportResponse(send); + } + #region Validations private static bool IsObjectNullOrEmpty(object value) { bool response = false; - if (value.IsNull() || string.IsNullOrWhiteSpace(value.ToString())) + if (value.IsNull() || string.IsNullOrWhiteSpace(value.ToString())) { response = true; - else response = false; + } + else { + response = false; + } return response; } + private static bool ValidateTableReference(string value) { bool response = false; - if (!string.IsNullOrEmpty(value) && value.Length <= 8) + if (!string.IsNullOrEmpty(value) && value.Length <= 8) { response = true; - else throw new BuilderException("Table number must not be less than or equal 0 or greater than 8 numerics."); + } + else { + throw new BuilderException("Table number must not be less than or equal 0 or greater than 8 numerics."); + } return response; } - private static int ValidatePaymentMode(PaymentMode _paymentMode) { - if (IsObjectNullOrEmpty(_paymentMode)) { - _paymentMode = PaymentMode.APPLICATION; + private static int ValidatePaymentMode(PaymentMode? paymentMode) { + if (IsObjectNullOrEmpty(paymentMode)) { + paymentMode = PaymentMode.APPLICATION; } - return (int)_paymentMode; + return (int)paymentMode; } private static string ValidateExtendedData(string value, ExtendedDataTags tags) { @@ -197,11 +209,15 @@ private static string ValidateExtendedData(string value, ExtendedDataTags tags) switch (tags) { case ExtendedDataTags.CASHB: decimal? cashbackAmount = Convert.ToDecimal(value); - if (cashbackAmount > 0m && cashbackAmount < 1000000m) + if (cashbackAmount > 0m && cashbackAmount < 1000000m) { cashbackAmount *= 100; - else if (cashbackAmount <= 0m) + } + else if (cashbackAmount <= 0m) { throw new BuilderException("Cashback Amount must not be in less than or equal 0 value."); - else throw new BuilderException("Cashback Amount exceed."); + } + else { + throw new BuilderException("Cashback Amount exceed."); + } extendedData = "CASHB={0};".FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); break; @@ -233,54 +249,50 @@ private static string ValidateExtendedData(string value, ExtendedDataTags tags) return extendedData; } - private static string ValidateCurrency(string _currencyCode) { - if (!string.IsNullOrWhiteSpace(_currencyCode)) { - _currencyCode = _currencyCode.PadLeft(3, '0'); + private static string ValidateCurrency(string currencyCode) { + if (!string.IsNullOrWhiteSpace(currencyCode)) { + currencyCode = currencyCode.PadLeft(3, '0'); + } + else { + currencyCode = "826"; } - else _currencyCode = "826"; - return _currencyCode; + return currencyCode; } - private decimal? ValidateAmount(decimal? _amount) { - if (_amount == null) + private decimal? ValidateAmount(decimal? amount) { + if (amount == null) { throw new BuilderException("Amount can not be null."); - else if (_amount > 0 && _amount < 1000000m) - _amount *= 100; - else if (_amount >= 1000000m) + } + else if (amount > 0 && amount < 1000000m) { + amount *= 100; + } + else if (amount >= 1000000m) { throw new BuilderException("Amount exceed."); - else + } + else { throw new BuilderException("Invalid input amount."); - return _amount; + } + return amount; } #endregion - public ITerminalConfiguration GetConfiguration() { - return _settings; - } - #endregion - - #region Report Request - internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { - var send = Send(request); - return new IngenicoTerminalReportResponse(send); - } #endregion #region Request internal IngenicoTerminalResponse DoRequest(IDeviceMessage request) { - var response = Send(request); + byte[] response = Send(request); return new IngenicoTerminalResponse(response); } private CancelResponse DoCancelRequest(IDeviceMessage request) { - var response = Send(request); + byte[] response = Send(request); return new CancelResponse(response); } private ReverseResponse DoReverseRequest(IDeviceMessage request) { - var response = Send(request); + byte[] response = Send(request); return new ReverseResponse(response); } #endregion From d8939fd64d745a2b6194c800e9f1cd319c3b5818 Mon Sep 17 00:00:00 2001 From: peter-yanong Date: Sat, 28 Mar 2020 19:02:00 +0800 Subject: [PATCH 031/120] Rename file IngenicoVariables to IngenicoEnums --- .../{IngenicoVariables.cs => IngenicoEnums.cs} | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) rename src/GlobalPayments.Api/Terminals/INGENICO/{IngenicoVariables.cs => IngenicoEnums.cs} (97%) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs similarity index 97% rename from src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs rename to src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 82dd883b..1b3d627a 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoVariables.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -44,10 +44,6 @@ public enum ReportType { ZBAL } - public enum Environment { - TEST, - PRODUCTION - } public enum TransactionStatus { SUCCESS = 0, REFERRAL = 2, @@ -55,20 +51,23 @@ public enum TransactionStatus { FAILED = 7, RECEIVED = 9 } + public enum ReverseStatus { REVERSAL_SUCCESS = 0, REVERSAL_FAILED = 7, NOTHING_TO_REVERSE = 9 } + public enum CancelStatus { CANCEL_DONE = 9, - CANCE_FAILED = 7 + CANCEL_FAILED = 7 } public enum DynamicCurrencyStatus { CONVERSION_APPLIED = 1, REJECTED = 0 } + public enum TransactionSubTypes { SPLIT_SALE_TXN = 0x53, // 0x53 byte for 'S' DCC_TXN = 0x44, // 0x44 byte for 'D' @@ -81,10 +80,12 @@ public enum ReportTypes { XBAL, ZBAL } + public enum TerminalStatus { NOT_READY = 0, READY = 1 } + public enum TerminalModes { STANDARD_MODE = 0, VENDING_MODE = 1 @@ -107,6 +108,7 @@ public enum PaymentType { AccountVerification = 6, ReferralConfirmation = 9 } + public enum PaymentMode { APPLICATION = 0, MAILORDER = 1 From 8d8086f05687eef204e6c2962fa9578cf7b129a4 Mon Sep 17 00:00:00 2001 From: peter-yanong Date: Sat, 28 Mar 2020 20:18:07 +0800 Subject: [PATCH 032/120] Update IngenicoInterface Prefaced class variable with an underscored. Add brackets on single line if statemens. --- .../Interfaces/IngenicoSerialInterface.cs | 148 ++++++++++-------- 1 file changed, 83 insertions(+), 65 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 3d7d8c10..6b0beda6 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -18,14 +18,14 @@ internal class IngenicoSerialInterface : IDeviceCommInterface { ITerminalConfiguration _settings; private SerialPort _serial; - private bool complete = false, - isResult = false, - isAcknowledge = false, - broadcast = false, - isXML = false; - private string buffer = string.Empty; - private string appendReport = string.Empty; - private List messageResponse; + private bool _complete = false; + private bool _isResult = false; + private bool _isAcknowledge = false; + private bool _broadcast = false; + private bool _isXML = false; + private string _buffer = string.Empty; + private string _appendReport = string.Empty; + private List _messageResponse; public IngenicoSerialInterface(ITerminalConfiguration settings) { this._settings = settings; @@ -46,41 +46,50 @@ public void Connect() { }; if (!_serial.IsOpen) { - _serial.DataReceived += new SerialDataReceivedEventHandler(_serial_DataReceived); + _serial.DataReceived += new SerialDataReceivedEventHandler(Serial_DataReceived); _serial.Open(); - } else throw new MessageException("Serial Port is already open."); + } + else { + throw new MessageException("Serial Port is already open."); + } } } - private void _serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { + private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort serial = (SerialPort)sender; do { Thread.Sleep(0100); - buffer = serial.ReadExisting(); + _buffer = serial.ReadExisting(); - if (!string.IsNullOrEmpty(buffer)) { + if (!string.IsNullOrEmpty(_buffer)) { _serial.ReadTimeout = _settings.Timeout; - if (buffer.Equals(INGENICO_RESP.ACKNOWLEDGE)) { - isAcknowledge = true; + if (_buffer.Equals(INGENICO_RESP.ACKNOWLEDGE)) { + _isAcknowledge = true; break; - } else if (buffer.Equals(INGENICO_RESP.ENQUIRY)) { + } + else if (_buffer.Equals(INGENICO_RESP.ENQUIRY)) { _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); break; - } else if (buffer.Contains(INGENICO_GLOBALS.BROADCAST)) { - broadcast = true; + } + else if (_buffer.Contains(INGENICO_GLOBALS.BROADCAST)) { + _broadcast = true; break; - } else if (buffer.Equals(INGENICO_RESP.NOTACKNOWLEDGE)) { - isAcknowledge = false; + } + else if (_buffer.Equals(INGENICO_RESP.NOTACKNOWLEDGE)) { + _isAcknowledge = false; break; - } else if (INGENICO_RESP.XML.Any(buffer.Contains)) { - isXML = true; + } + else if (INGENICO_RESP.XML.Any(_buffer.Contains)) { + _isXML = true; break; - } else { - if (!buffer.Contains(INGENICO_GLOBALS.BROADCAST) && !buffer.Contains(INGENICO_RESP.INVALID) - && !INGENICO_RESP.XML.Any(buffer.Contains) && !buffer.Contains(INGENICO_RESP.ENDOFTXN) - && !buffer.Contains(INGENICO_RESP.NOTACKNOWLEDGE)) - isResult = true; + } + else { + if (!_buffer.Contains(INGENICO_GLOBALS.BROADCAST) && !_buffer.Contains(INGENICO_RESP.INVALID) + && !INGENICO_RESP.XML.Any(_buffer.Contains) && !_buffer.Contains(INGENICO_RESP.ENDOFTXN) + && !_buffer.Contains(INGENICO_RESP.NOTACKNOWLEDGE)) { + _isResult = true; + } break; } } @@ -88,16 +97,16 @@ private void _serial_DataReceived(object sender, SerialDataReceivedEventArgs e) } public void Disconnect() { - _serial?.Dispose(); _serial.Close(); + _serial?.Dispose(); _serial = null; - buffer = string.Empty; - appendReport = string.Empty; - isResult = false; - complete = false; - isAcknowledge = false; - broadcast = false; - isXML = false; + _buffer = string.Empty; + _appendReport = string.Empty; + _isResult = false; + _complete = false; + _isAcknowledge = false; + _broadcast = false; + _isXML = false; } private bool ValidateResponseLRC(string calculate, string actual) { @@ -106,8 +115,9 @@ private bool ValidateResponseLRC(string calculate, string actual) { byte[] calculateLRC = TerminalUtilities.CalculateLRC(calculate); byte[] actualLRC = TerminalUtilities.CalculateLRC(actual); - if (BitConverter.ToString(actualLRC) == BitConverter.ToString(calculateLRC)) + if (BitConverter.ToString(actualLRC) == BitConverter.ToString(calculateLRC)) { response = true; + } return response; } @@ -116,14 +126,15 @@ private async Task WriteMessage(IDeviceMessage message) { return await Task.Run(() => { try { int enquiryCount = 0; - messageResponse = new List(); + _messageResponse = new List(); - if (_serial == null) + if (_serial == null) { return false; + } do { _serial.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); - if (isAcknowledge) { + if (_isAcknowledge) { do { byte[] msg = message.GetSendBuffer(); foreach (byte b in msg) { @@ -131,55 +142,57 @@ private async Task WriteMessage(IDeviceMessage message) { _serial.Write(_b, 0, 1); } - if (isAcknowledge) { + if (_isAcknowledge) { _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); break; } } while (true); do { - if (broadcast) { - byte[] bMsg = Encoding.ASCII.GetBytes(buffer); + if (_broadcast) { + byte[] bMsg = Encoding.ASCII.GetBytes(_buffer); BroadcastMessage broadcastMsg = new BroadcastMessage(bMsg); OnBroadcastMessage?.Invoke(broadcastMsg.Code, broadcastMsg.Message); - broadcast = false; + _broadcast = false; } - if (isXML) { + if (_isXML) { do { - appendReport += buffer; - if (appendReport.Contains(INGENICO_RESP.ENDXML)) { - string xmlData = appendReport.Substring(1, appendReport.Length - 3); + _appendReport += _buffer; + if (_appendReport.Contains(INGENICO_RESP.ENDXML)) { + string xmlData = _appendReport.Substring(1, _appendReport.Length - 3); if (MessageReceived(xmlData)) { _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - complete = true; + _complete = true; } } Thread.Sleep(0500); - } while (!complete); + } while (!_complete); } - if (isResult) { + if (_isResult) { string check = Encoding.UTF8.GetString(message.GetSendBuffer()); - if (buffer.Contains(check.Substring(0, 2))) { + if (_buffer.Contains(check.Substring(0, 2))) { do { - string rData = buffer.Substring(1, buffer.Length - 3); - buffer = buffer.Substring(1, buffer.Length - 3); - bool validateLRC = ValidateResponseLRC(rData, buffer); + string rData = _buffer.Substring(1, _buffer.Length - 3); + _buffer = _buffer.Substring(1, _buffer.Length - 3); + bool validateLRC = ValidateResponseLRC(rData, _buffer); if (validateLRC) { if (MessageReceived(rData)) { _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - complete = true; + _complete = true; } } - } while (!complete); + } while (!_complete); } } - if (complete) + if (_complete) { break; + } } while (true); break; - } else { + } + else { Thread.Sleep(1000); _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); enquiryCount++; @@ -190,7 +203,7 @@ private async Task WriteMessage(IDeviceMessage message) { } } while (true); - return complete; + return _complete; } catch (MessageException e) { throw new MessageException(e.Message); @@ -208,9 +221,12 @@ public byte[] Send(IDeviceMessage message) { if (!task.Wait(_settings.Timeout)) { throw new MessageException("Terminal did not response within timeout."); } - string test = Encoding.ASCII.GetString(messageResponse.ToArray()); - return messageResponse.ToArray(); - } else throw new MessageException("Terminal not connected."); + string test = Encoding.ASCII.GetString(_messageResponse.ToArray()); + return _messageResponse.ToArray(); + } + else { + throw new MessageException("Terminal not connected."); + } } finally { if (_serial != null) { @@ -220,10 +236,12 @@ public byte[] Send(IDeviceMessage message) { } private bool MessageReceived(string messageData) { - if (messageResponse == null) + if (_messageResponse == null) { return false; - foreach (char b in messageData) - messageResponse.Add((byte)b); + } + foreach (char b in messageData) { + _messageResponse.Add((byte)b); + } return true; } } From 1640cf8fbc58f54a6767b55f2d8b2d9d9650f64b Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Sat, 28 Mar 2020 20:22:45 +0800 Subject: [PATCH 033/120] Update from var to Task data type declaration --- .../Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 6b0beda6..9497e180 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -217,7 +217,7 @@ public byte[] Send(IDeviceMessage message) { if (_serial != null) { string bufferSend = Encoding.ASCII.GetString(message.GetSendBuffer()); OnMessageSent?.Invoke(bufferSend.Substring(1, bufferSend.Length - 3)); - var task = WriteMessage(message); + Task task = WriteMessage(message); if (!task.Wait(_settings.Timeout)) { throw new MessageException("Terminal did not response within timeout."); } From 9fb73e9eed7d3f6e24ba8b5cc72a90349e5443a9 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Sat, 28 Mar 2020 20:40:54 +0800 Subject: [PATCH 034/120] Update TerminalManageBuilder Wrapped in brackets all single line if statements. Remove validation for Cancel --- .../Terminals/Builders/TerminalManageBuilder.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs index a7bdbe84..7d2e8bbc 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs @@ -11,8 +11,9 @@ public class TerminalManageBuilder : TerminalBuilder { internal decimal? Gratuity { get; set; } internal string TransactionId { get { - if (PaymentMethod is TransactionReference) + if (PaymentMethod is TransactionReference) { return (PaymentMethod as TransactionReference).TransactionId; + } return null; } } @@ -36,7 +37,7 @@ public TerminalManageBuilder WithCurrencyCode(string value) { CurrencyCode = value; return this; } - + public TerminalManageBuilder WithAmount(decimal? amount) { Amount = amount; return this; @@ -60,8 +61,9 @@ public TerminalManageBuilder WithGratuity(decimal? amount) { /// Authorization Code /// public TerminalManageBuilder WithAuthCode(string value) { - if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) + if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) { PaymentMethod = new TransactionReference(); + } (PaymentMethod as TransactionReference).AuthCode = value; ExtendedDataTags = ExtendedDataTags.AUTHCODE; return this; @@ -71,7 +73,7 @@ public TerminalManageBuilder WithTransactionId(string value) { if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) PaymentMethod = new TransactionReference(); (PaymentMethod as TransactionReference).TransactionId = value; - + return this; } @@ -96,7 +98,6 @@ protected override void SetupValidations() { Validations.For(TransactionType.Capture).When(() => AuthCode).IsNull().Check(() => TransactionId).IsNotNull(); Validations.For(TransactionType.Void).When(() => ClientTransactionId).IsNull().Check(() => TransactionId).IsNotNull(); Validations.For(PaymentMethodType.Gift).Check(() => Currency).IsNotNull(); - Validations.For(TransactionType.Cancel).Check(() => Amount).IsNotNull(); } } } From 09a00a5e600c056fa5b9823926e053ef29d864d2 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:12:34 +0800 Subject: [PATCH 035/120] Removed changes on file that should not be modified during Implementation of Ingenico Terminal --- .../ServiceConfigs/Gateways/GatewayConfig.cs | 2 +- .../ServiceConfigs/Gateways/TransitConfig.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs b/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs index 38a30e41..ddd0e50e 100644 --- a/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs +++ b/src/GlobalPayments.Api/ServiceConfigs/Gateways/GatewayConfig.cs @@ -1,7 +1,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Gateways; -namespace GlobalPayments.Api.Gateway { +namespace GlobalPayments.Api { public abstract class GatewayConfig : Configuration { public AcceptorConfig AcceptorConfig { get; set; } protected GatewayProvider GatewayProvider { get; set; } diff --git a/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs b/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs index b82f9e25..d5a9aa61 100644 --- a/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs +++ b/src/GlobalPayments.Api/ServiceConfigs/Gateways/TransitConfig.cs @@ -8,9 +8,7 @@ public class TransitConfig : GatewayConfig { public string DeviceId { get;set; } public string MerchantId { get; set; } public string TransactionKey { get; set; } - - private AcceptorConfig AcceptorConfig; - + public TransitConfig() : base(GatewayProvider.TransIT) { } internal override void ConfigureContainer(ConfiguredServices services) { @@ -22,7 +20,7 @@ internal override void ConfigureContainer(ConfiguredServices services) { } else ServiceUrl = ServiceEndpoints.TRANSIT_MULTIPASS_PRODUCTION; } - AcceptorConfig = new AcceptorConfig(); + var gateway = new TransitConnector() { AcceptorConfig = AcceptorConfig, DeveloperId = DeveloperId, From 4625a76e149d2cfde062cf02bc4c8afb1cdde996 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:22:15 +0800 Subject: [PATCH 036/120] Update unit test Change device type to more descriptive enum --- .../Terminals/Ingenico/IngenicoTransactionTests.cs | 3 +-- .../Terminals/Ingenico/SaleTransactionManagement.cs | 4 +--- .../Terminals/Ingenico/SerialPaymentTransactions.cs | 5 +---- .../Terminals/Ingenico/TransactionTest.cs | 9 ++------- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs index f5bd9b28..12d108fa 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -15,7 +14,7 @@ public class IngenicoTransactionTests { public IngenicoTransactionTests() { _device = DeviceService.Create(new ConnectionConfig() { - DeviceType = Entities.DeviceType.INGENICO, + DeviceType = Entities.DeviceType.Ingenico_EPOS_Desk5000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", Timeout = 60000, diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs index 3a97d3de..72df1ca8 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using GlobalPayments.Api.Entities; @@ -18,7 +16,7 @@ public class PaymentTransactionManagement { public PaymentTransactionManagement() { _device = DeviceService.Create(new ConnectionConfig() { - DeviceType = DeviceType.INGENICO, + DeviceType = DeviceType.Ingenico_EPOS_Lane3000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", Timeout = 30000, diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs index a96e5ffe..7c6c463d 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs @@ -4,9 +4,6 @@ using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.INGENICO; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using ReportType = GlobalPayments.Api.Terminals.INGENICO.ReportType; @@ -18,7 +15,7 @@ public class SerialPaymentTransactions { public SerialPaymentTransactions() { _device = DeviceService.Create(new ConnectionConfig() { - DeviceType = DeviceType.INGENICO, + DeviceType = DeviceType.Ingenico_EPOS_Lane3000, ConnectionMode = ConnectionModes.SERIAL, Port = "5", BaudRate = BaudRate.r9600, diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs index 131e4dc2..98d08852 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using GlobalPayments.Api.Entities; using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; -using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { @@ -17,7 +12,7 @@ public class TransactionTest { public TransactionTest() { _device = DeviceService.Create(new ConnectionConfig() { - DeviceType = Entities.DeviceType.INGENICO, + DeviceType = DeviceType.Ingenico_EPOS_Lane3000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", Timeout = 60000, From 217105ef4f2614382ea17f8cbc3235c344027466 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:47:58 +0800 Subject: [PATCH 037/120] TerminalAuthBuilder Update Wrap single line if statements with brackets. Remove ExtendedTags property. --- .../Terminals/Builders/TerminalAuthBuilder.cs | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index 513727b8..b3cd6b70 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -11,8 +11,9 @@ public class TerminalAuthBuilder : TerminalBuilder { internal decimal? Amount { get; set; } internal string AuthCode { get { - if (PaymentMethod is TransactionReference) + if (PaymentMethod is TransactionReference) { return (PaymentMethod as TransactionReference).AuthCode; + } return null; } } @@ -31,12 +32,16 @@ internal string AuthCode { internal string TaxExemptId { get; set; } internal string TransactionId { get { - if (PaymentMethod is TransactionReference) + if (PaymentMethod is TransactionReference) { return (PaymentMethod as TransactionReference).TransactionId; + } return null; } } - internal INGENICO.ReportType? ReportType { get; set; } = null; + internal INGENICO.ReportType? ReportType { get; set; } + internal string CurrencyCode { get; set; } + internal PaymentMode PaymentMode { get; set; } + internal string TableNumber { get; set; } /// /// Sets the report type for the transaction. @@ -67,10 +72,10 @@ public TerminalAuthBuilder WithAmount(decimal? amount) { /// Authorization Code /// public TerminalAuthBuilder WithAuthCode(string value) { - if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) + if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) { PaymentMethod = new TransactionReference(); + } (PaymentMethod as TransactionReference).AuthCode = value; - ExtendedDataTags = ExtendedDataTags.AUTHCODE; return this; } @@ -91,7 +96,6 @@ public TerminalAuthBuilder WithAutoSubstantiation(AutoSubstantiation value) { /// public TerminalAuthBuilder WithCashBack(decimal? amount) { CashBackAmount = amount; - ExtendedDataTags = ExtendedDataTags.CASHB; return this; } public TerminalAuthBuilder WithClientTransactionId(string value) { @@ -152,15 +156,6 @@ public TerminalAuthBuilder WithTransactionId(string value) { return this; } - #region Additional Methods for Ingenico - - internal string CurrencyCode { get; set; } - internal PaymentMode PaymentMode { get; set; } - - internal string TableNumber { get; set; } - internal ExtendedDataTags ExtendedDataTags { get; set; } - - /// /// Sets the currency code for the transaction. /// @@ -188,12 +183,9 @@ public TerminalAuthBuilder WithPaymentMode(PaymentMode value) { /// public TerminalAuthBuilder WithTableNumber(string value) { TableNumber = value; - ExtendedDataTags = ExtendedDataTags.TABLE_NUMBER; return this; } - #endregion - internal TerminalAuthBuilder(TransactionType type, PaymentMethodType paymentType) : base(type, paymentType) { } From 87123eb36f48b76ef17dc2cda34696d26eda7c93 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:48:33 +0800 Subject: [PATCH 038/120] TerminalManageBuilder update Remove ExtendedTags property. --- .../Terminals/Builders/TerminalManageBuilder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs index 7d2e8bbc..22ecede8 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs @@ -19,7 +19,6 @@ internal string TransactionId { } internal string CurrencyCode { get; set; } internal PaymentMode PaymentMode { get; set; } - internal ExtendedDataTags ExtendedDataTags { get; set; } internal string AuthCode { get { if (PaymentMethod is TransactionReference) @@ -65,7 +64,6 @@ public TerminalManageBuilder WithAuthCode(string value) { PaymentMethod = new TransactionReference(); } (PaymentMethod as TransactionReference).AuthCode = value; - ExtendedDataTags = ExtendedDataTags.AUTHCODE; return this; } From 57c66be77d9e9f8ba5ca3961245b54dabac6c8af Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:50:31 +0800 Subject: [PATCH 039/120] Controller Update Remove all validation regarding ExtendedTags property. Wrap single line if statements with brackets. Create new validation for Cashback amount, Authcode, and Table reference. --- .../Terminals/INGENICO/IngenicoController.cs | 131 +++++++----------- 1 file changed, 53 insertions(+), 78 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index 7e2f8406..adbbaa90 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -1,4 +1,5 @@ -using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Builders; +using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; using GlobalPayments.Api.Utils; @@ -84,14 +85,25 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { int forceOnline = 0; string extendedData = "0000000000"; - // For Auth code value in extended data - if (!string.IsNullOrEmpty(builder.AuthCode)) - extendedData = ValidateExtendedData(builder.AuthCode, builder.ExtendedDataTags); - else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) - // For Reversal with Transaction Id value in extended data - extendedData = ValidateExtendedData(builder.TransactionId, ExtendedDataTags.TXN_COMMANDS_PARAMS); - else - extendedData = ValidateExtendedData(builder.TransactionType.ToString(), ExtendedDataTags.TXN_COMMANDS); + // Validation for Authcode + if (!string.IsNullOrEmpty(builder.AuthCode)) { + extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(builder.AuthCode); + } + // Validation for Reversal with Transaction Id value in Extended data + else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) { + extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(builder.TransactionId); + } + // TODO: Remove Cancel, Duplicate, Reverse here this should be on Interface + // Temporary for CANCEL. + else if (builder.TransactionType == TransactionType.Cancel) { + extendedData = INGENICO_REQ_CMD.CANCEL; + } + else if (builder.TransactionType == TransactionType.Duplicate) { + extendedData = INGENICO_REQ_CMD.DUPLICATE; + } + else if (builder.TransactionType == TransactionType.Reversal) { + extendedData = INGENICO_REQ_CMD.REVERSE; + } // Concat all data to create a request string. var sb = new StringBuilder(); @@ -131,25 +143,30 @@ internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { string tableId = builder.TableNumber; // Validations + if (referenceNumber == default(int) && RequestIdProvider != null) { + referenceNumber = RequestIdProvider.GetRequestId(); + } amount = ValidateAmount(amount); paymentMode = ValidatePaymentMode(builder.PaymentMode); currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? currencyCode : builder.CurrencyCode)); if (!string.IsNullOrEmpty(tableId)) { - bool validateTableId = ValidateTableReference(tableId); - if (validateTableId) - extendedData = ValidateExtendedData(tableId, builder.ExtendedDataTags); + ValidateTableId(tableId); + extendedData = INGENICO_REQ_CMD.TABLE_WITH_ID.FormatWith(tableId); + } + else if (!string.IsNullOrEmpty(authCode)) { + extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(authCode); + } + else if (cashbackAmount != null) { + ValidateCashbackAmount(cashbackAmount); + cashbackAmount *= 100; + extendedData = INGENICO_REQ_CMD.CASHBACK.FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); } - - if (!IsObjectNullOrEmpty(cashbackAmount)) - extendedData = ValidateExtendedData(cashbackAmount.ToString(), builder.ExtendedDataTags); - else if (!string.IsNullOrEmpty(authCode)) - extendedData = ValidateExtendedData(authCode, builder.ExtendedDataTags); // Concat all data to create a request string. var sb = new StringBuilder(); - sb.Append(referenceNumber.ToString("00")); + sb.Append(referenceNumber.ToString("00").Substring(0, 2)); sb.Append(amount?.ToString("00000000")); sb.Append(returnRep); sb.Append(paymentMode); @@ -172,7 +189,7 @@ internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { } #region Validations - private static bool IsObjectNullOrEmpty(object value) { + private bool IsObjectNullOrEmpty(object value) { bool response = false; if (value.IsNull() || string.IsNullOrWhiteSpace(value.ToString())) { response = true; @@ -184,19 +201,22 @@ private static bool IsObjectNullOrEmpty(object value) { return response; } - private static bool ValidateTableReference(string value) { - bool response = false; - if (!string.IsNullOrEmpty(value) && value.Length <= 8) { - response = true; - } - else { - throw new BuilderException("Table number must not be less than or equal 0 or greater than 8 numerics."); + private void ValidateTableId(string value) { + if (value.Length != 8) { + throw new BuilderException("The required length for table number is 8."); } + } - return response; + private void ValidateCashbackAmount(decimal? value) { + if (value >= 1000000m) { + throw new BuilderException("Cashback Amount exceed."); + } + if (value < 0m) { + throw new BuilderException("Cashback Amount must not be in less than zero."); + } } - private static int ValidatePaymentMode(PaymentMode? paymentMode) { + private int ValidatePaymentMode(PaymentMode? paymentMode) { if (IsObjectNullOrEmpty(paymentMode)) { paymentMode = PaymentMode.APPLICATION; } @@ -204,52 +224,7 @@ private static int ValidatePaymentMode(PaymentMode? paymentMode) { return (int)paymentMode; } - private static string ValidateExtendedData(string value, ExtendedDataTags tags) { - string extendedData = string.Empty; - switch (tags) { - case ExtendedDataTags.CASHB: - decimal? cashbackAmount = Convert.ToDecimal(value); - if (cashbackAmount > 0m && cashbackAmount < 1000000m) { - cashbackAmount *= 100; - } - else if (cashbackAmount <= 0m) { - throw new BuilderException("Cashback Amount must not be in less than or equal 0 value."); - } - else { - throw new BuilderException("Cashback Amount exceed."); - } - - extendedData = "CASHB={0};".FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); - break; - case ExtendedDataTags.AUTHCODE: - extendedData = "AUTHCODE={0}".FormatWith(value); - break; - case ExtendedDataTags.TABLE_NUMBER: - extendedData = "CMD=ID{0}".FormatWith(value); - break; - case ExtendedDataTags.TXN_COMMANDS: - var transType = (TransactionType)Enum.Parse(typeof(TransactionType), value, true); - switch (transType) { - case TransactionType.Cancel: - extendedData = INGENICO_REQ_CMD.CANCEL; - break; - case TransactionType.Duplicate: - extendedData = INGENICO_REQ_CMD.DUPLICATE; - break; - case TransactionType.Reversal: - extendedData = INGENICO_REQ_CMD.REVERSE; - break; - } - break; - case ExtendedDataTags.TXN_COMMANDS_PARAMS: - extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(value); - break; - } - - return extendedData; - } - - private static string ValidateCurrency(string currencyCode) { + private string ValidateCurrency(string currencyCode) { if (!string.IsNullOrWhiteSpace(currencyCode)) { currencyCode = currencyCode.PadLeft(3, '0'); } @@ -261,12 +236,12 @@ private static string ValidateCurrency(string currencyCode) { } private decimal? ValidateAmount(decimal? amount) { - if (amount == null) { - throw new BuilderException("Amount can not be null."); - } - else if (amount > 0 && amount < 1000000m) { + if (amount != null && amount > 0 && amount < 1000000m) { amount *= 100; } + else if (amount == null) { + throw new BuilderException("Amount can not be null."); + } else if (amount >= 1000000m) { throw new BuilderException("Amount exceed."); } From 8b7c7268843650fbd1ed21dd705a224cbf00024f Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 11:51:10 +0800 Subject: [PATCH 040/120] Ingenico Enums update Remove ExtendedTags enum. --- .../Terminals/INGENICO/IngenicoEnums.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 1b3d627a..06dd36e0 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -3,10 +3,18 @@ namespace GlobalPayments.Api.Terminals.INGENICO { internal class INGENICO_REQ_CMD { + // Request Transactions + public const string AUTHCODE = "AUTHCODE={0}"; + public const string CASHBACK = "CASHB={0}"; + + // Request Commands public const string CANCEL = "CMD=CANCEL"; public const string DUPLICATE = "CMD=DUPLIC"; public const string REVERSE = "CMD=REVERSE"; public const string REVERSE_WITH_ID = "CMD=REV{0}"; + public const string TABLE_WITH_ID = "CMD=ID{0}"; + + // Requesr Report public const string REPORT = "0100000001100826EXT0100000A010B010CMD={0}"; public const string RECEIPT = "0100000001100826EXT0100000A010B010CMD={0}"; } @@ -113,12 +121,4 @@ public enum PaymentMode { APPLICATION = 0, MAILORDER = 1 } - - public enum ExtendedDataTags { - CASHB, - AUTHCODE, - TABLE_NUMBER, - TXN_COMMANDS, - TXN_COMMANDS_PARAMS - } } \ No newline at end of file From ef09e2a6fe892e11754987faea5572ed50f0d3f8 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:24:38 +0800 Subject: [PATCH 041/120] Change return type of GetReport from TerminalAuthBuilder to TerminalReportBuilder --- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index bb9f40f6..3bd48ae7 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -108,8 +108,8 @@ public virtual TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptTy return new TerminalReportBuilder(type); } - public virtual TerminalAuthBuilder GetReport(INGENICO.ReportType type) { - return new TerminalAuthBuilder(TransactionType.Create, PaymentMethodType.Other).WithReportType(type); + public virtual TerminalReportBuilder GetReport(INGENICO.ReportType type) { + return new TerminalReportBuilder(TerminalReportType.LocalDetailReport).WithReportType(type); } #endregion From 2b8241e2d917f73f5a1c1f581f65f7323f968733 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:25:48 +0800 Subject: [PATCH 042/120] Change return type of GetReport from TerminalAuthBuilder to TerminalReportBuilder --- .../Terminals/Abstractions/IDeviceInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 18d6c4d6..85b7babb 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -48,7 +48,7 @@ public interface IDeviceInterface : IDisposable { /// /// Report Type /// - TerminalAuthBuilder GetReport(INGENICO.ReportType type); + TerminalReportBuilder GetReport(INGENICO.ReportType type); #endregion #region Batch Calls From 64b6b788a27a547765e6911d65c36af0a50cbf98 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:27:58 +0800 Subject: [PATCH 043/120] IDeviceResponse Update Return MerchantFee property Remove ReportData property in ITerminalReport --- .../Terminals/Abstractions/IDeviceResponse.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs index 3485eb21..93a63f04 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs @@ -48,9 +48,10 @@ public interface ITerminalResponse : IDeviceResponse { string ApplicationCryptogram { get; set; } string CardHolderVerificationMethod { get; set; } string TerminalVerificationResults { get; set; } + decimal? MerchantFee { get; set; } } public interface ITerminalReport : IDeviceResponse { - string ReportData { get; set; } + } } From 5c9470b8f793f8b7ae597f6c2eb3ca899cb727be Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:35:30 +0800 Subject: [PATCH 044/120] TerminalAuthBuilder update Remove WithReportType method from TerminalAuthBuilder. Remove ReportType property from TerminalAuthBuilder --- .../Terminals/Builders/TerminalAuthBuilder.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index b3cd6b70..bc4f103a 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -38,21 +38,10 @@ internal string TransactionId { return null; } } - internal INGENICO.ReportType? ReportType { get; set; } internal string CurrencyCode { get; set; } internal PaymentMode PaymentMode { get; set; } internal string TableNumber { get; set; } - /// - /// Sets the report type for the transaction. - /// - /// Report Type - /// - public TerminalAuthBuilder WithReportType(INGENICO.ReportType reportType) { - ReportType = reportType; - return this; - } - public TerminalAuthBuilder WithAddress(Address address) { Address = address; return this; From 2efe261f76bb61102801fd956942a2e5906c2b43 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:39:29 +0800 Subject: [PATCH 045/120] TerminalReportBuilder Update Add temporary report type property Create WithReportType method. --- .../Terminals/Builders/TerminalReportBuilder.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs index bd8adf8b..2e95db8c 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs @@ -10,6 +10,9 @@ public class TerminalReportBuilder { internal TerminalReportType ReportType { get; set; } internal ReceiptType ReceiptType { get; set; } + // TODO: Remove this property. Need to clarify with Russell. + internal ReportType? _ReportType { get; set; } + private TerminalSearchBuilder _searchBuilder; internal TerminalSearchBuilder SearchBuilder { get { @@ -32,6 +35,17 @@ public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) { return SearchBuilder.And(criteria, value); } + + /// + /// Sets the report type for the transaction. + /// + /// Report Type + /// + public TerminalReportBuilder WithReportType(ReportType reportType) { + _ReportType = reportType; + return this; + } + public ITerminalReport Execute(string configName = "default") { var device = ServicesContainer.Instance.GetDeviceController(configName); return device.ProcessReport(this); From 8e8cbedf3e1a0e2c7e50a4930bc7188b5f948099 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:43:31 +0800 Subject: [PATCH 046/120] IgenicoController Update Create BuildReportTransaction method Handle method in ProcessReport Add validation for passed report Type. --- .../Terminals/INGENICO/IngenicoController.cs | 127 ++++++++++-------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index adbbaa90..f317ac04 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -50,8 +50,15 @@ internal override ITerminalResponse ManageTransaction(TerminalManageBuilder buil } internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) { - IDeviceMessage request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); - return ReportRequest(request); + IDeviceMessage request; + if (builder._ReportType != null) { + request = BuildReportTransaction(builder); + return ReportRequest(request); + } + else { + request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); + return ReportRequest(request); + } } internal override ITerminalResponse ProcessTransaction(TerminalAuthBuilder builder) { @@ -87,7 +94,7 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { // Validation for Authcode if (!string.IsNullOrEmpty(builder.AuthCode)) { - extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(builder.AuthCode); + extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(builder.AuthCode); } // Validation for Reversal with Transaction Id value in Extended data else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) { @@ -124,65 +131,71 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { string message = string.Empty; - if (!IsObjectNullOrEmpty(builder.ReportType)) - message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); - else { - int referenceNumber = builder.ReferenceNumber; - decimal? amount = builder.Amount; - int returnRep = 1; - int paymentMode = 0; - int paymentType = (int)((IngenicoInterface)_device).paymentMethod; - string currencyCode = "826"; - string privateData = "EXT0100000"; - int immediateAnswer = 0; - int forceOnline = 0; - string extendedData = "0000000000"; - - decimal? cashbackAmount = builder.CashBackAmount; - string authCode = builder.AuthCode; - string tableId = builder.TableNumber; - - // Validations - if (referenceNumber == default(int) && RequestIdProvider != null) { - referenceNumber = RequestIdProvider.GetRequestId(); - } - amount = ValidateAmount(amount); - paymentMode = ValidatePaymentMode(builder.PaymentMode); - currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? currencyCode : builder.CurrencyCode)); - - if (!string.IsNullOrEmpty(tableId)) { - ValidateTableId(tableId); - extendedData = INGENICO_REQ_CMD.TABLE_WITH_ID.FormatWith(tableId); - } - else if (!string.IsNullOrEmpty(authCode)) { - extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(authCode); - } - else if (cashbackAmount != null) { - ValidateCashbackAmount(cashbackAmount); - cashbackAmount *= 100; - extendedData = INGENICO_REQ_CMD.CASHBACK.FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); - } - - // Concat all data to create a request string. - var sb = new StringBuilder(); - - sb.Append(referenceNumber.ToString("00").Substring(0, 2)); - sb.Append(amount?.ToString("00000000")); - sb.Append(returnRep); - sb.Append(paymentMode); - sb.Append(paymentType); - sb.Append(currencyCode); - sb.Append(privateData); - sb.Append("A01" + immediateAnswer); - sb.Append("B01" + forceOnline); - sb.Append(extendedData); - - message = sb.ToString(); + int referenceNumber = builder.ReferenceNumber; + decimal? amount = builder.Amount; + int returnRep = 1; + int paymentMode = 0; + int paymentType = (int)((IngenicoInterface)_device).paymentMethod; + string currencyCode = "826"; + string privateData = "EXT0100000"; + int immediateAnswer = 0; + int forceOnline = 0; + string extendedData = "0000000000"; + + decimal? cashbackAmount = builder.CashBackAmount; + string authCode = builder.AuthCode; + string tableId = builder.TableNumber; + + // Validations + if (referenceNumber == default(int) && RequestIdProvider != null) { + referenceNumber = RequestIdProvider.GetRequestId(); + } + amount = ValidateAmount(amount); + paymentMode = ValidatePaymentMode(builder.PaymentMode); + currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? currencyCode : builder.CurrencyCode)); + + if (!string.IsNullOrEmpty(tableId)) { + ValidateTableId(tableId); + extendedData = INGENICO_REQ_CMD.TABLE_WITH_ID.FormatWith(tableId); + } + else if (!string.IsNullOrEmpty(authCode)) { + extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(authCode); } + else if (cashbackAmount != null) { + ValidateCashbackAmount(cashbackAmount); + cashbackAmount *= 100; + extendedData = INGENICO_REQ_CMD.CASHBACK.FormatWith(Convert.ToInt64(Math.Round(cashbackAmount.Value, MidpointRounding.AwayFromZero))); + } + + // Concat all data to create a request string. + var sb = new StringBuilder(); + + sb.Append(referenceNumber.ToString("00").Substring(0, 2)); + sb.Append(amount?.ToString("00000000")); + sb.Append(returnRep); + sb.Append(paymentMode); + sb.Append(paymentType); + sb.Append(currencyCode); + sb.Append(privateData); + sb.Append("A01" + immediateAnswer); + sb.Append("B01" + forceOnline); + sb.Append(extendedData); + + message = sb.ToString(); return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); } + internal IDeviceMessage BuildReportTransaction(TerminalReportBuilder builder) { + if (!IsObjectNullOrEmpty(builder.ReportType)) { + string message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); + return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + } + else { + throw new BuilderException("Type of report is missing in request."); + } + } + internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { var send = Send(request); return new IngenicoTerminalReportResponse(send); From e1afe833836a724a8cad4b38fdc5deb8828597bb Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:44:52 +0800 Subject: [PATCH 047/120] DeviceResponse Update Remove ReportData property and its handling. --- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index 2572ac6f..bae1ce17 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -80,6 +80,7 @@ internal IngenicoTerminalResponse(byte[] buffer) { public string ApplicationPreferredName { get; set; } public string ApplicationLabel { get; set; } public string ApplicationId { get; set; } + public decimal? MerchantFee { get; set; } #endregion /** Index @@ -110,16 +111,12 @@ public virtual void ParseResponse(byte[] response) { public class IngenicoTerminalReportResponse : IngenicoBaseResponse, ITerminalReport { private byte[] _buffer; - private string _reportData; internal IngenicoTerminalReportResponse(byte[] buffer) { _buffer = buffer; - _reportData = ASCIIEncoding.ASCII.GetString(_buffer); Status = _buffer.Length > 0 ? "SUCCESS" : "FAILED"; } - public string ReportData { get { return _reportData;} set { } } - public override string ToString() { return Encoding.ASCII.GetString(_buffer); } From 033f586812eb5e8503906ced091dced5d391c84d Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:45:39 +0800 Subject: [PATCH 048/120] Update unit test. --- .../Terminals/Ingenico/SaleTransactionManagement.cs | 2 +- .../Terminals/Ingenico/SerialPaymentTransactions.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs index 72df1ca8..f81593d2 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs @@ -132,7 +132,7 @@ public void GetLastReceiptTest() { .GetLastReceipt() .Execute(); - Assert.IsNotNull(response.ReportData); + Assert.IsNotNull(response.ToString()); } } } diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs index 7c6c463d..3da741d8 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs @@ -112,9 +112,9 @@ public void TaxFree() { .GetLastReceipt(ReceiptType.SPLITR) .Execute(); - string test = splitR.ReportData; + string test = splitR.ToString(); - if (splitR.ReportData.Contains("")) + if (splitR.ToString().Contains("")) Assert.IsNotNull(splitR); } @@ -124,9 +124,9 @@ public void Ticket() { .GetLastReceipt(ReceiptType.TICKET) .Execute(); - string test = res.ReportData; + string test = res.ToString(); - if (res.ReportData.Contains("")) + if (res.ToString().Contains("")) Assert.IsNotNull(res); } @@ -136,7 +136,7 @@ public void EOD() { /** This example doesn't return XML/Report Data but it intiate End of Day Report and the terminal will return EODOK if success. */ - ITerminalResponse res = _device + ITerminalReport res = _device .GetReport(ReportType.EOD) .Execute(); From 848d94e505327ab323951f1446e7659c2dc79a80 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 30 Mar 2020 17:46:35 +0800 Subject: [PATCH 049/120] IngenicoInterface Update Move all methods regarding report to TerminalReportBuilder from TerminalAuthBuilder. --- src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index eb7ba4ea..114d1b9d 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -58,7 +58,7 @@ public override TerminalAuthBuilder Verify() { #endregion #region XML & Report Management - public override TerminalAuthBuilder GetReport(ReportType type) { + public override TerminalReportBuilder GetReport(ReportType type) { return base.GetReport(type); } From 6b0a6d297d6c902d4e74e8226e0feb40efae819e Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 11:57:22 +0800 Subject: [PATCH 050/120] Update namespace Change namespace case into Initial Caps. --- .../Terminals/Abstractions/IDeviceInterface.cs | 4 +++- .../Terminals/Builders/TerminalAuthBuilder.cs | 2 +- .../Terminals/Builders/TerminalManageBuilder.cs | 2 +- .../Terminals/Builders/TerminalReportBuilder.cs | 2 +- src/GlobalPayments.Api/Terminals/ConnectionConfig.cs | 2 +- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 2 +- .../Terminals/INGENICO/IngenicoController.cs | 2 +- .../Terminals/INGENICO/IngenicoInterface.cs | 2 +- .../Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs | 2 +- .../Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs | 2 +- .../Terminals/INGENICO/Interfaces/TcpListenerEx.cs | 2 +- .../Terminals/INGENICO/Responses/BroadcastMessage.cs | 4 ++-- .../Terminals/INGENICO/Responses/CancelResponse.cs | 2 +- .../Terminals/INGENICO/Responses/DataResponse.cs | 4 ++-- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 2 +- .../Terminals/INGENICO/Responses/ReverseResponse.cs | 2 +- 16 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 85b7babb..fa7b2ad9 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -2,8 +2,10 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using GlobalPayments.Api.Terminals.Messaging; +// TODO: remove this. +using ReportType = GlobalPayments.Api.Terminals.Ingenico.ReportType; namespace GlobalPayments.Api.Terminals { public interface IDeviceInterface : IDisposable { diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index bc4f103a..4c67fd9a 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -2,7 +2,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.PaymentMethods; using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; namespace GlobalPayments.Api.Terminals.Builders { public class TerminalAuthBuilder : TerminalBuilder { diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs index 22ecede8..06e2ab68 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs @@ -1,7 +1,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.PaymentMethods; using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; namespace GlobalPayments.Api.Terminals.Builders { public class TerminalManageBuilder : TerminalBuilder { diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs index 2e95db8c..de377899 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs @@ -1,5 +1,5 @@ using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using GlobalPayments.Api.Terminals.PAX; using System; using System.Linq; diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs index 7321baa0..80c00722 100644 --- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs +++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs @@ -4,7 +4,7 @@ using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Genius; using System.IO.Ports; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; namespace GlobalPayments.Api.Terminals { public enum ConnectionModes { diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 3bd48ae7..b36538d7 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -2,7 +2,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using GlobalPayments.Api.Terminals.Messaging; namespace GlobalPayments.Api.Terminals { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index f317ac04..6f4e46fa 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -6,7 +6,7 @@ using System; using System.Text; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class IngenicoController : DeviceController { IDeviceInterface _device; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 114d1b9d..b77cb787 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -8,7 +8,7 @@ using GlobalPayments.Api.Terminals.Builders; using GlobalPayments.Api.Utils; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class IngenicoInterface : DeviceInterface, IDeviceInterface { internal PaymentType? paymentMethod = null; internal IngenicoInterface(IngenicoController controller) : base(controller) { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 9497e180..356d2168 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -10,7 +10,7 @@ using System.Threading; using System.Threading.Tasks; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoSerialInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index 5b3ffdae..5b31ec27 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; //using Serilog; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoTcpInterface : IDeviceCommInterface { private TcpClient _client; private NetworkStream _stream; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs index 51f83d7b..c15fcfad 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/TcpListenerEx.cs @@ -1,7 +1,7 @@ using System.Net; using System.Net.Sockets; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class TcpListenerEx : TcpListener { public TcpListenerEx(IPAddress localaddr, int port) : base(localaddr, port) { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs index 8c174570..77a6e50a 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/BroadcastMessage.cs @@ -1,10 +1,10 @@ using GlobalPayments.Api.Entities; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using System; using System.Collections.Generic; using System.Text; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class BroadcastMessage { private byte[] _buffer; private string _code; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs index 2d69f371..e17badf0 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/CancelResponse.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Text; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class CancelResponse : IngenicoTerminalResponse, IDeviceResponse{ public CancelResponse(byte[] buffer) : base(buffer) { ParseResponse(buffer); diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs index a13e2f0d..0fc9d126 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Text; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using GlobalPayments.Api.Utils; using System.Linq; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class DataResponse { private string _authCode; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index bae1ce17..ad5868d7 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -6,7 +6,7 @@ using GlobalPayments.Api.Terminals; using GlobalPayments.Api.Utils; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public abstract class IngenicoBaseResponse : IDeviceResponse { public string Status { get; set; } public string Command { get; set; } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs index 3c4aee88..dfe7524f 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReverseResponse.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Text; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { public class ReverseResponse : IngenicoTerminalResponse, IDeviceResponse{ public ReverseResponse(byte[] buffer) : base(buffer) { ParseResponse(buffer); From 3ab6be4c5cac048e2c70ad41fd6509d8da575444 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:00:35 +0800 Subject: [PATCH 051/120] Update Cancel method Change Cancel return type to IDeviceResponse from TerminaAuthBuilder Remove paramater amount for Cancel. Update hanlding of cancel method in DeviceInterface, HpaInterface and PaxInterface classes. Update Test for cancel. --- .../Terminals/Abstractions/IDeviceInterface.cs | 2 +- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 7 +++---- src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs | 2 +- src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs | 2 +- .../Terminals/Ingenico/IngenicoTransactionTests.cs | 6 ++---- .../Terminals/Ingenico/SaleTransactionManagement.cs | 2 +- .../Terminals/Ingenico/TransactionTest.cs | 4 +--- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index fa7b2ad9..f06a548a 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -19,7 +19,7 @@ public interface IDeviceInterface : IDisposable { /// /// Amount to be passed for cancel request. /// TerminalManageBuilder - TerminalManageBuilder Cancel(decimal? amount = null); + IDeviceResponse Cancel(); IDeviceResponse CloseLane(); IDeviceResponse DisableHostResponseBeep(); ISignatureResponse GetSignatureFile(); diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index b36538d7..b29a28df 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -28,9 +28,8 @@ internal DeviceInterface(T controller) { } #region Admin Methods - public virtual TerminalManageBuilder Cancel(decimal? amount = null) { - return new TerminalManageBuilder(TransactionType.Cancel, PaymentMethodType.Credit) - .WithAmount(amount); + public virtual IDeviceResponse Cancel() { + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); } public virtual IDeviceResponse CloseLane() { @@ -108,7 +107,7 @@ public virtual TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptTy return new TerminalReportBuilder(type); } - public virtual TerminalReportBuilder GetReport(INGENICO.ReportType type) { + public virtual TerminalReportBuilder GetReport(Ingenico.ReportType type) { return new TerminalReportBuilder(TerminalReportType.LocalDetailReport).WithReportType(type); } diff --git a/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs index a149b9f9..9f4c4226 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs @@ -14,7 +14,7 @@ internal HpaInterface(HpaController controller) : base(controller) { } #region Admin Messages - public override TerminalManageBuilder Cancel(decimal? amount = null) { + public override IDeviceResponse Cancel() { // TODO: Cancel for HPA? Reset(); return null; diff --git a/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs index d96755af..fd37cd56 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs @@ -24,7 +24,7 @@ public override ISignatureResponse GetSignatureFile() { return new SignatureResponse(response, _controller.DeviceType.Value); } - public override TerminalManageBuilder Cancel(decimal? amount = null) { + public override IDeviceResponse Cancel() { if (_controller.ConnectionMode == ConnectionModes.HTTP) { throw new MessageException("The cancel command is not available in HTTP mode"); } diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs index 12d108fa..754a307d 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { @@ -60,9 +60,7 @@ public void CancelTest() { var tsk2 = Task.Factory.StartNew(() => { Thread.Sleep(7000); - var respCancel = _device.Cancel(15.12m) - .WithReferenceNumber(03) - .Execute(); + var respCancel = _device.Cancel(); Assert.IsNotNull(respCancel); }); diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs index f81593d2..61e2b85a 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs @@ -5,7 +5,7 @@ using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs index 98d08852..1e33301b 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs @@ -36,9 +36,7 @@ public void AsyncCancelTest() { var tsk2 = Task.Factory.StartNew(() => { Thread.Sleep(7000); - var respCancel = _device.Cancel(15.12m) - .WithReferenceNumber(03) - .Execute(); + var respCancel = _device.Cancel(); Assert.IsNotNull(respCancel); Assert.AreEqual(respCancel.Status, "CANCEL_DONE"); From ade3a3891daa82f7a475cad5790b98ce57b44945 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:02:45 +0800 Subject: [PATCH 052/120] Terminal Utilities update Rename BuildIngenicoRequest to BuildRequest method. Declare settings: parameter when calling BuildRequest. --- .../Terminals/INGENICO/IngenicoController.cs | 8 +-- .../Terminals/TerminalUtilities.cs | 59 ++++++++++--------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index 6f4e46fa..b26c490a 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -56,7 +56,7 @@ internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) { return ReportRequest(request); } else { - request = TerminalUtilities.BuildIngenicoRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), _settings.ConnectionMode); + request = TerminalUtilities.BuildRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), settings: _settings.ConnectionMode); return ReportRequest(request); } } @@ -126,7 +126,7 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { sb.Append("B01" + forceOnline); sb.Append(extendedData); - return TerminalUtilities.BuildIngenicoRequest(sb.ToString(), _settings.ConnectionMode); + return TerminalUtilities.BuildRequest(sb.ToString(), settings: _settings.ConnectionMode); } internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { @@ -183,13 +183,13 @@ internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { message = sb.ToString(); - return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + return TerminalUtilities.BuildRequest(message, settings: _settings.ConnectionMode); } internal IDeviceMessage BuildReportTransaction(TerminalReportBuilder builder) { if (!IsObjectNullOrEmpty(builder.ReportType)) { string message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); - return TerminalUtilities.BuildIngenicoRequest(message, _settings.ConnectionMode); + return TerminalUtilities.BuildRequest(message, settings: _settings.ConnectionMode); } else { throw new BuilderException("Type of report is missing in request."); diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs index f3af2040..020640f5 100644 --- a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs +++ b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs @@ -31,35 +31,6 @@ private static string GetElementString(object[] elements) { return sb.ToString(); } - public static DeviceMessage BuildIngenicoRequest(string message, ConnectionModes? settings) { - var buffer = new List(); - byte[] lrc; - - switch (settings) { - case ConnectionModes.SERIAL: - buffer.Add((byte)ControlCodes.STX); - foreach (char c in message) - buffer.Add((byte)c); - buffer.Add((byte)ControlCodes.ETX); - lrc = CalculateLRC(message); - buffer.Add(lrc[0]); - break; - case ConnectionModes.TCP_IP_SERVER: - var _msg = CalculateHeader(Encoding.UTF8.GetBytes(message)) + message; - - foreach (char c in _msg) - buffer.Add((byte)c); - - break; - case ConnectionModes.TCP_IP: - break; - default: - throw new BuilderException("Failed to build request message. Unknown Connection mode."); - } - - return new DeviceMessage(buffer.ToArray()); - } - private static DeviceMessage BuildMessage(string messageId, string message) { var buffer = new List(); @@ -91,6 +62,35 @@ private static DeviceMessage BuildMessage(string messageId, string message) { return new DeviceMessage(buffer.ToArray()); } + public static DeviceMessage BuildRequest(string message, ConnectionModes settings) { + var buffer = new List(); + byte[] lrc; + + switch (settings) { + case ConnectionModes.SERIAL: + buffer.Add((byte)ControlCodes.STX); + foreach (char c in message) + buffer.Add((byte)c); + buffer.Add((byte)ControlCodes.ETX); + lrc = CalculateLRC(message); + buffer.Add(lrc[0]); + break; + case ConnectionModes.TCP_IP_SERVER: + var _msg = CalculateHeader(Encoding.UTF8.GetBytes(message)) + message; + + foreach (char c in _msg) + buffer.Add((byte)c); + + break; + case ConnectionModes.TCP_IP: + break; + default: + throw new BuilderException("Failed to build request message. Unknown Connection mode."); + } + + return new DeviceMessage(buffer.ToArray()); + } + public static DeviceMessage BuildRequest(string message, MessageFormat format) { var buffer = new List(); @@ -148,6 +148,7 @@ public static byte[] BuildSignatureImage(string pathData, int width = 150) { Bitmap bmp = new Bitmap(width, 100); var gfx = Graphics.FromImage(bmp); + // TODO: Remove color from Utilities //gfx.Clear(Color.White); var index = 0; From 1b972602809c386ca7b161783ef29137e9aadcd4 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:03:16 +0800 Subject: [PATCH 053/120] Update cancel handling for Ingenico Interface --- .../Terminals/INGENICO/IngenicoInterface.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index b77cb787..453d54d6 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -69,11 +69,14 @@ public override TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptT #region Transaction Management - public override TerminalManageBuilder Cancel(decimal? amount = null) { - if (amount != null) { - return base.Cancel(amount); - } - else throw new BuilderException("Amount can't be null."); + public override IDeviceResponse Cancel() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.CANCEL); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new CancelResponse(response); } public override TerminalManageBuilder Reverse(decimal? amount = null) { From 2f56109a3fbb534bfc1936f3ae657c138cdb7cb2 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:03:59 +0800 Subject: [PATCH 054/120] Update summary for Authorize method. --- .../Terminals/Abstractions/IDeviceInterface.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index f06a548a..e717e7d6 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -50,7 +50,7 @@ public interface IDeviceInterface : IDisposable { /// /// Report Type /// - TerminalReportBuilder GetReport(INGENICO.ReportType type); + TerminalReportBuilder GetReport(ReportType type); #endregion #region Batch Calls @@ -90,9 +90,9 @@ public interface IDeviceInterface : IDisposable { TerminalAuthBuilder AddValue(decimal? amount = null); /// - /// Intructs the terminal to transact hotel mode pre-auth. + /// Intructs the terminal to transact a pre-autorization transaction. /// - /// Pre-auth Amount + /// Amount /// TerminalAuthBuilder Authorize(decimal? amount = null); TerminalAuthBuilder Balance(); From ba20d74fabc215114673fec0412d97d759de47a3 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:05:21 +0800 Subject: [PATCH 055/120] Update Ingenico Enums Add new const value for requst message with commands. This value will be ignored in the terminal. Upadate test. --- .../Terminals/INGENICO/IngenicoEnums.cs | 9 +++++++-- .../Ingenico/SerialPaymentTransactions.cs | 16 ++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 06dd36e0..771c0765 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -1,20 +1,25 @@  using System; -namespace GlobalPayments.Api.Terminals.INGENICO { +namespace GlobalPayments.Api.Terminals.Ingenico { internal class INGENICO_REQ_CMD { // Request Transactions public const string AUTHCODE = "AUTHCODE={0}"; public const string CASHBACK = "CASHB={0}"; // Request Commands + /** + * REQUEST_MESSAGE is hard-coded in order to fulfill the request message frame 3 + * and values in here are ignored in the terminal. + */ + public const string REQUEST_MESSAGE = "0100000001100826EXT0100000A010B010"; public const string CANCEL = "CMD=CANCEL"; public const string DUPLICATE = "CMD=DUPLIC"; public const string REVERSE = "CMD=REVERSE"; public const string REVERSE_WITH_ID = "CMD=REV{0}"; public const string TABLE_WITH_ID = "CMD=ID{0}"; - // Requesr Report + // Request Report public const string REPORT = "0100000001100826EXT0100000A010B010CMD={0}"; public const string RECEIPT = "0100000001100826EXT0100000A010B010CMD={0}"; } diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs index 3da741d8..698d23a0 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs @@ -2,11 +2,11 @@ using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; using GlobalPayments.Api.Terminals.Abstractions; -using GlobalPayments.Api.Terminals.INGENICO; +using GlobalPayments.Api.Terminals.Ingenico; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading; using System.Threading.Tasks; -using ReportType = GlobalPayments.Api.Terminals.INGENICO.ReportType; +using ReportType = GlobalPayments.Api.Terminals.Ingenico.ReportType; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { [TestClass] @@ -45,7 +45,7 @@ public void CaptureTest() { public void SaleTest() { var response = _device.Sale(6.18m) .WithReferenceNumber(1) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .WithTableNumber("1") .Execute(); @@ -66,7 +66,7 @@ public void Reverse() { public void SaleRefund() { var response = _device.Refund(6.18m) .WithReferenceNumber(1) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .WithTableNumber("1") .Execute(); @@ -78,7 +78,7 @@ public void SaleRefund() { public void preAuth() { var response = _device.Authorize(20.00m) .WithReferenceNumber(1) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .Execute(); @@ -90,7 +90,7 @@ public void SaleCancel() { var task1 = Task.Factory.StartNew(() => { var response = _device.Sale(523m) .WithReferenceNumber(1) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .Execute(); @@ -147,7 +147,7 @@ public void EOD() { public void Cashback() { var response = _device.Sale(20.00m) .WithReferenceNumber(01) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .WithCashBack(2.00m) .Execute(); @@ -171,7 +171,7 @@ public void AccountVerification() { public void test123() { var response = _device.Verify() .WithReferenceNumber(01) - .WithPaymentMode(Api.Terminals.INGENICO.PaymentMode.APPLICATION) + .WithPaymentMode(PaymentMode.APPLICATION) .WithCurrencyCode("826") .Execute(); From 0f59d78d55c1804c0ce0662b6ea368a0e3018036 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:19:31 +0800 Subject: [PATCH 056/120] Update IDeviceInterface Update Cancel and Authorize summary. Remove completion method --- .../Terminals/Abstractions/IDeviceInterface.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index e717e7d6..33543abb 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -15,7 +15,7 @@ public interface IDeviceInterface : IDisposable { #region Admin Calls /// - /// A method for Cancelling a live transaction. + /// A method to Cancel a live transaction. /// /// Amount to be passed for cancel request. /// TerminalManageBuilder @@ -90,7 +90,7 @@ public interface IDeviceInterface : IDisposable { TerminalAuthBuilder AddValue(decimal? amount = null); /// - /// Intructs the terminal to transact a pre-autorization transaction. + /// Instructs the terminal to transact a pre-authorization transaction. /// /// Amount /// @@ -99,14 +99,7 @@ public interface IDeviceInterface : IDisposable { TerminalManageBuilder Capture(decimal? amount = null); /// - /// Intructs the terminal to complete the completed hotel mode pre-auth transaction. - /// - /// Complete Amount/param> - /// - TerminalAuthBuilder Completion(decimal? amount = null); - - /// - /// Instruct the temrinal to refund the last completed transaction. + /// Instruct the terminal to refund the last completed transaction. /// /// Refund Amount /// From 684d42d619a376928eae1a3878927dfa55da9b8f Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 31 Mar 2020 12:24:44 +0800 Subject: [PATCH 057/120] Remove completion method for DeviceInterface and IngenicoInterface file. --- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 5 ----- .../Terminals/INGENICO/IngenicoInterface.cs | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index b29a28df..51fbb78f 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -138,11 +138,6 @@ public virtual TerminalAuthBuilder Sale(decimal? amount = null) { .WithAmount(amount); } - public virtual TerminalAuthBuilder Completion(decimal? amount = null) { - return new TerminalAuthBuilder(TransactionType.PreAuthCompletion, PaymentMethodType.Credit) - .WithAmount(amount); - } - public virtual TerminalAuthBuilder Verify() { return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit) .WithAmount(6.18m); diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 453d54d6..9f948efd 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -41,16 +41,6 @@ public override TerminalAuthBuilder Authorize(decimal? amount = null) { return base.Authorize(amount); } - /// - /// Completion method is the Hotel Mode Completion of Ingenico - /// - /// - /// - public override TerminalAuthBuilder Completion(decimal? amount = null) { - paymentMethod = PaymentType.CompletionMode; - return base.Completion(amount); - } - public override TerminalAuthBuilder Verify() { paymentMethod = PaymentType.AccountVerification; return base.Verify(); From a85708edabad37187fbf7dbd97eb5326c746b7ab Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 1 Apr 2020 11:48:23 +0800 Subject: [PATCH 058/120] Update unit test for Payment transaction. --- .../Terminals/DeviceInterface.cs | 2 +- .../Terminals/INGENICO/IngenicoEnums.cs | 2 +- .../Ingenico/IngenicoTransactionTests.cs | 134 ++++++++++-------- 3 files changed, 80 insertions(+), 58 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 51fbb78f..b7e138a2 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -137,7 +137,7 @@ public virtual TerminalAuthBuilder Sale(decimal? amount = null) { return new TerminalAuthBuilder(TransactionType.Sale, PaymentMethodType.Credit) .WithAmount(amount); } - + public virtual TerminalAuthBuilder Verify() { return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit) .WithAmount(6.18m); diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 771c0765..a5c31adf 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -5,7 +5,7 @@ namespace GlobalPayments.Api.Terminals.Ingenico { internal class INGENICO_REQ_CMD { // Request Transactions public const string AUTHCODE = "AUTHCODE={0}"; - public const string CASHBACK = "CASHB={0}"; + public const string CASHBACK = "CASHB={0};"; // Request Commands /** diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs index 754a307d..05e88c31 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs @@ -17,93 +17,115 @@ public IngenicoTransactionTests() { DeviceType = Entities.DeviceType.Ingenico_EPOS_Desk5000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", - Timeout = 60000, - RequestIdProvider = new RandomIdProvider() + Timeout = 60000 }); Assert.IsNotNull(_device); } [TestMethod] - public void SaleTest() { - try { - _device.Dispose(); - Thread.Sleep(25000); - - var resp = _device.Sale(15m) - .WithCashBack(2m) - .WithCurrencyCode("826") - .Execute(); - - Assert.IsNotNull(resp); - Assert.IsNotNull(resp.AuthorizationCode); - } - catch (Exception ex) { - Assert.Fail(ex.Message); - } - finally { - _device.Dispose(); - } + public void Test() { + SaleTest(); + RefundTest(); + PreAuthTest(); + CompletionTest(); } - [TestMethod] - public void CancelTest() { + public void SaleTest() { + _device.OnMessageSent += (message) => { + Assert.IsNotNull(message); + }; - var tsk1 = Task.Factory.StartNew(() => { - var respSale = _device.Sale(15.12m) + var resp = _device.Sale(6.18m) + .WithReferenceNumber(1) + .WithCurrencyCode("826") .WithCashBack(3m) - .WithReferenceNumber(02) .Execute(); - Assert.IsNotNull(respSale); - }); + Assert.IsNotNull(resp); - var tsk2 = Task.Factory.StartNew(() => { - Thread.Sleep(7000); + Thread.Sleep(5000); + } - var respCancel = _device.Cancel(); + public void RefundTest() { + _device.OnMessageSent += (message) => { + Assert.IsNotNull(message); + }; - Assert.IsNotNull(respCancel); - }); + var resp = _device.Refund(6.18m) + .WithReferenceNumber(1) + .WithCurrencyCode("826") + .WithCashBack(3m) + .Execute(); + + Assert.IsNotNull(resp); - Task.WaitAll(tsk1, tsk2); + Thread.Sleep(5000); } - [TestMethod] - public void ReverseTest() { + public void PreAuthTest() { + _device.OnMessageSent += (message) => { + Assert.IsNotNull(message); + }; - var resSale = _device.Sale(125.12m) - .WithReferenceNumber(55) + var resp = _device.Authorize(6.18m) + .WithReferenceNumber(1) + .WithCurrencyCode("826") + .WithCashBack(3m) .Execute(); - Thread.Sleep(10000); + Assert.IsNotNull(resp); + + Thread.Sleep(5000); + } - if (resSale != null) { - var resp = _device.Reverse(amount: 6.18m) - .WithReferenceNumber(12) + [TestMethod] + public void CompletionTest() { + _device.OnMessageSent += (message) => { + Assert.IsNotNull(message); + }; + + var resp = _device.Capture(6.18m) + .WithAuthCode("025433") + .WithReferenceNumber(1) + .WithCurrencyCode("826") .Execute(); - var termId = resp.TerminalRefNumber; + Assert.IsNotNull(resp); - Assert.IsNotNull(termId); - Assert.IsNotNull(resp); - } - else Assert.IsNull(resSale); + Thread.Sleep(5000); } [TestMethod] - public void DuplicateTest() { - var resp = _device.Duplicate(); - Assert.IsNotNull(resp); + public void AsyncCancelTest() { + + Thread thSale = new Thread(new ThreadStart(() => { + var resp = _device.Sale(6.18m) + .WithReferenceNumber(1) + .WithCurrencyCode("826") + .WithCashBack(3m) + .Execute(); + + + Assert.IsNotNull(resp, "Sale Assert"); + })); + + + Thread thCancel = new Thread(new ThreadStart(() => { + var resp = _device.Cancel(); + + Assert.IsNotNull(resp, "Cancel assert"); + })); + + thSale.Start(); + Thread.Sleep(2000); + thCancel.Start(); } [TestMethod] - public void ParsingRawResponseTest() { - string res = "09000015120 826CANCELDONE"; - byte[] buffers = Encoding.ASCII.GetBytes(res); - - var pResp = new DataResponse(ASCIIEncoding.ASCII.GetBytes(res.Substring(12, 55))); + public void CancelTest() { + var resp = _device.Cancel(); - Assert.IsNotNull(pResp); + Assert.IsNotNull(resp); } } } From 13090c21bf88e46e9137a9abdb6de65cb6fa67fa Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Thu, 2 Apr 2020 11:42:39 +0800 Subject: [PATCH 059/120] Update Report builder Remove duplicate enum for ReportType --- .../Terminals/Builders/TerminalReportBuilder.cs | 6 ++---- .../Terminals/INGENICO/IngenicoController.cs | 4 ++-- src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs | 7 ------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs index de377899..b1b2a9d6 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs @@ -9,9 +9,7 @@ namespace GlobalPayments.Api.Terminals.Builders { public class TerminalReportBuilder { internal TerminalReportType ReportType { get; set; } internal ReceiptType ReceiptType { get; set; } - - // TODO: Remove this property. Need to clarify with Russell. - internal ReportType? _ReportType { get; set; } + internal ReportType? Type { get; set; } private TerminalSearchBuilder _searchBuilder; internal TerminalSearchBuilder SearchBuilder { @@ -42,7 +40,7 @@ public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) { /// Report Type /// public TerminalReportBuilder WithReportType(ReportType reportType) { - _ReportType = reportType; + Type = reportType; return this; } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index b26c490a..a563d1fd 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -187,8 +187,8 @@ internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { } internal IDeviceMessage BuildReportTransaction(TerminalReportBuilder builder) { - if (!IsObjectNullOrEmpty(builder.ReportType)) { - string message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.ReportType); + if (!IsObjectNullOrEmpty(builder.Type)) { + string message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.Type); return TerminalUtilities.BuildRequest(message, settings: _settings.ConnectionMode); } else { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index a5c31adf..f52f8a08 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -87,13 +87,6 @@ public enum TransactionSubTypes { REFERRAL_RESULT = 0x82 // 0x52 byte for 'R' } - public enum ReportTypes { - BANKING, - EOD, - XBAL, - ZBAL - } - public enum TerminalStatus { NOT_READY = 0, READY = 1 From 60a875f4af2189b8b2b9189a1c6de10e2c6dd840 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 3 Apr 2020 19:08:51 +0800 Subject: [PATCH 060/120] Add temporary OnMessageReceived event for testing Update data receieved method for tcp connection mode. --- .../Abstractions/IDeviceCommInterface.cs | 3 + .../Abstractions/IDeviceInterface.cs | 6 +- .../Builders/TerminalReportBuilder.cs | 15 ++--- .../Terminals/DeviceController.cs | 5 ++ .../Terminals/DeviceInterface.cs | 8 ++- .../Genius/Interfaces/GeniusHttpInterface.cs | 1 + .../HPA/Interfaces/HpaTcpInterface.cs | 1 + .../Terminals/INGENICO/IngenicoController.cs | 18 ++++-- .../Interfaces/IngenicoSerialInterface.cs | 2 + .../Interfaces/IngenicoTcpInterface.cs | 56 +++++++++++++------ .../INGENICO/Responses/DeviceResponse.cs | 2 + .../Messaging/MessageSentEventHandler.cs | 1 + .../PAX/Interfaces/PaxHttpInterface.cs | 1 + .../PAX/Interfaces/PaxTcpInterface.cs | 1 + .../Terminals/TerminalUtilities.cs | 12 ++++ 15 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs index cb82d647..43380be7 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs @@ -11,5 +11,8 @@ public interface IDeviceCommInterface { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; + + // TODO: Remove this event + event MessageReceivedEventHandler OnMessageReceived; } } diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 33543abb..a2469e3f 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -11,6 +11,7 @@ namespace GlobalPayments.Api.Terminals { public interface IDeviceInterface : IDisposable { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; + event MessageReceivedEventHandler OnMessageReceived; #region Admin Calls @@ -39,14 +40,15 @@ public interface IDeviceInterface : IDisposable { #region reporting TerminalReportBuilder LocalDetailReport(); /// - /// Instruct the terminal to print the receipt of the last completed transaction. + /// Used to request the XML data for the last completed report that is stored in the terminal’s memory /// /// Receipt Type /// TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptType.TICKET); /// - /// Instruct the terminal to get the report in XML format of all the transactions. + /// Instruct the terminal to initiate report and stores it in terminal's memory. + /// GetLastReceipt can be used to extract XML data after. /// /// Report Type /// diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs index b1b2a9d6..454f4979 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs @@ -29,19 +29,12 @@ public TerminalReportBuilder(ReceiptType receiptType) { ReceiptType = receiptType; } - public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) { - return SearchBuilder.And(criteria, value); + public TerminalReportBuilder(ReportType reportType) { + Type = reportType; } - - /// - /// Sets the report type for the transaction. - /// - /// Report Type - /// - public TerminalReportBuilder WithReportType(ReportType reportType) { - Type = reportType; - return this; + public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) { + return SearchBuilder.And(criteria, value); } public ITerminalReport Execute(string configName = "default") { diff --git a/src/GlobalPayments.Api/Terminals/DeviceController.cs b/src/GlobalPayments.Api/Terminals/DeviceController.cs index cb2c5686..fdf47790 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceController.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceController.cs @@ -34,6 +34,7 @@ public IRequestIdProvider RequestIdProvider { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; internal DeviceController(ITerminalConfiguration settings) { _settings = settings; @@ -45,6 +46,10 @@ internal DeviceController(ITerminalConfiguration settings) { _connector.OnBroadcastMessage += (code, message) => { OnBroadcastMessage?.Invoke(code, message); }; + + _connector.OnMessageReceived += (message) => { + OnMessageReceived?.Invoke(message); + }; } public byte[] Send(IDeviceMessage message) { diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index b7e138a2..8ac2f104 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -12,6 +12,7 @@ public abstract class DeviceInterface : IDeviceInterface where T : DeviceCont public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; internal DeviceInterface(T controller) { _controller = controller; @@ -23,6 +24,10 @@ internal DeviceInterface(T controller) { OnBroadcastMessage?.Invoke(code, message); }; + _controller.OnMessageReceived += (message) => { + OnMessageReceived?.Invoke(message); + }; + _requestIdProvider = _controller.RequestIdProvider; } @@ -108,7 +113,8 @@ public virtual TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptTy } public virtual TerminalReportBuilder GetReport(Ingenico.ReportType type) { - return new TerminalReportBuilder(TerminalReportType.LocalDetailReport).WithReportType(type); + //return new TerminalReportBuilder(TerminalReportType.LocalDetailReport).WithReportType(type); + return new TerminalReportBuilder(type); } #endregion diff --git a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs index e9d2266e..cf0342b2 100644 --- a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs @@ -18,6 +18,7 @@ internal class GeniusHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; public GeniusHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs index 42886756..1bea0d96 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs @@ -17,6 +17,7 @@ internal class HpaTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; public HpaTcpInterface(ITerminalConfiguration settings) { this._settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index a563d1fd..f83fa5da 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -2,6 +2,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Terminals.Abstractions; using GlobalPayments.Api.Terminals.Builders; +using GlobalPayments.Api.Terminals.Ingenico.Responses; using GlobalPayments.Api.Utils; using System; using System.Text; @@ -51,13 +52,13 @@ internal override ITerminalResponse ManageTransaction(TerminalManageBuilder buil internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) { IDeviceMessage request; - if (builder._ReportType != null) { + if (builder.Type != null) { request = BuildReportTransaction(builder); return ReportRequest(request); } else { request = TerminalUtilities.BuildRequest(INGENICO_REQ_CMD.RECEIPT.FormatWith(builder.ReceiptType), settings: _settings.ConnectionMode); - return ReportRequest(request); + return XmlRequest(request); } } @@ -196,11 +197,16 @@ internal IDeviceMessage BuildReportTransaction(TerminalReportBuilder builder) { } } - internal IngenicoTerminalReportResponse ReportRequest(IDeviceMessage request) { - var send = Send(request); + internal IngenicoTerminalReportResponse XmlRequest(IDeviceMessage request) { + byte[] send = Send(request); return new IngenicoTerminalReportResponse(send); } + internal ReportResponse ReportRequest(IDeviceMessage request) { + byte[] send = Send(request); + return new ReportResponse(send); + } + #region Validations private bool IsObjectNullOrEmpty(object value) { bool response = false; @@ -215,8 +221,8 @@ private bool IsObjectNullOrEmpty(object value) { } private void ValidateTableId(string value) { - if (value.Length != 8) { - throw new BuilderException("The required length for table number is 8."); + if (value.Length > 8) { + throw new BuilderException("The maximum length for table number is 8."); } } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 356d2168..542eff20 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -14,6 +14,7 @@ namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoSerialInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; ITerminalConfiguration _settings; @@ -242,6 +243,7 @@ private bool MessageReceived(string messageData) { foreach (char b in messageData) { _messageResponse.Add((byte)b); } + OnMessageReceived?.Invoke(Encoding.UTF8.GetString(_messageResponse.ToArray())); return true; } } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index 5b31ec27..0a69ad2e 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -9,7 +9,6 @@ using GlobalPayments.Api.Entities; using System.Threading; using System.Threading.Tasks; -//using Serilog; namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoTcpInterface : IDeviceCommInterface { @@ -25,6 +24,7 @@ internal class IngenicoTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; public IngenicoTcpInterface(ITerminalConfiguration settings) { _settings = settings; @@ -169,31 +169,53 @@ private byte[] KeepAliveResponse(byte[] buffer) { } } - private void AnalyzeReceivedData() { + private async void AnalyzeReceivedData() { try { var headerBuffer = new byte[2]; while (_stream.CanRead && _listener.Active && _client.Connected) { - _stream.ReadAsync(headerBuffer, 0, headerBuffer.Length).Wait(); + await _stream.ReadAsync(headerBuffer, 0, headerBuffer.Length); - var dataLength = Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)).Result; - byte[] dataBuffer = new byte[dataLength + 2]; + int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); + if (dataLength > 0) { + byte[] dataBuffer = new byte[dataLength]; - // Read data - _stream.ReadAsync(dataBuffer, 0, dataBuffer.Length).Wait(); + var incomplete = true; + int offset = 0; + int tempLength = dataLength; - if (isBroadcast(dataBuffer)) { - _broadcastMessage = new BroadcastMessage(dataBuffer); - OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); - } - else if (isKeepAlive(dataBuffer) && INGENICO_GLOBALS.KeepAlive) { - var keepAliveRep = KeepAliveResponse(dataBuffer); - _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); + do { + + // Read data + int bytesReceived = await _stream.ReadAsync(dataBuffer, offset, tempLength); + if (bytesReceived != tempLength) { + offset += bytesReceived; + tempLength -= bytesReceived; + } + else { + incomplete = false; + } + } while (incomplete); + + var readBuffer = new byte[dataLength]; + Array.Copy(dataBuffer, readBuffer, dataLength); + + if (isBroadcast(readBuffer)) { + _broadcastMessage = new BroadcastMessage(readBuffer); + OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); + } + else if (isKeepAlive(readBuffer) && INGENICO_GLOBALS.KeepAlive) { + var keepAliveRep = KeepAliveResponse(readBuffer); + _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); + } + else { // Receiving request response data. + OnMessageReceived?.Invoke(UTF8Encoding.UTF8.GetString(readBuffer)); + termResponse = readBuffer; + } } - else { // Receiving request response data. - termResponse = dataBuffer; + else { + throw new ApiException("No data received."); } - headerBuffer = new byte[2]; } } catch (Exception ex) { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index ad5868d7..8cc81da0 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -120,5 +120,7 @@ internal IngenicoTerminalReportResponse(byte[] buffer) { public override string ToString() { return Encoding.ASCII.GetString(_buffer); } + + } } \ No newline at end of file diff --git a/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs b/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs index 115c5aba..6a1ba1ad 100644 --- a/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs +++ b/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs @@ -1,3 +1,4 @@ namespace GlobalPayments.Api.Terminals.Messaging { public delegate void MessageSentEventHandler(string message); + public delegate void MessageReceivedEventHandler(string message); } diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs index 6c318d20..901ffbbc 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs @@ -14,6 +14,7 @@ internal class PaxHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; public PaxHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs index 4a4810be..083c0335 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs @@ -15,6 +15,7 @@ internal class PaxTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event MessageReceivedEventHandler OnMessageReceived; public PaxTcpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs index 020640f5..d8b5a581 100644 --- a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs +++ b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs @@ -199,6 +199,18 @@ public static int HeaderLength(byte[] buffer) { return int.Parse(_hex, System.Globalization.NumberStyles.HexNumber); } + public static void HeaderLength(byte[] buffer, out int result) { + // Conversion from decimal to hex value + var fHex = Convert.ToInt64(buffer[0]).ToString("X2"); + var sHex = Convert.ToInt64(buffer[1]).ToString("X2"); + + // Concat two hex value + var _hex = fHex + sHex; + + // Get decimal value of concatenated hex + result = int.Parse(_hex, System.Globalization.NumberStyles.HexNumber); + } + public static byte[] CalculateLRC(string requestMessage) { byte[] bytes = Encoding.ASCII.GetBytes((requestMessage + (char)ControlCodes.ETX)); byte lrc = 0; From cbcb2b133d3b819ff1bae855e90be12ae7715894 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 3 Apr 2020 19:10:19 +0800 Subject: [PATCH 061/120] Create ReportResponse file Hande response from terminal when request is about report --- .../INGENICO/Responses/ReportResponse.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs new file mode 100644 index 00000000..1d4623b0 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs @@ -0,0 +1,16 @@ +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Utils; +using System.Text; + +namespace GlobalPayments.Api.Terminals.Ingenico.Responses { + public class ReportResponse : IngenicoTerminalResponse, ITerminalReport { + internal ReportResponse(byte[] buffer) : base(buffer) { + ParseResponse(buffer); + } + + public override void ParseResponse(byte[] response) { + base.ParseResponse(response); + _privateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); + } + } +} From 70063c50385c060be37f29fd98a5ca8ebe8985e0 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 6 Apr 2020 13:46:50 +0800 Subject: [PATCH 062/120] Update Duplic request Command Change return type of Duplicate Command from TerminalManageBuilder to IDeviceResponse --- .../Terminals/Abstractions/IDeviceInterface.cs | 13 +++++++------ src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 8 ++++---- .../Terminals/INGENICO/IngenicoInterface.cs | 10 ++++++++-- .../Terminals/Ingenico/TransactionTest.cs | 7 +++---- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index a2469e3f..32f1a150 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -35,6 +35,13 @@ public interface IDeviceInterface : IDisposable { ISAFResponse SendStoreAndForward(); IDeviceResponse SetStoreAndForwardMode(bool enabled); IDeviceResponse StartCard(PaymentMethodType paymentMethodType); + + /// + /// The terminal immediately initiates a duplicate of the last completed transaction + /// + /// Amount to be passed for cancel request. + /// TerminalManageBuilder + IDeviceResponse Duplicate(); #endregion #region reporting @@ -132,12 +139,6 @@ public interface IDeviceInterface : IDisposable { /// TerminalManageBuilder TerminalManageBuilder Reverse(decimal? amount = null); - /// - /// The terminal immediately initiates a duplicate of the last completed transaction - /// - /// Amount to be passed for cancel request. - /// TerminalManageBuilder - TerminalManageBuilder Duplicate(decimal? amount = null); #endregion } } diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 8ac2f104..72b085b7 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -92,6 +92,10 @@ public virtual IDeviceResponse SetStoreAndForwardMode(bool enabled) { public virtual IDeviceResponse StartCard(PaymentMethodType paymentMethodType) { throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); } + + public virtual IDeviceResponse Duplicate() { + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + } #endregion #region Batching @@ -170,10 +174,6 @@ public virtual TerminalManageBuilder Reverse(decimal? amount = null) { return new TerminalManageBuilder(TransactionType.Reversal, PaymentMethodType.Credit) .WithAmount(amount); } - public virtual TerminalManageBuilder Duplicate(decimal? amount = null) { - return new TerminalManageBuilder(TransactionType.Duplicate, PaymentMethodType.Credit) - .WithAmount(amount); - } #endregion #endregion diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 9f948efd..17d1e9fb 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -79,8 +79,14 @@ public override TerminalManageBuilder Reverse(decimal? amount = null) { ///// ///// Duplicate falls under lost transaction recovery and we have mechanisms for this which we'll need to look into further ///// - public override TerminalManageBuilder Duplicate(decimal? amount = null) { - return base.Duplicate(amount); + public override IDeviceResponse Duplicate() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.DUPLICATE); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new IngenicoTerminalResponse(response); } #endregion diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs index 1e33301b..0483167a 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs @@ -3,6 +3,7 @@ using GlobalPayments.Api.Entities; using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Ingenico; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { @@ -75,13 +76,11 @@ public void ReverseTest() { [TestMethod] public void DuplicTest() { - var duplicate = _device.Duplicate(12.5m) - .WithReferenceNumber(39) - .Execute(); + var duplicate = _device.Duplicate(); _device.Dispose(); Assert.IsNotNull(duplicate); - Assert.AreEqual(duplicate.TransactionAmount, 12.5m); + Assert.AreEqual(((IngenicoTerminalResponse)duplicate).PrivateData, "DUPLICDONE"); } } } From 2ab5fc301cf2bdd8c03d1f3bebe71cd474f82639 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 6 Apr 2020 15:05:24 +0800 Subject: [PATCH 063/120] Remove temporary MessageReceivedEventHandler --- .../Terminals/Abstractions/IDeviceCommInterface.cs | 3 --- .../Terminals/Abstractions/IDeviceInterface.cs | 1 - src/GlobalPayments.Api/Terminals/DeviceController.cs | 5 ----- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 5 ----- .../Terminals/Genius/Interfaces/GeniusHttpInterface.cs | 1 - .../Terminals/HPA/Interfaces/HpaTcpInterface.cs | 1 - .../Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs | 2 -- .../Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs | 2 -- .../Terminals/Messaging/MessageSentEventHandler.cs | 1 - .../Terminals/PAX/Interfaces/PaxHttpInterface.cs | 1 - .../Terminals/PAX/Interfaces/PaxTcpInterface.cs | 1 - 11 files changed, 23 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs index 43380be7..cb82d647 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs @@ -11,8 +11,5 @@ public interface IDeviceCommInterface { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; - - // TODO: Remove this event - event MessageReceivedEventHandler OnMessageReceived; } } diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 32f1a150..67cb51c0 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -11,7 +11,6 @@ namespace GlobalPayments.Api.Terminals { public interface IDeviceInterface : IDisposable { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; - event MessageReceivedEventHandler OnMessageReceived; #region Admin Calls diff --git a/src/GlobalPayments.Api/Terminals/DeviceController.cs b/src/GlobalPayments.Api/Terminals/DeviceController.cs index fdf47790..cb2c5686 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceController.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceController.cs @@ -34,7 +34,6 @@ public IRequestIdProvider RequestIdProvider { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; internal DeviceController(ITerminalConfiguration settings) { _settings = settings; @@ -46,10 +45,6 @@ internal DeviceController(ITerminalConfiguration settings) { _connector.OnBroadcastMessage += (code, message) => { OnBroadcastMessage?.Invoke(code, message); }; - - _connector.OnMessageReceived += (message) => { - OnMessageReceived?.Invoke(message); - }; } public byte[] Send(IDeviceMessage message) { diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 72b085b7..7316b08f 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -12,7 +12,6 @@ public abstract class DeviceInterface : IDeviceInterface where T : DeviceCont public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; internal DeviceInterface(T controller) { _controller = controller; @@ -24,10 +23,6 @@ internal DeviceInterface(T controller) { OnBroadcastMessage?.Invoke(code, message); }; - _controller.OnMessageReceived += (message) => { - OnMessageReceived?.Invoke(message); - }; - _requestIdProvider = _controller.RequestIdProvider; } diff --git a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs index cf0342b2..e9d2266e 100644 --- a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs @@ -18,7 +18,6 @@ internal class GeniusHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; public GeniusHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs index 1bea0d96..42886756 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs @@ -17,7 +17,6 @@ internal class HpaTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; public HpaTcpInterface(ITerminalConfiguration settings) { this._settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 542eff20..356d2168 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -14,7 +14,6 @@ namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoSerialInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; ITerminalConfiguration _settings; @@ -243,7 +242,6 @@ private bool MessageReceived(string messageData) { foreach (char b in messageData) { _messageResponse.Add((byte)b); } - OnMessageReceived?.Invoke(Encoding.UTF8.GetString(_messageResponse.ToArray())); return true; } } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index 0a69ad2e..fe8efa7f 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -24,7 +24,6 @@ internal class IngenicoTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; public IngenicoTcpInterface(ITerminalConfiguration settings) { _settings = settings; @@ -209,7 +208,6 @@ private async void AnalyzeReceivedData() { _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); } else { // Receiving request response data. - OnMessageReceived?.Invoke(UTF8Encoding.UTF8.GetString(readBuffer)); termResponse = readBuffer; } } diff --git a/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs b/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs index 6a1ba1ad..115c5aba 100644 --- a/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs +++ b/src/GlobalPayments.Api/Terminals/Messaging/MessageSentEventHandler.cs @@ -1,4 +1,3 @@ namespace GlobalPayments.Api.Terminals.Messaging { public delegate void MessageSentEventHandler(string message); - public delegate void MessageReceivedEventHandler(string message); } diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs index 901ffbbc..6c318d20 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs @@ -14,7 +14,6 @@ internal class PaxHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; public PaxHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs index 083c0335..4a4810be 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs @@ -15,7 +15,6 @@ internal class PaxTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; - public event MessageReceivedEventHandler OnMessageReceived; public PaxTcpInterface(ITerminalConfiguration settings) { _settings = settings; From 5d7231ea02187a2b4c4022150e630bffdedd8d0c Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 6 Apr 2020 15:43:46 +0800 Subject: [PATCH 064/120] Remove Cancel and Duplicate enums item. Remove handling of Cancel and Duplicate from Controller --- src/GlobalPayments.Api/Entities/Enums.cs | 12 +----------- .../Terminals/INGENICO/IngenicoController.cs | 13 +------------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs index fd138758..cd0aed0f 100644 --- a/src/GlobalPayments.Api/Entities/Enums.cs +++ b/src/GlobalPayments.Api/Entities/Enums.cs @@ -368,17 +368,7 @@ public enum TransactionType : long{ /// DccRateLookup = 1 << 31, - Increment = 1L << 32, - - /// - /// Indicate that latest transaction will be duplicate. - /// - Duplicate = 1L << 33, - - /// - /// Indicate that the current transaction will be cancelled. - /// - Cancel = 1L << 34 + Increment = 1L << 32 } /// diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index f83fa5da..e21f4794 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -39,10 +39,7 @@ internal override IDeviceCommInterface ConfigureConnector() { internal override ITerminalResponse ManageTransaction(TerminalManageBuilder builder) { IDeviceMessage request = BuildManageTransaction(builder); - if (builder.TransactionType == TransactionType.Cancel) { - return DoCancelRequest(request); - } - else if (builder.TransactionType == TransactionType.Reversal) { + if (builder.TransactionType == TransactionType.Reversal) { return DoReverseRequest(request); } else { @@ -101,14 +98,6 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) { extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(builder.TransactionId); } - // TODO: Remove Cancel, Duplicate, Reverse here this should be on Interface - // Temporary for CANCEL. - else if (builder.TransactionType == TransactionType.Cancel) { - extendedData = INGENICO_REQ_CMD.CANCEL; - } - else if (builder.TransactionType == TransactionType.Duplicate) { - extendedData = INGENICO_REQ_CMD.DUPLICATE; - } else if (builder.TransactionType == TransactionType.Reversal) { extendedData = INGENICO_REQ_CMD.REVERSE; } From de6f3c63a0b8c0ca7efaca16e2f695a8012547af Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 6 Apr 2020 15:45:07 +0800 Subject: [PATCH 065/120] Change to its original block which is throw exception. --- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 7316b08f..537f19c1 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -105,7 +105,7 @@ public virtual IEODResponse EndOfDay() { #region Reporting Methods public virtual TerminalReportBuilder LocalDetailReport() { - return new TerminalReportBuilder(TerminalReportType.LocalDetailReport); + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); } public virtual TerminalReportBuilder GetLastReceipt(ReceiptType type = ReceiptType.TICKET) { return new TerminalReportBuilder(type); From 5c7af75c048a91817804c676009c9e502b4bae89 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 6 Apr 2020 15:45:49 +0800 Subject: [PATCH 066/120] Remove ReportData property for HPA and PAX responses handling. --- .../Terminals/Abstractions/IDeviceResponse.cs | 4 +--- .../Terminals/HPA/Responses/SipBaseResponse.cs | 2 -- .../Terminals/PAX/Responses/DeviceResponse.cs | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs index 93a63f04..dee676d3 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs @@ -51,7 +51,5 @@ public interface ITerminalResponse : IDeviceResponse { decimal? MerchantFee { get; set; } } - public interface ITerminalReport : IDeviceResponse { - - } + public interface ITerminalReport : IDeviceResponse { } } diff --git a/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs b/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs index 11a7f6e4..a33768a3 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Responses/SipBaseResponse.cs @@ -256,7 +256,5 @@ internal SipTerminalResponse(byte[] buffer, params string[] messageIds) : base(b public class SipTerminalReport : SipBaseResponse, ITerminalReport { internal SipTerminalReport(byte[] buffer, params string[] messageIds) : base(buffer, messageIds) { } - - public string ReportData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } } diff --git a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs index b41523d8..0e544eba 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs @@ -327,8 +327,6 @@ public class PaxTerminalReport : PaxBaseResponse, ITerminalReport { internal PaxTerminalReport(byte[] buffer, params string[] messageIds) : base(buffer, messageIds) { } - public string ReportData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - protected virtual void MapResponse() { } } } From d2b359b38a7d3572f69fdbd475b52df210af38cb Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 5 May 2020 12:43:28 +0800 Subject: [PATCH 067/120] Handle timeout exception for Tcp Communication Create class variable of general exception. Assigned any exception thrown from other thread to class variable. Throw exception in send method. --- .../Interfaces/IngenicoTcpInterface.cs | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index fe8efa7f..d88811a3 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -21,6 +21,9 @@ internal class IngenicoTcpInterface : IDeviceCommInterface { private BroadcastMessage _broadcastMessage; private byte[] termResponse; private Thread dataReceiving; + private bool _isKeepAlive; + private bool _isKeepAliveRunning; + private Exception _receivingException; public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; @@ -84,6 +87,10 @@ public byte[] Send(IDeviceMessage message) { throw new ConfigurationException("Server is not running."); } + // Validate keep alive for setting of timeout during Transaction + if (!_isKeepAlive) { + _stream.ReadTimeout = _settings.Timeout; + } if (_ipAddresses.Count > 0) { _stream.WriteAsync(buffer, 0, buffer.Length).Wait(); @@ -92,7 +99,19 @@ public byte[] Send(IDeviceMessage message) { while (termResponse == null) { Thread.Sleep(100); + if (_receivingException != null) { + Exception ex = _receivingException; + _receivingException = null; + throw ex; + } + if (termResponse != null) { + + // Remove timeout for stream read + if (!_isKeepAlive) { + _stream.ReadTimeout = -1; + } + return termResponse; } } @@ -119,7 +138,10 @@ private void InitializeServer() { // Set timeout for client to send data. _server = _listener.Server; - _server.ReceiveTimeout = _settings.Timeout; + + // Initialize keep Alive value to false. + _isKeepAlive = false; + _isKeepAliveRunning = false; } else { throw new ConfigurationException("Server already initialize."); @@ -129,6 +151,8 @@ private void InitializeServer() { private void AcceptingClient() { _client = _listener.AcceptTcpClient(); _stream = _client.GetStream(); + + _ipAddresses.Add(((IPEndPoint)_client.Client.RemoteEndPoint).Address); // Start thread for handling keep alive request. @@ -171,9 +195,9 @@ private byte[] KeepAliveResponse(byte[] buffer) { private async void AnalyzeReceivedData() { try { var headerBuffer = new byte[2]; - while (_stream.CanRead && _listener.Active && _client.Connected) { + while (true) { - await _stream.ReadAsync(headerBuffer, 0, headerBuffer.Length); + _stream.Read(headerBuffer, 0, headerBuffer.Length); int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); if (dataLength > 0) { @@ -186,7 +210,7 @@ private async void AnalyzeReceivedData() { do { // Read data - int bytesReceived = await _stream.ReadAsync(dataBuffer, offset, tempLength); + int bytesReceived = _stream.Read(dataBuffer, offset, tempLength); if (bytesReceived != tempLength) { offset += bytesReceived; tempLength -= bytesReceived; @@ -204,6 +228,14 @@ private async void AnalyzeReceivedData() { OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); } else if (isKeepAlive(readBuffer) && INGENICO_GLOBALS.KeepAlive) { + + _isKeepAlive = true; + + if (_isKeepAlive && !_isKeepAliveRunning) { + _stream.ReadTimeout = _settings.Timeout; + _isKeepAliveRunning = true; + } + var keepAliveRep = KeepAliveResponse(readBuffer); _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); } @@ -212,14 +244,12 @@ private async void AnalyzeReceivedData() { } } else { - throw new ApiException("No data received."); + _receivingException = new ApiException("No data received."); } } } catch (Exception ex) { - if (_stream.CanRead && _listener.Active && _client.Connected) { - throw new ApiException("Unable to get data from terminal. " + ex.Message); - } + _receivingException = ex; } } #endregion From 158a9f803352e61ea96c30077a04749d883ec1fd Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 5 May 2020 20:20:50 +0800 Subject: [PATCH 068/120] Implementation of Partial Taxfree refund structure Add TaxFreeType enum in IngenicoEnums file. Create WithTaxFree method in TerminalAuthBuilder file. Handle passed value of taxFreeType from WithTaxFree method in IngenicoController file. Create Unit test for partial taxFree refund implementaion. --- .../Terminals/Builders/TerminalAuthBuilder.cs | 13 +++++++ .../Terminals/INGENICO/IngenicoController.cs | 10 +++++ .../Terminals/INGENICO/IngenicoEnums.cs | 5 +++ .../Ingenico/IngenicoTransactionTests.cs | 38 ++++++++++++++++++- 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index 4c67fd9a..c42020a0 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -41,6 +41,7 @@ internal string TransactionId { internal string CurrencyCode { get; set; } internal PaymentMode PaymentMode { get; set; } internal string TableNumber { get; set; } + internal TaxFreeType? TaxFreeType { get; set; } public TerminalAuthBuilder WithAddress(Address address) { Address = address; @@ -175,6 +176,18 @@ public TerminalAuthBuilder WithTableNumber(string value) { return this; } + /// + /// Method used for requesting a Tax Free Refund Payment type transaction. + /// + /// + /// Payment Type of refund. Either Cash or Credit + /// + /// + public TerminalAuthBuilder WithTaxFree(TaxFreeType taxFreeType) { + TaxFreeType = taxFreeType; + return this; + } + internal TerminalAuthBuilder(TransactionType type, PaymentMethodType paymentType) : base(type, paymentType) { } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index e21f4794..e79d04bf 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -141,6 +141,16 @@ internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { referenceNumber = RequestIdProvider.GetRequestId(); } amount = ValidateAmount(amount); + + // Tax free Refund handling + if (paymentType == (int)PaymentType.Refund && builder.TaxFreeType == TaxFreeType.CASH) { + paymentType = (int)PaymentType.TaxFreeCashRefund; + } + else if (paymentType == (int)PaymentType.Refund && builder.TaxFreeType == TaxFreeType.CREDIT) { + paymentType = (int)PaymentType.TaxFreeCreditRefund; + + } + paymentMode = ValidatePaymentMode(builder.PaymentMode); currencyCode = ValidateCurrency((string.IsNullOrEmpty(builder.CurrencyCode) ? currencyCode : builder.CurrencyCode)); diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index f52f8a08..d14a56af 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -119,4 +119,9 @@ public enum PaymentMode { APPLICATION = 0, MAILORDER = 1 } + + public enum TaxFreeType { + CREDIT = 0, + CASH = 1 + } } \ No newline at end of file diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs index 05e88c31..5601183c 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs @@ -2,6 +2,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using GlobalPayments.Api.Entities; using GlobalPayments.Api.Services; using GlobalPayments.Api.Terminals; using GlobalPayments.Api.Terminals.Ingenico; @@ -17,7 +18,7 @@ public IngenicoTransactionTests() { DeviceType = Entities.DeviceType.Ingenico_EPOS_Desk5000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", - Timeout = 60000 + Timeout = 30 * 1000 }); Assert.IsNotNull(_device); } @@ -102,6 +103,7 @@ public void AsyncCancelTest() { var resp = _device.Sale(6.18m) .WithReferenceNumber(1) .WithCurrencyCode("826") + .WithTaxFree(TaxFreeType.CASH) .WithCashBack(3m) .Execute(); @@ -127,5 +129,39 @@ public void CancelTest() { Assert.IsNotNull(resp); } + + [TestMethod] + public void TaxFreeCreditCardRefundTest() { + try { + var respone = _device.Refund(5m) + .WithTaxFree(TaxFreeType.CREDIT) + .Execute(); + + Assert.IsNotNull(respone); + } + catch (ApiException e) { + Assert.Fail(e.Message); + //throw e; + } + catch (Exception e) { + Assert.Fail(e.Message); + //throw e; + } + } + + [TestMethod] + public void TaxFreeCashRefundTest() { + try { + var respone = _device.Refund(5m) + .WithReferenceNumber(1) + .WithTaxFree(TaxFreeType.CASH) + .Execute(); + + Assert.IsNotNull(respone); + } + catch (Exception e) { + Assert.Fail(e.Message); + } + } } } From f94bf5c349a6ffa4be82f6225c5302bd17df1d77 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 6 May 2020 16:09:06 +0800 Subject: [PATCH 069/120] Handle raw response from terminal itselft --- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index 8cc81da0..a28b2327 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -25,11 +25,17 @@ public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse internal string terminalRawData; internal string _currencyCode; internal DataResponse _respField; + private byte[] _buffer; internal IngenicoTerminalResponse(byte[] buffer) { + _buffer = buffer; ParseResponse(buffer); } + public override string ToString() { + return Encoding.UTF8.GetString(_buffer, 0, _buffer.Length); + } + #region Added Properties Specific for Ingenico public string DccCurrency { get { return _respField.DccCode; } set { } } public DynamicCurrencyStatus? DccStatus { get { return _respField.DccStatus; } set { } } From 7dad4bab15f418eebe644db98ab7b9f62773dad5 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 18:46:32 +0800 Subject: [PATCH 070/120] Update DeviceInterface adapter. Handle/Implement GetTerminalStatus method in DeviceInterface. --- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 537f19c1..7267b7fc 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -91,6 +91,10 @@ public virtual IDeviceResponse StartCard(PaymentMethodType paymentMethodType) { public virtual IDeviceResponse Duplicate() { throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); } + + public virtual IDeviceResponse GetTerminalStatus() { + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + } #endregion #region Batching From e5c25c41904585a106b087f5ffe81c43e928be37 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 18:48:34 +0800 Subject: [PATCH 071/120] Declare GetTerminalStatus in IDeviceInterface. --- .../Terminals/Abstractions/IDeviceInterface.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 67cb51c0..144d251c 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -41,6 +41,12 @@ public interface IDeviceInterface : IDisposable { /// Amount to be passed for cancel request. /// TerminalManageBuilder IDeviceResponse Duplicate(); + + /// + /// Command used to gain feedback as to the status of the terminal. + /// + /// + IDeviceResponse GetTerminalStatus(); #endregion #region reporting From 102eab39495eae0020a776112b228707c8faa946 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 18:52:01 +0800 Subject: [PATCH 072/120] Update IngenicoInterface Handle GetTerminalStatus method in Ingenico Interface. Build request message for STATE Command. --- .../Terminals/INGENICO/IngenicoInterface.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 17d1e9fb..9b097dae 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -14,6 +14,18 @@ public class IngenicoInterface : DeviceInterface, IDeviceInt internal IngenicoInterface(IngenicoController controller) : base(controller) { } + #region Admin Methods + public override IDeviceResponse GetTerminalStatus() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.STATE); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new StateResponse(response); + } + #endregion + #region Payment Transaction Management public override TerminalAuthBuilder Sale(decimal? amount = null) { From bbbce4da1fa484bb3ca27b5a8bb72c27a89e3cb8 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 18:59:03 +0800 Subject: [PATCH 073/120] Update Ingenico enums. Add constant variable for STATE command. Declare RAW_RESPONSE_LENGHT for standard lenght of response data if ever passed response data from terminal is insufficient. Rename TerminalModes to SalesMode. Rename STANDARD_MODE to STANDARD_SALE_MODE. Add RepFieldCode in Ingenico Enum. Add StateResponse Code for TLV parsing. Add TLV Format enum. --- .../Terminals/INGENICO/IngenicoEnums.cs | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index d14a56af..dca030c8 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -19,6 +19,9 @@ internal class INGENICO_REQ_CMD { public const string REVERSE_WITH_ID = "CMD=REV{0}"; public const string TABLE_WITH_ID = "CMD=ID{0}"; + // Terminal Management Commands + public const string STATE = "CMD=STATE"; + // Request Report public const string REPORT = "0100000001100826EXT0100000A010B010CMD={0}"; public const string RECEIPT = "0100000001100826EXT0100000A010B010CMD={0}"; @@ -31,6 +34,7 @@ internal class INGENICO_GLOBALS { public const string KEEP_ALIVE_RESPONSE = " OK"; public static bool KeepAlive = true; public const int IP_PORT = 18101; + public const int RAW_RESPONSE_LENGTH = 80; } internal static class INGENICO_RESP { @@ -92,8 +96,8 @@ public enum TerminalStatus { READY = 1 } - public enum TerminalModes { - STANDARD_MODE = 0, + public enum SalesMode { + STANDARD_SALE_MODE = 0, VENDING_MODE = 1 } @@ -124,4 +128,38 @@ public enum TaxFreeType { CREDIT = 0, CASH = 1 } + + // Codes in Response field for TLV format + public enum RepFieldCode { + AuthCode = 67, // C + CashbackAmount = 90, // Z + GratuityAmount = 89, // Y + FinalTransactionAmount = 77, // M + AvailableAmount = 65, // A + DccCurrency = 85, // U + DccConvertedAmount = 79, // O + PaymentMethod = 80, // P + TransactionSubType = 84, // T + SplitSalePaidAmount = 83, // S + DccOperationStatus = 68, // D + } + + public enum StateResponseCode { + Status = 83, // S + AppVersionNumber = 86, // V + HandsetNumber = 72, // H + TerminalId = 84, // T + } + + public enum TLVFormat { + /// + /// Format for transaction request. + /// + Standard, + + /// + /// Format for State command request. + /// + State + } } \ No newline at end of file From f0a7e6d6896a20039cb6b8f6169097acb9b66aca Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 19:07:09 +0800 Subject: [PATCH 074/120] Refactor DataResponse file Remove private declaration of Response Code and move it to IngenicoEnums. Remove GetValueOfRespField method for parsing of response data. --- .../INGENICO/Responses/DataResponse.cs | 91 ++++--------------- 1 file changed, 16 insertions(+), 75 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs index 0fc9d126..be4df46c 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs @@ -22,22 +22,6 @@ public class DataResponse { private byte[] _buffer; - #region Byte Code - // For less memory allocation; - private byte _C = 67; - private byte _Z = 90; - private byte _Y = 89; - private byte _M = 77; - private byte _A = 65; - private byte _U = 85; - private byte _O = 79; - private byte _P = 80; - private byte _T = 84; - private byte _S = 83; - private byte _D = 68; - #endregion - - public DataResponse(byte[] buffer) { _buffer = buffer; ParseData(); @@ -101,67 +85,24 @@ public DynamicCurrencyStatus? DccStatus { #endregion - /** - * C = AuthCode - * Z = Cashback Amount - * Y = Gratuity Amount - * M = Final Transaction Amount - * A = Available Amount - * U = DCC Currency - * O = DCC Converted transaction amount - * P = Payment Method - * T = Transaction Sub-Type - * S = Split Sale Paid Amount - * D = DCC Operation Status - */ - private void ParseData() { - _authCode = (string)GetValueOfRespField(_C, typeof(string)); - _cashbackAmount = (decimal?)GetValueOfRespField(_Z, typeof(decimal?)); - _gratuityAmount = (decimal?)GetValueOfRespField(_Y, typeof(decimal?)); - _finalAmount = (decimal?)GetValueOfRespField(_M, typeof(decimal?)); - _availableAmount = (decimal?)GetValueOfRespField(_A, typeof(decimal?)); - _dccCode = (string)GetValueOfRespField(_U, typeof(string)); - _dccAmount = (decimal?)GetValueOfRespField(_O, typeof(decimal?)); - _txnSubType = (TransactionSubTypes?)GetValueOfRespField(_T, typeof(TransactionSubTypes?)); - _dccStatus = (DynamicCurrencyStatus?)GetValueOfRespField(_D, typeof(DynamicCurrencyStatus?)); - _splitSaleAmount = (decimal?)GetValueOfRespField(_S, typeof(decimal?)); - _paymentMethod = (PaymentMethod?)GetValueOfRespField(_P, typeof(PaymentMethod?)); - } - - private object GetValueOfRespField(byte toGet, Type returnType) { - var index = Array.FindIndex(_buffer, e => e == toGet); - if (index >= 0) { - // Get the length based on Documention (TLV). - byte[] lengthBuffer = { _buffer[index + 1], _buffer[index + 2] }; - var length = Convert.ToInt32(Encoding.UTF8.GetString(lengthBuffer, 0, lengthBuffer.Length), 16); - - var _arrValue = _buffer.SubArray(index + 3, length); ; - var endLength = index + length + 3; - _buffer = _buffer.SubArray(0, index).Concat(_buffer.SubArray(endLength, _buffer.Length - endLength)).ToArray(); - var strValue = Encoding.ASCII.GetString(_arrValue, 0, _arrValue.Length); - - - if (returnType == typeof(decimal?)) { - return decimal.Parse(strValue); - } - else if (returnType == typeof(string)) { - return strValue; - } - else if (returnType == typeof(TransactionSubTypes?)) { - return (TransactionSubTypes)int.Parse(Convert.ToInt64(_arrValue[0]).ToString("X2"), System.Globalization.NumberStyles.HexNumber); - } - else if (returnType == typeof(DynamicCurrencyStatus?)) { - return (DynamicCurrencyStatus)int.Parse(strValue); - } - else if (returnType == typeof(PaymentMethod?)) { - return (PaymentMethod)int.Parse(strValue); - } - else - throw new Exception("Data type not supported in parsing of response data."); - } - return null; + var tlv = new TypeLengthValue(_buffer); + + Type stringType = typeof(string); + Type decimalType = typeof(decimal?); + + _authCode = (string)tlv.GetValue((byte)RepFieldCode.AuthCode, stringType); + _cashbackAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.CashbackAmount, decimalType); + _gratuityAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.GratuityAmount, decimalType); + _finalAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.FinalTransactionAmount, decimalType); + _availableAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.AvailableAmount, decimalType); + _dccCode = (string)tlv.GetValue((byte)RepFieldCode.DccCurrency, stringType); + _dccAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.DccConvertedAmount, decimalType); + _txnSubType = (TransactionSubTypes?)tlv.GetValue((byte)RepFieldCode.TransactionSubType, typeof(TransactionSubTypes?)); + _dccStatus = (DynamicCurrencyStatus?)tlv.GetValue((byte)RepFieldCode.DccOperationStatus, typeof(DynamicCurrencyStatus?)); + _splitSaleAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.SplitSalePaidAmount, decimalType); + _paymentMethod = (PaymentMethod?)tlv.GetValue((byte)RepFieldCode.PaymentMethod, typeof(PaymentMethod?)); } } } \ No newline at end of file From 94ee47e6e3d5ed8ff4849a973f75967064f733b4 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 19:11:25 +0800 Subject: [PATCH 075/120] Update DeviceResponse file. Add optional parameter of parseRepField in IngenicoTerminalResponse constructor. Add validation for parsing of response data field in message frame 2 data. --- .../INGENICO/Responses/DeviceResponse.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index a28b2327..58586696 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -5,6 +5,7 @@ using System.Text; using GlobalPayments.Api.Terminals; using GlobalPayments.Api.Utils; +using System; namespace GlobalPayments.Api.Terminals.Ingenico { public abstract class IngenicoBaseResponse : IDeviceResponse { @@ -25,15 +26,19 @@ public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse internal string terminalRawData; internal string _currencyCode; internal DataResponse _respField; + private byte[] _buffer; + private bool _parsedResponseData; - internal IngenicoTerminalResponse(byte[] buffer) { + internal IngenicoTerminalResponse(byte[] buffer, bool parseRepField = true) { _buffer = buffer; + _parsedResponseData = parseRepField; ParseResponse(buffer); } public override string ToString() { - return Encoding.UTF8.GetString(_buffer, 0, _buffer.Length); + base.DeviceResponseText = Encoding.UTF8.GetString(_buffer, 0, _buffer.Length); + return base.DeviceResponseText; } #region Added Properties Specific for Ingenico @@ -102,15 +107,19 @@ public override string ToString() { */ public virtual void ParseResponse(byte[] response) { if (response != null) { + ReferenceNumber = Encoding.UTF8.GetString(response.SubArray(0, 2)); _transactionStatus = ((TransactionStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); decimal.TryParse(Encoding.UTF8.GetString(response.SubArray(3, 8)), out _amount); _paymentMode = (PaymentMode)Encoding.UTF8.GetString(response.SubArray(11, 1)).ToInt32(); - _respField = new DataResponse(response.SubArray(12, 55)); _currencyCode = Encoding.UTF8.GetString(response.SubArray(67, 3)); _privateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); - Status = _transactionStatus; + + // This is for parsing of Response field for Transaction request + if (_parsedResponseData) { + _respField = new DataResponse(response.SubArray(12, 55)); + } } } } From 9b715145a777df64144d7d6343da0de3a59a333d Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 19:16:24 +0800 Subject: [PATCH 076/120] Add util for parsing of TLV data Create class constructor to passed data to be parse. Create method name GetValue for parsing and getting value of passed response type/code. Create method for setting tlv parsing format. --- .../Utils/TypeLengthValue.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/GlobalPayments.Api/Utils/TypeLengthValue.cs diff --git a/src/GlobalPayments.Api/Utils/TypeLengthValue.cs b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs new file mode 100644 index 00000000..22cc768f --- /dev/null +++ b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs @@ -0,0 +1,81 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Terminals.Ingenico; +using System; +using System.Linq; +using System.Text; + +namespace GlobalPayments.Api.Utils { + internal class TypeLengthValue { + + private byte[] _data = new byte[0]; + private TLVFormat _format = TLVFormat.Standard; + + public TypeLengthValue() { + + } + + public TypeLengthValue(byte[] data) { + _data = data; + } + + // Add TLV Format since Ingenico has different format of length when it comes to TLV Standard. + public TLVFormat TLVFormat { + get { return _format; } + set { _format = value; } + } + + + public object GetValue(byte type, Type returnType, TLVFormat? format = null) { + if (_data.Length == 0) { + throw new Exception("No data to parse."); + } + + int typeIndexLocation = Array.FindIndex(_data, e => e == type); + if (typeIndexLocation >= 0) { + // Get the length based on Documentation (TLV). + byte[] lengthBuffer = { _data[typeIndexLocation + 1], _data[typeIndexLocation + 2] }; + int length = 0; + + if ((format != null && format == TLVFormat.Standard) || _format == TLVFormat.Standard) { + length = Convert.ToInt32(Encoding.UTF8.GetString(lengthBuffer, 0, lengthBuffer.Length), 16); + } + else if ((format != null && format == TLVFormat.State) || _format == TLVFormat.State) { + length = Convert.ToInt32(Encoding.UTF8.GetString(lengthBuffer, 0, lengthBuffer.Length)); + } + else { + throw new ApiException("Unsupported TLV format."); + } + + // Get the value of type according to length limit. + byte[] value = _data.SubArray(typeIndexLocation + 3, length); + + int endLength = typeIndexLocation + length + 3; + + // Remove field that have been parsed and successfully get the value. + _data = _data.SubArray(0, typeIndexLocation).Concat(_data.SubArray(endLength, _data.Length - endLength)).ToArray(); + string strValue = Encoding.ASCII.GetString(value, 0, value.Length); + + + if (returnType == typeof(decimal?)) { + return decimal.Parse(strValue); + } + else if (returnType == typeof(string)) { + return strValue; + } + else if (returnType == typeof(TransactionSubTypes?)) { + return (TransactionSubTypes)int.Parse(Convert.ToInt64(value[0]).ToString("X2"), System.Globalization.NumberStyles.HexNumber); + } + else if (returnType == typeof(DynamicCurrencyStatus?)) { + return (DynamicCurrencyStatus)int.Parse(strValue); + } + else if (returnType == typeof(PaymentMethod?)) { + return (PaymentMethod)int.Parse(strValue); + } + else { + throw new Exception("Data type not supported in parsing of TLV data."); + } + } + return null; + } + } +} From 6405e7709ffe80e7ed98981545c753f8f8f1e5fa Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 19:18:08 +0800 Subject: [PATCH 077/120] Create StateResponse Class Override ParseResponse method from base class. Parse response data according to STATE response TLV format. --- .../INGENICO/Responses/StateResponse.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs new file mode 100644 index 00000000..c8315fd3 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs @@ -0,0 +1,59 @@ +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Terminals.Ingenico; +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.Ingenico { + public class StateResponse : IngenicoTerminalResponse, IDeviceResponse { + + private TerminalStatus _terminalStatus; + private SalesMode _salesMode; + private string _terminalCapabilities; + private string _additionalTerminalCapabilities; + private string _appVersionNumber; + private string _handsetNumber; + private string _terminalId; + + public StateResponse(byte[] buffer) + : base(buffer, false) { + } + + public TerminalStatus TerminalStatus { get { return _terminalStatus; } set { } } + public SalesMode SalesMode { get { return _salesMode; } set { } } + public string TerminalCapabilities { get { return _terminalCapabilities; } set { } } + public string AdditionalTerminalCapabilities { get { return _additionalTerminalCapabilities; } set { } } + public string AppVersionNumber { get { return _appVersionNumber; } set { } } + public string HandsetNumber { get { return _handsetNumber; } set { } } + public string TerminalId { get { return _terminalId; } set { } } + + public override void ParseResponse(byte[] response) { + if (response == null) { + throw new ApiException("Response data is null"); + } + + if (response.Length < INGENICO_GLOBALS.RAW_RESPONSE_LENGTH) { + byte[] newResponse = new byte[INGENICO_GLOBALS.RAW_RESPONSE_LENGTH]; + response.CopyTo(newResponse, 0); + + response = newResponse; + } + + base.ParseResponse(response); + + var tlv = new TypeLengthValue(response.SubArray(12, 55)); + tlv.TLVFormat = TLVFormat.State; + + string terminalStatusData = (string)tlv.GetValue((byte)StateResponseCode.Status, typeof(string)); + _terminalStatus = (TerminalStatus)Convert.ToByte(terminalStatusData.Substring(0, 1)); + _salesMode = (SalesMode)Convert.ToByte(terminalStatusData.Substring(1, 1)); + _terminalCapabilities = terminalStatusData.Substring(2, 6); + _additionalTerminalCapabilities = terminalStatusData.Substring(8, 10); + _appVersionNumber = (string)tlv.GetValue((byte)StateResponseCode.AppVersionNumber, typeof(string)); + _handsetNumber = (string)tlv.GetValue((byte)StateResponseCode.HandsetNumber, typeof(string)); + _terminalId = (string)tlv.GetValue((byte)StateResponseCode.TerminalId, typeof(string)); + } + } +} From 9dfb58f70794c71063914ac0f798ca737b98f1c0 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 8 May 2020 19:19:40 +0800 Subject: [PATCH 078/120] Create unit test for Terminal Management Commands Create test method for STATE command request. Test StateResponse Parsing method. --- .../Ingenico/TerminalManagementTest.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs new file mode 100644 index 00000000..c2526506 --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs @@ -0,0 +1,57 @@ +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Ingenico; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Text; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class TerminalManagementTest { + IDeviceInterface _device; + + public TerminalManagementTest() { + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = Entities.DeviceType.Ingenico_EPOS_Desk5000, + ConnectionMode = ConnectionModes.TCP_IP_SERVER, + Port = "18101", + Timeout = 30 * 1000 + }); + Assert.IsNotNull(_device); + } + + [TestMethod] + public void StateCommandTest() { + try { + var resp = _device.GetTerminalStatus(); + + Assert.IsNotNull(resp); + } + catch (Exception e) { + Assert.Fail(e.Message); + } + } + + [TestMethod] + public void StateParsing() { + string resp = "010000006180S1810e0b8c8f000f0a001V109712019054H011T0822164769"; + byte[] rawResp = Encoding.UTF8.GetBytes(resp, 0, resp.Length); + + var stateResp = new StateResponse(rawResp); + + var s = stateResp.ReferenceNumber; + + Assert.IsNotNull(stateResp); + } + + [TestMethod] + public void CancelResponseParsing() { + string resp = "019000006180 826CANCELDONE"; + byte[] rawResp = Encoding.UTF8.GetBytes(resp, 0, resp.Length); + + var TerminalResponse = new CancelResponse(rawResp); + + Assert.IsNotNull(TerminalResponse); + } + } +} From bbdb2dc651e7fb596ed7672b1672d76f11da6f0f Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:23:46 +0800 Subject: [PATCH 079/120] Add Command for Terminal Management Request. Create constant variable for PID, LOGON, RESET, and CALLTMS. Create enum for ParseFormat for checking format of parsing when response data from terminal is received. --- .../Terminals/INGENICO/IngenicoEnums.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index dca030c8..a7aae9f8 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -21,6 +21,10 @@ internal class INGENICO_REQ_CMD { // Terminal Management Commands public const string STATE = "CMD=STATE"; + public const string PID = "CMD=PID"; + public const string LOGON = "CMD=LOGON"; + public const string RESET = "CMD=RESET"; + public const string CALLTMS = "CMD=CALLTMS"; // Request Report public const string REPORT = "0100000001100826EXT0100000A010B010CMD={0}"; @@ -162,4 +166,21 @@ public enum TLVFormat { /// State } + + public enum ParseFormat { + /// + /// For Transaction response parsing format + /// + Transaction = 0, + + /// + /// For State Command response parsing format + /// + State = 1, + + /// + /// For PID Command response parsing format + /// + PID = 2, + } } \ No newline at end of file From aacf4b6843e10d7133ef67b0fb647c37ecd13521 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:25:31 +0800 Subject: [PATCH 080/120] Handle Initialize request Handle override method of Initialize in IngenicoInterface. Build request message for PID Terminal Management Commad. --- .../Terminals/INGENICO/IngenicoInterface.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 9b097dae..fe98f354 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -14,7 +14,7 @@ public class IngenicoInterface : DeviceInterface, IDeviceInt internal IngenicoInterface(IngenicoController controller) : base(controller) { } - #region Admin Methods + #region Terminal Management/Admin Methods public override IDeviceResponse GetTerminalStatus() { StringBuilder sb = new StringBuilder(); sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); @@ -24,6 +24,17 @@ public override IDeviceResponse GetTerminalStatus() { return new StateResponse(response); } + + public override IInitializeResponse Initialize() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.PID); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new POSIdentifierResponse(response); + } + #endregion #region Payment Transaction Management From 2f07408845c771a09cf55ee0ebb20d2f21d4b081 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:29:34 +0800 Subject: [PATCH 081/120] Update Device Response Change second parameter of constructor from boolean to ParseFormat enum. --- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index 58586696..ac2ba9f5 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -28,11 +28,11 @@ public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse internal DataResponse _respField; private byte[] _buffer; - private bool _parsedResponseData; + private ParseFormat _parseFormat; - internal IngenicoTerminalResponse(byte[] buffer, bool parseRepField = true) { + internal IngenicoTerminalResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) { _buffer = buffer; - _parsedResponseData = parseRepField; + _parseFormat = format; ParseResponse(buffer); } @@ -117,7 +117,7 @@ public virtual void ParseResponse(byte[] response) { Status = _transactionStatus; // This is for parsing of Response field for Transaction request - if (_parsedResponseData) { + if (_parseFormat == ParseFormat.Transaction) { _respField = new DataResponse(response.SubArray(12, 55)); } } From 0841d41d84d99e719e4fcab448c7f8a28b253d77 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:30:26 +0800 Subject: [PATCH 082/120] Change base paramter from type --- .../Terminals/INGENICO/Responses/StateResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs index c8315fd3..3323df4c 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/StateResponse.cs @@ -18,7 +18,7 @@ public class StateResponse : IngenicoTerminalResponse, IDeviceResponse { private string _terminalId; public StateResponse(byte[] buffer) - : base(buffer, false) { + : base(buffer, ParseFormat.State) { } public TerminalStatus TerminalStatus { get { return _terminalStatus; } set { } } From 78bfaeccf33b7011fadf2cbeee206cebacd060f1 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:31:11 +0800 Subject: [PATCH 083/120] Create test method for Initialize method --- .../Terminals/Ingenico/TerminalManagementTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs index c2526506..27bef03a 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs @@ -32,6 +32,18 @@ public void StateCommandTest() { } } + [TestMethod] + public void PIDCommandTest() { + try { + var resp = _device.Initialize(); + + Assert.IsNotNull(resp); + } + catch (Exception e) { + Assert.Fail(e.Message); + } + } + [TestMethod] public void StateParsing() { string resp = "010000006180S1810e0b8c8f000f0a001V109712019054H011T0822164769"; From dea07f74bf100b6eaa44bd8452b36e47620e448f Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 13 May 2020 19:33:59 +0800 Subject: [PATCH 084/120] Create POSIdentifierResponse Class Inherit Ingenico Terminal response and Initialize class. Override method of parsing for SerialNumber value setting. Parse value for PID Response data. --- .../Responses/POSIdentifierResponse.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Responses/POSIdentifierResponse.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/POSIdentifierResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/POSIdentifierResponse.cs new file mode 100644 index 00000000..a2231fc2 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/POSIdentifierResponse.cs @@ -0,0 +1,18 @@ +using GlobalPayments.Api.Terminals.Abstractions; +using GlobalPayments.Api.Utils; +using System.Text; + +namespace GlobalPayments.Api.Terminals.Ingenico { + public class POSIdentifierResponse : IngenicoTerminalResponse, IInitializeResponse { + public string SerialNumber { get; set; } + + public POSIdentifierResponse(byte[] buffer) + : base(buffer, ParseFormat.PID) { + } + + public override void ParseResponse(byte[] response) { + base.ParseResponse(response); + SerialNumber = Encoding.UTF8.GetString(response.SubArray(12, 55)).Trim(); + } + } +} From e1df3e74584211b1080d5c79b326dea8a771db71 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Thu, 21 May 2020 12:27:59 +0800 Subject: [PATCH 085/120] Update in IDeviceInterface. Declare method name GetTerminalConfiguration. Declare method name TestConnection. --- .../Terminals/Abstractions/IDeviceInterface.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index 144d251c..f9e74b1e 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -38,8 +38,7 @@ public interface IDeviceInterface : IDisposable { /// /// The terminal immediately initiates a duplicate of the last completed transaction /// - /// Amount to be passed for cancel request. - /// TerminalManageBuilder + /// IDeviceResponse IDeviceResponse Duplicate(); /// @@ -47,6 +46,14 @@ public interface IDeviceInterface : IDisposable { /// /// IDeviceResponse GetTerminalStatus(); + + /// + /// Command used to request for CALL TMS in the terminal. + /// + /// IDeviceResponse + IDeviceResponse GetTerminalConfiguration(); + + IDeviceResponse TestConnection(); #endregion #region reporting From 8902708eb185ef15b588b7ac200b1e6b1f0d22a8 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 27 May 2020 12:41:37 +0800 Subject: [PATCH 086/120] Update DeviceInteface Declare GetTerminalConfiguration, and TestConnection methods. --- src/GlobalPayments.Api/Terminals/DeviceInterface.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index 7267b7fc..a7f85cc2 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -95,6 +95,14 @@ public virtual IDeviceResponse Duplicate() { public virtual IDeviceResponse GetTerminalStatus() { throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); } + + public virtual IDeviceResponse GetTerminalConfiguration() { + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + } + + public virtual IDeviceResponse TestConnection() { + throw new UnsupportedTransactionException("This function is not supported by the currently configured device."); + } #endregion #region Batching From 9680eca68f3a1b6b654a0acda62936c828ab8824 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 27 May 2020 12:43:22 +0800 Subject: [PATCH 087/120] Update Ingenico Interface file Ovveride and handled methods of GetTerminalConfiguration, TestConnection, and Reboot --- .../Terminals/INGENICO/IngenicoInterface.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index fe98f354..03f926df 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -35,6 +35,36 @@ public override IInitializeResponse Initialize() { return new POSIdentifierResponse(response); } + public override IDeviceResponse GetTerminalConfiguration() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.CALLTMS); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new IngenicoTerminalResponse(response); + } + + public override IDeviceResponse TestConnection() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.LOGON); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new IngenicoTerminalResponse(response); + } + + public override IDeviceResponse Reboot() { + StringBuilder sb = new StringBuilder(); + sb.Append(INGENICO_REQ_CMD.REQUEST_MESSAGE); + sb.Append(INGENICO_REQ_CMD.RESET); + + byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); + + return new IngenicoTerminalResponse(response); + } + #endregion #region Payment Transaction Management From 7d5960d522180cf0e68541c79cf5445b44c1b0ef Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 27 May 2020 12:44:28 +0800 Subject: [PATCH 088/120] Create test scenarios for CALLTMS, LOGON, RESET command. --- .../Ingenico/TerminalManagementTest.cs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs index 27bef03a..47529852 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Text; +using System.Threading; namespace GlobalPayments.Api.Tests.Terminals.Ingenico { [TestClass] @@ -45,25 +46,42 @@ public void PIDCommandTest() { } [TestMethod] - public void StateParsing() { - string resp = "010000006180S1810e0b8c8f000f0a001V109712019054H011T0822164769"; - byte[] rawResp = Encoding.UTF8.GetBytes(resp, 0, resp.Length); + public void CALLTMSCommandTest() { + try { + var resp = _device.GetTerminalConfiguration(); - var stateResp = new StateResponse(rawResp); + Assert.IsNotNull(resp); + Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData); + } + catch (Exception e) { + Assert.Fail(e.Message); + } + } - var s = stateResp.ReferenceNumber; + [TestMethod] + public void LOGONCommandTest() { + try { + var resp = _device.TestConnection(); - Assert.IsNotNull(stateResp); + Assert.IsNotNull(resp); + Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData); + } + catch (Exception e) { + Assert.Fail(e.Message); + } } [TestMethod] - public void CancelResponseParsing() { - string resp = "019000006180 826CANCELDONE"; - byte[] rawResp = Encoding.UTF8.GetBytes(resp, 0, resp.Length); - - var TerminalResponse = new CancelResponse(rawResp); + public void RESETCommandTest() { + try { + var resp = _device.Reboot(); - Assert.IsNotNull(TerminalResponse); + Assert.IsNotNull(resp); + Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData); + } + catch (Exception e) { + Assert.Fail(e.Message); + } } } } From 557004b03a5f6a54ded50379c0ab52b61a964250 Mon Sep 17 00:00:00 2001 From: estebantan Date: Sun, 31 May 2020 13:00:32 +0800 Subject: [PATCH 089/120] Enhancement of serial interface; port connection and the whole structure. --- .../GlobalPayments.Api.csproj | 1 + .../Terminals/ConnectionConfig.cs | 11 +- .../Terminals/INGENICO/IngenicoEnums.cs | 2 + .../Interfaces/IngenicoSerialInterface.cs | 240 +++++++++--------- .../Ingenico/SerialPaymentTransactions.cs | 7 +- 5 files changed, 125 insertions(+), 136 deletions(-) diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.csproj index d3fb9b96..aa9b0c9b 100644 --- a/src/GlobalPayments.Api/GlobalPayments.Api.csproj +++ b/src/GlobalPayments.Api/GlobalPayments.Api.csproj @@ -20,6 +20,7 @@ + diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs index 80c00722..22395c0e 100644 --- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs +++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs @@ -23,15 +23,6 @@ public enum BaudRate { r115200 = 115200 } - public enum Parity { - None = 0, - Odd, - Even, - } - public enum StopBits { - One = 1, - Two - } public enum DataBits { Seven = 7, Eight = 8 @@ -108,7 +99,7 @@ internal override void Validate() { if (ConnectionMode == ConnectionModes.TCP_IP || ConnectionMode == ConnectionModes.HTTP) { if (string.IsNullOrEmpty(IpAddress)) throw new ApiException("IpAddress is required for TCP or HTTP communication modes."); - if(string.IsNullOrEmpty(Port)) + if (string.IsNullOrEmpty(Port)) throw new ApiException("Port is required for TCP or HTTP communication modes."); } } diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index a7aae9f8..782fa4ef 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -39,6 +39,8 @@ internal class INGENICO_GLOBALS { public static bool KeepAlive = true; public const int IP_PORT = 18101; public const int RAW_RESPONSE_LENGTH = 80; + public const string MGMT_SCOPE = "root\\CIMV2"; + public const string MGMT_QUERY = "SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\""; } internal static class INGENICO_RESP { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 356d2168..809cc954 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -9,49 +9,67 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Management; namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoSerialInterface : IDeviceCommInterface { - public event MessageSentEventHandler OnMessageSent; - public event BroadcastMessageEventHandler OnBroadcastMessage; + private SerialPort serialPort; + private ITerminalConfiguration settings; + + private bool transComplete; + private bool isResult; + private bool isAcknowledge; + private bool isBroadcast; + private bool isXML; - ITerminalConfiguration _settings; + private List messageResponse; + private string bufferReceived = string.Empty; + private StringBuilder report = new StringBuilder(); - private SerialPort _serial; - private bool _complete = false; - private bool _isResult = false; - private bool _isAcknowledge = false; - private bool _broadcast = false; - private bool _isXML = false; - private string _buffer = string.Empty; - private string _appendReport = string.Empty; - private List _messageResponse; + public event MessageSentEventHandler OnMessageSent; + public event BroadcastMessageEventHandler OnBroadcastMessage; public IngenicoSerialInterface(ITerminalConfiguration settings) { - this._settings = settings; + this.settings = settings; + Connect(); } public void Connect() { - if (_serial == null) { - _serial = new SerialPort() { - PortName = "COM{0}".FormatWith(_settings.Port), - BaudRate = (int)_settings.BaudRate, - DataBits = (int)_settings.DataBits, - StopBits = (System.IO.Ports.StopBits)_settings.StopBits, - Parity = (System.IO.Ports.Parity)_settings.Parity, - Handshake = (Handshake)_settings.Handshake, - RtsEnable = true, - DtrEnable = true, - ReadTimeout = _settings.Timeout - }; - - if (!_serial.IsOpen) { - _serial.DataReceived += new SerialDataReceivedEventHandler(Serial_DataReceived); - _serial.Open(); + if (serialPort == null) { + ManagementObjectSearcher searcher = new ManagementObjectSearcher(INGENICO_GLOBALS.MGMT_SCOPE, INGENICO_GLOBALS.MGMT_QUERY); + + string manufacturer = string.Empty; + foreach (ManagementObject mgmtObject in searcher.Get()) { + manufacturer = mgmtObject["Manufacturer"].ToString().ToLower(); + if (manufacturer.Equals("ingenico")) { + string caption = mgmtObject["Caption"].ToString(); + string portName = "COM{0}".FormatWith(settings.Port); + if (caption.Equals(portName)) { + serialPort = new SerialPort() { + PortName = portName, + BaudRate = (int)settings.BaudRate, + DataBits = (int)settings.DataBits, + StopBits = settings.StopBits, + Parity = settings.Parity, + Handshake = settings.Handshake, + RtsEnable = true, + DtrEnable = true, + ReadTimeout = settings.Timeout + }; + } + } } - else { - throw new MessageException("Serial Port is already open."); + + if (serialPort == null) { + throw new ConfigurationException("Can't connect to the terminal. Port not found."); + } + + if (!serialPort.IsOpen) { + serialPort.DataReceived += new SerialDataReceivedEventHandler(Serial_DataReceived); + serialPort.Open(); } + } else { + throw new ConfigurationException("Serial port is already open."); } } @@ -59,37 +77,27 @@ private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort serial = (SerialPort)sender; do { Thread.Sleep(0100); - _buffer = serial.ReadExisting(); + bufferReceived = serial.ReadExisting(); - if (!string.IsNullOrEmpty(_buffer)) { - _serial.ReadTimeout = _settings.Timeout; + if (!string.IsNullOrEmpty(bufferReceived)) { + serialPort.ReadTimeout = settings.Timeout; - if (_buffer.Equals(INGENICO_RESP.ACKNOWLEDGE)) { - _isAcknowledge = true; + if (bufferReceived.Equals(INGENICO_RESP.ACKNOWLEDGE)) { + isAcknowledge = true; break; - } - else if (_buffer.Equals(INGENICO_RESP.ENQUIRY)) { - _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + } else if (bufferReceived.Equals(INGENICO_RESP.ENQUIRY)) { + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); break; - } - else if (_buffer.Contains(INGENICO_GLOBALS.BROADCAST)) { - _broadcast = true; + } else if (bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST)) { + isBroadcast = true; break; - } - else if (_buffer.Equals(INGENICO_RESP.NOTACKNOWLEDGE)) { - _isAcknowledge = false; - break; - } - else if (INGENICO_RESP.XML.Any(_buffer.Contains)) { - _isXML = true; + } else if (INGENICO_RESP.XML.Any(bufferReceived.Contains)) { + isXML = true; break; - } - else { - if (!_buffer.Contains(INGENICO_GLOBALS.BROADCAST) && !_buffer.Contains(INGENICO_RESP.INVALID) - && !INGENICO_RESP.XML.Any(_buffer.Contains) && !_buffer.Contains(INGENICO_RESP.ENDOFTXN) - && !_buffer.Contains(INGENICO_RESP.NOTACKNOWLEDGE)) { - _isResult = true; - } + } else if (!bufferReceived.Contains(INGENICO_RESP.INVALID) + && !bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST) + && !INGENICO_RESP.XML.Any(bufferReceived.Contains)) { + isResult = true; break; } } @@ -97,16 +105,9 @@ private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { } public void Disconnect() { - _serial.Close(); - _serial?.Dispose(); - _serial = null; - _buffer = string.Empty; - _appendReport = string.Empty; - _isResult = false; - _complete = false; - _isAcknowledge = false; - _broadcast = false; - _isXML = false; + serialPort.Close(); + serialPort?.Dispose(); + serialPort = null; } private bool ValidateResponseLRC(string calculate, string actual) { @@ -126,122 +127,117 @@ private async Task WriteMessage(IDeviceMessage message) { return await Task.Run(() => { try { int enquiryCount = 0; - _messageResponse = new List(); + messageResponse = new List(); - if (_serial == null) { + if (serialPort == null) { return false; } do { - _serial.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); - if (_isAcknowledge) { + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); + if (!isAcknowledge) { + Thread.Sleep(1000); + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + enquiryCount++; + + if (enquiryCount.Equals(3)) { + throw new MessageException("Terminal did not respond in Enquiry for three (3) times. Send aborted."); + } + } else { do { byte[] msg = message.GetSendBuffer(); foreach (byte b in msg) { byte[] _b = new byte[] { b }; - _serial.Write(_b, 0, 1); + serialPort.Write(_b, 0, 1); } - if (_isAcknowledge) { - _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + if (isAcknowledge) { + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + isAcknowledge = false; break; } } while (true); do { - if (_broadcast) { - byte[] bMsg = Encoding.ASCII.GetBytes(_buffer); + Thread.Sleep(100); + if (isBroadcast) { + byte[] bMsg = Encoding.ASCII.GetBytes(bufferReceived); BroadcastMessage broadcastMsg = new BroadcastMessage(bMsg); OnBroadcastMessage?.Invoke(broadcastMsg.Code, broadcastMsg.Message); - _broadcast = false; + isBroadcast = false; } - if (_isXML) { + if (isXML) { do { - _appendReport += _buffer; - if (_appendReport.Contains(INGENICO_RESP.ENDXML)) { - string xmlData = _appendReport.Substring(1, _appendReport.Length - 3); + report.Append(bufferReceived); + if (report.ToString().Contains(INGENICO_RESP.ENDXML)) { + string xmlData = report.ToString().Substring(1, report.ToString().Length - 3); if (MessageReceived(xmlData)) { - _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - _complete = true; + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + isXML = false; + transComplete = true; } } Thread.Sleep(0500); - } while (!_complete); + } while (!transComplete); } - if (_isResult) { + if (isResult) { string check = Encoding.UTF8.GetString(message.GetSendBuffer()); - if (_buffer.Contains(check.Substring(0, 2))) { + if (bufferReceived.Contains(check.Substring(0, 2))) { do { - string rData = _buffer.Substring(1, _buffer.Length - 3); - _buffer = _buffer.Substring(1, _buffer.Length - 3); - bool validateLRC = ValidateResponseLRC(rData, _buffer); + string rData = bufferReceived.Substring(1, bufferReceived.Length - 3); + bufferReceived = bufferReceived.Substring(1, bufferReceived.Length - 3); + bool validateLRC = ValidateResponseLRC(rData, bufferReceived); if (validateLRC) { if (MessageReceived(rData)) { - _serial.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - _complete = true; + serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + isResult = false; + transComplete = true; } } - } while (!_complete); + } while (!transComplete); } } - if (_complete) { - break; - } - } while (true); - break; - } - else { - Thread.Sleep(1000); - _serial.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); - enquiryCount++; + } while (!transComplete); - if (enquiryCount.Equals(3)) { - throw new MessageException("Terminal did not respond in Enquiry for three (3) times. Send aborted."); - } + return transComplete; } } while (true); - - return _complete; - } - catch (MessageException e) { + } catch (MessageException e) { throw new MessageException(e.Message); } }); } public byte[] Send(IDeviceMessage message) { - Connect(); try { - if (_serial != null) { + if (serialPort != null) { string bufferSend = Encoding.ASCII.GetString(message.GetSendBuffer()); OnMessageSent?.Invoke(bufferSend.Substring(1, bufferSend.Length - 3)); Task task = WriteMessage(message); - if (!task.Wait(_settings.Timeout)) { + if (!task.Wait(settings.Timeout)) { throw new MessageException("Terminal did not response within timeout."); } - string test = Encoding.ASCII.GetString(_messageResponse.ToArray()); - return _messageResponse.ToArray(); - } - else { + + return messageResponse.ToArray(); + } else { throw new MessageException("Terminal not connected."); } - } - finally { - if (_serial != null) { - Disconnect(); - } + } finally { + transComplete = false; } } private bool MessageReceived(string messageData) { - if (_messageResponse == null) { + if (messageResponse == null) { return false; } + foreach (char b in messageData) { - _messageResponse.Add((byte)b); + messageResponse.Add((byte)b); } + return true; } } diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs index 698d23a0..f36b9b33 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs @@ -20,11 +20,10 @@ public SerialPaymentTransactions() { Port = "5", BaudRate = BaudRate.r9600, DataBits = DataBits.Seven, - StopBits = StopBits.One, - Parity = Parity.Even, + StopBits = System.IO.Ports.StopBits.One, + Parity = System.IO.Ports.Parity.Even, Handshake = System.IO.Ports.Handshake.None, - Timeout = 65000, - RequestIdProvider = new RandomIdProvider() + Timeout = 65000 }); Assert.IsNotNull(_device); From ccc14aef85ffe69e43bccd0d6ff49ef3d15a8a6c Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 1 Jun 2020 23:31:24 +0800 Subject: [PATCH 090/120] Enhancement on IngenicoTcpInterface file. Declare variable for checking if response is needed. Handle throw exception for timeout. Validate if response data is needed otherwise invoke recursive flow for receiving data. Set timeout in client Accepting method. --- .../INGENICO/Interfaces/IngenicoTcpInterface.cs | 13 ++++++++++++- .../Terminals/Ingenico/TerminalManagementTest.cs | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index d88811a3..cfccef7b 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -24,6 +24,7 @@ internal class IngenicoTcpInterface : IDeviceCommInterface { private bool _isKeepAlive; private bool _isKeepAliveRunning; private Exception _receivingException; + private bool _isResponseNeeded; public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; @@ -80,6 +81,7 @@ public byte[] Send(IDeviceMessage message) { byte[] buffer = message.GetSendBuffer(); termResponse = null; + _isResponseNeeded = true; try { // Validate if server is starting @@ -106,7 +108,7 @@ public byte[] Send(IDeviceMessage message) { } if (termResponse != null) { - + _isResponseNeeded = false; // Remove timeout for stream read if (!_isKeepAlive) { _stream.ReadTimeout = -1; @@ -151,6 +153,7 @@ private void InitializeServer() { private void AcceptingClient() { _client = _listener.AcceptTcpClient(); _stream = _client.GetStream(); + _stream.ReadTimeout = _settings.Timeout; _ipAddresses.Add(((IPEndPoint)_client.Client.RemoteEndPoint).Address); @@ -248,6 +251,14 @@ private async void AnalyzeReceivedData() { } } } + catch (System.IO.IOException ex) { + if (_isResponseNeeded || _isKeepAlive) { + _receivingException = ex; + } + else { + AnalyzeReceivedData(); + } + } catch (Exception ex) { _receivingException = ex; } diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs index 47529852..cc5c1270 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs @@ -16,7 +16,7 @@ public TerminalManagementTest() { DeviceType = Entities.DeviceType.Ingenico_EPOS_Desk5000, ConnectionMode = ConnectionModes.TCP_IP_SERVER, Port = "18101", - Timeout = 30 * 1000 + Timeout = 5 * 1000 }); Assert.IsNotNull(_device); } From aa53da7215bab6b531b826f0f8beb207e6c2c9c3 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 3 Jun 2020 03:16:36 +0800 Subject: [PATCH 091/120] Update device reponse Include DCC amount --- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index ac2ba9f5..4a580887 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -44,10 +44,10 @@ public override string ToString() { #region Added Properties Specific for Ingenico public string DccCurrency { get { return _respField.DccCode; } set { } } public DynamicCurrencyStatus? DccStatus { get { return _respField.DccStatus; } set { } } + public decimal? DccAmount { get { return _respField.DccAmount; } set { } } public TransactionSubTypes? TransactionSubType { get { return _respField.TransactionSubType; } set { } } - public decimal SplitSaleAmount { get { return 0; } set { } } + public decimal? SplitSaleAmount { get { return 0; } set { } } public PaymentMode PaymentMode { get { return _paymentMode; } set { } } - public string DynamicCurrencyCode { get { return _respField.DccCode; } } public string CurrencyCode { get { return _currencyCode; } set { } } public string PrivateData { get { return _privateData; } } public decimal? FinalTransactionAmount { get { return _respField.FinalAmount; } } From a3c3ba0387d2035168fd3d422d64f64f9bda18b1 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 24 Jun 2020 23:23:06 +0800 Subject: [PATCH 092/120] Update Ingenico Controller. Remove other connection mode option in switch case. Just include only two connection mode for Ingenico. --- .../Terminals/INGENICO/IngenicoController.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index e79d04bf..950e50a1 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -25,9 +25,6 @@ internal override IDeviceCommInterface ConfigureConnector() { switch (_settings.ConnectionMode) { case ConnectionModes.SERIAL: return new IngenicoSerialInterface(_settings); - case ConnectionModes.TCP_IP: - case ConnectionModes.SSL_TCP: - case ConnectionModes.HTTP: case ConnectionModes.TCP_IP_SERVER: return new IngenicoTcpInterface(_settings); default: From 26dacd9b5ff7cb220aef00c7e3c31531cbf5aa69 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 26 Jun 2020 12:27:34 +0800 Subject: [PATCH 093/120] Update SDK for partial implementation of review Remove IsNull method in Extensions. Remove extra case in BuildMessage method for Ingenico. Update validation of IsNull implementation --- .../Terminals/INGENICO/IngenicoController.cs | 4 ++-- src/GlobalPayments.Api/Terminals/TerminalUtilities.cs | 5 +---- src/GlobalPayments.Api/Utils/Extensions.cs | 4 ---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index 950e50a1..6ad96ace 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -92,7 +92,7 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { extendedData = INGENICO_REQ_CMD.AUTHCODE.FormatWith(builder.AuthCode); } // Validation for Reversal with Transaction Id value in Extended data - else if (!builder.TransactionId.IsNull() && builder.TransactionType == TransactionType.Reversal) { + else if (builder.TransactionId != null && builder.TransactionType == TransactionType.Reversal) { extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(builder.TransactionId); } else if (builder.TransactionType == TransactionType.Reversal) { @@ -206,7 +206,7 @@ internal ReportResponse ReportRequest(IDeviceMessage request) { #region Validations private bool IsObjectNullOrEmpty(object value) { bool response = false; - if (value.IsNull() || string.IsNullOrWhiteSpace(value.ToString())) { + if (value == null || string.IsNullOrWhiteSpace(value.ToString())) { response = true; } else { diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs index d8b5a581..8af0f1b0 100644 --- a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs +++ b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs @@ -81,8 +81,6 @@ public static DeviceMessage BuildRequest(string message, ConnectionModes setting foreach (char c in _msg) buffer.Add((byte)c); - break; - case ConnectionModes.TCP_IP: break; default: throw new BuilderException("Failed to build request message. Unknown Connection mode."); @@ -148,8 +146,7 @@ public static byte[] BuildSignatureImage(string pathData, int width = 150) { Bitmap bmp = new Bitmap(width, 100); var gfx = Graphics.FromImage(bmp); - // TODO: Remove color from Utilities - //gfx.Clear(Color.White); + gfx.Clear(Color.White); var index = 0; var coordinate = coordinates[index++]; diff --git a/src/GlobalPayments.Api/Utils/Extensions.cs b/src/GlobalPayments.Api/Utils/Extensions.cs index d8d8d68d..eca72018 100644 --- a/src/GlobalPayments.Api/Utils/Extensions.cs +++ b/src/GlobalPayments.Api/Utils/Extensions.cs @@ -211,9 +211,5 @@ public static T[] SubArray(this T[] data, int index, int length) { Array.Copy(data, index, result, 0, length); return result; } - - public static bool IsNull(this T data) { - return data == null; - } } } From abc3c04776b804c223d63b088017e32fa406b009 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Mon, 29 Jun 2020 16:53:50 +0800 Subject: [PATCH 094/120] Enhacement for timeout handling and proper disposing of stream object. --- .../Interfaces/IngenicoTcpInterface.cs | 85 ++++++++++++------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index cfccef7b..928e3bc7 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -12,26 +12,28 @@ namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoTcpInterface : IDeviceCommInterface { - private TcpClient _client; + private volatile TcpClient _client; private NetworkStream _stream; private ITerminalConfiguration _settings; private TcpListenerEx _listener; private Socket _server; private List _ipAddresses = new List(); private BroadcastMessage _broadcastMessage; - private byte[] termResponse; - private Thread dataReceiving; + private byte[] _termResponse; + private Thread _dataReceiving; private bool _isKeepAlive; private bool _isKeepAliveRunning; private Exception _receivingException; private bool _isResponseNeeded; + private volatile bool _readData; + private volatile bool _disposable; public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; public IngenicoTcpInterface(ITerminalConfiguration settings) { _settings = settings; - _client = new TcpClient(); + _client = new TcpClient(); ; _ipAddresses = new List(); InitializeServer(); @@ -61,10 +63,17 @@ public void Disconnect() { try { if (_listener.Active) { - // Closing and disposing current clients - _client.Close(); - _client.Dispose(); + _readData = false; + _stream.ReadTimeout = 1; + // Closing and disposing current clients + while (true) { + if (_disposable) { + _client.Close(); + _client.Dispose(); + break; + } + } // Stopping server listening _listener.Stop(); @@ -80,7 +89,7 @@ public void Disconnect() { public byte[] Send(IDeviceMessage message) { byte[] buffer = message.GetSendBuffer(); - termResponse = null; + _termResponse = null; _isResponseNeeded = true; try { @@ -90,16 +99,14 @@ public byte[] Send(IDeviceMessage message) { } // Validate keep alive for setting of timeout during Transaction - if (!_isKeepAlive) { - _stream.ReadTimeout = _settings.Timeout; - } + _stream.ReadTimeout = _settings.Timeout; if (_ipAddresses.Count > 0) { _stream.WriteAsync(buffer, 0, buffer.Length).Wait(); // Should be move to Finally block before deployment OnMessageSent?.Invoke(Encoding.UTF8.GetString(RemoveHeader(buffer))); - while (termResponse == null) { + while (_termResponse == null) { Thread.Sleep(100); if (_receivingException != null) { Exception ex = _receivingException; @@ -107,20 +114,23 @@ public byte[] Send(IDeviceMessage message) { throw ex; } - if (termResponse != null) { - _isResponseNeeded = false; + if (_termResponse != null) { // Remove timeout for stream read if (!_isKeepAlive) { _stream.ReadTimeout = -1; } - return termResponse; + _isResponseNeeded = false; + _receivingException = null; + + return _termResponse; } } return null; } - else + else { throw new ConfigurationException("No terminal connected to server."); + } } catch (Exception ex) { throw new Exception(ex.Message); @@ -132,8 +142,9 @@ private void InitializeServer() { if (_listener == null) { int _port = INGENICO_GLOBALS.IP_PORT; // Default port. if (!string.IsNullOrWhiteSpace(_settings.Port)) { - if (!int.TryParse(_settings.Port, out _port)) + if (!int.TryParse(_settings.Port, out _port)) { throw new ConfigurationException("Invalid port number."); + } } _listener = new TcpListenerEx(IPAddress.Any, _port); @@ -144,6 +155,9 @@ private void InitializeServer() { // Initialize keep Alive value to false. _isKeepAlive = false; _isKeepAliveRunning = false; + + _readData = true; + _disposable = false; } else { throw new ConfigurationException("Server already initialize."); @@ -151,17 +165,17 @@ private void InitializeServer() { } private void AcceptingClient() { + _client = _listener.AcceptTcpClient(); _stream = _client.GetStream(); _stream.ReadTimeout = _settings.Timeout; _ipAddresses.Add(((IPEndPoint)_client.Client.RemoteEndPoint).Address); - // Start thread for handling keep alive request. - if (dataReceiving == null || dataReceiving.ThreadState != ThreadState.Running) { - dataReceiving = new Thread(new ThreadStart(AnalyzeReceivedData)); - dataReceiving.Start(); + if (_dataReceiving == null || _dataReceiving.ThreadState != ThreadState.Running) { + _dataReceiving = new Thread(new ThreadStart(AnalyzeReceivedData)); + _dataReceiving.Start(); } } @@ -169,10 +183,6 @@ private bool isBroadcast(byte[] terminalResponse) { return Encoding.UTF8.GetString(terminalResponse).Contains(INGENICO_GLOBALS.BROADCAST); } - private bool isCancel(byte[] buffer) { - return Encoding.UTF8.GetString(buffer).Contains(INGENICO_GLOBALS.CANCEL); - } - private bool isKeepAlive(byte[] buffer) { return Encoding.UTF8.GetString(buffer).Contains(INGENICO_GLOBALS.TID_CODE); } @@ -198,10 +208,12 @@ private byte[] KeepAliveResponse(byte[] buffer) { private async void AnalyzeReceivedData() { try { var headerBuffer = new byte[2]; - while (true) { + while (_readData) { _stream.Read(headerBuffer, 0, headerBuffer.Length); - + if (!_readData) { + throw new Exception(); + } int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); if (dataLength > 0) { byte[] dataBuffer = new byte[dataLength]; @@ -214,6 +226,9 @@ private async void AnalyzeReceivedData() { // Read data int bytesReceived = _stream.Read(dataBuffer, offset, tempLength); + if (!_readData) { + throw new Exception(); + } if (bytesReceived != tempLength) { offset += bytesReceived; tempLength -= bytesReceived; @@ -243,24 +258,28 @@ private async void AnalyzeReceivedData() { _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); } else { // Receiving request response data. - termResponse = readBuffer; + _termResponse = readBuffer; } } else { _receivingException = new ApiException("No data received."); } + } } - catch (System.IO.IOException ex) { + catch (Exception ex) { if (_isResponseNeeded || _isKeepAlive) { _receivingException = ex; + _stream.ReadTimeout = -1; + _isKeepAlive = false; } - else { + + if (_readData) { AnalyzeReceivedData(); } - } - catch (Exception ex) { - _receivingException = ex; + else { + _disposable = true; + } } } #endregion From 242a9ccb45703d5f5bb238c23fab92bc3f30a9bd Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 3 Jul 2020 18:46:02 +0800 Subject: [PATCH 095/120] Update package for GlobalPayment, replace CoreCompa.System.Drawing into System.Drawing.Common. --- src/GlobalPayments.Api/GlobalPayments.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.csproj index d3fb9b96..257247a5 100644 --- a/src/GlobalPayments.Api/GlobalPayments.Api.csproj +++ b/src/GlobalPayments.Api/GlobalPayments.Api.csproj @@ -18,7 +18,7 @@ - + From fab4724490ea10447eeec46b6f23baff80467d6c Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 7 Jul 2020 20:29:32 +0800 Subject: [PATCH 096/120] Update IngenicoController change response type of DoReverseRequest from ReverseResponse to IngenicoTerminalResponse --- .../Terminals/INGENICO/IngenicoController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index 6ad96ace..557f577d 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -281,9 +281,9 @@ private CancelResponse DoCancelRequest(IDeviceMessage request) { return new CancelResponse(response); } - private ReverseResponse DoReverseRequest(IDeviceMessage request) { + private IngenicoTerminalResponse DoReverseRequest(IDeviceMessage request) { byte[] response = Send(request); - return new ReverseResponse(response); + return new IngenicoTerminalResponse(response); } #endregion } From 19906c2324db0e49d252053384a0a10ef8e6ea74 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 7 Jul 2020 20:30:58 +0800 Subject: [PATCH 097/120] Update IngenicoInterface change response type of Cancel request from CancelResponse to IngenicoTerminalResponse --- src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 03f926df..1e86ac74 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -119,7 +119,7 @@ public override IDeviceResponse Cancel() { byte[] response = _controller.Send(TerminalUtilities.BuildRequest(sb.ToString(), settings: _controller.ConnectionMode.Value)); - return new CancelResponse(response); + return new IngenicoTerminalResponse(response); } public override TerminalManageBuilder Reverse(decimal? amount = null) { From 2c90d26b1ad7185c65fd28baecf897f170626e08 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 7 Jul 2020 20:41:31 +0800 Subject: [PATCH 098/120] Update DataResponse Change data type of all amount field in response data from terminal to string. Use .ToAmount() extension method of string for converting of string data type into decimal. --- .../INGENICO/Responses/DataResponse.cs | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs index be4df46c..7b9d37d7 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs @@ -9,15 +9,15 @@ namespace GlobalPayments.Api.Terminals.Ingenico { public class DataResponse { private string _authCode; - private decimal? _finalAmount; + private string _finalAmount; private PaymentMethod? _paymentMethod; - private decimal? _cashbackAmount; - private decimal? _gratuityAmount; - private decimal? _availableAmount; + private string _cashbackAmount; + private string _gratuityAmount; + private string _availableAmount; private string _dccCode; - private decimal? _dccAmount; + private string _dccAmount; private TransactionSubTypes? _txnSubType; - private decimal? _splitSaleAmount; + private string _splitSaleAmount; private DynamicCurrencyStatus? _dccStatus; private byte[] _buffer; @@ -35,8 +35,8 @@ private set { } } public decimal? FinalAmount { - get { return _finalAmount.ToString().ToAmount(); } - set { _finalAmount = value; } + get { return _finalAmount.ToAmount(); } + set { _finalAmount = value.ToString(); } } public PaymentMethod? PaymentMethod { @@ -45,18 +45,18 @@ public PaymentMethod? PaymentMethod { } public decimal? CashbackAmount { - get { return _cashbackAmount.ToString().ToAmount(); } - set { _cashbackAmount = value; } + get { return _cashbackAmount.ToAmount(); } + set { _cashbackAmount = value.ToString(); } } public decimal? GratuityAmount { - get { return _gratuityAmount.ToString().ToAmount(); } - set { _gratuityAmount = value; } + get { return _gratuityAmount.ToAmount(); } + set { _gratuityAmount = value.ToString(); } } public decimal? AvailableAmount { - get { return _availableAmount.ToString().ToAmount(); } - set { _availableAmount = value; } + get { return _availableAmount.ToAmount(); } + set { _availableAmount = value.ToString(); } } public string DccCode { get { return _dccCode; } @@ -64,8 +64,8 @@ public string DccCode { } public decimal? DccAmount { - get { return _dccAmount.ToString().ToAmount(); } - set { _dccAmount = value; } + get { return _dccAmount.ToAmount(); } + set { _dccAmount = value.ToString(); } } public TransactionSubTypes? TransactionSubType { @@ -74,8 +74,8 @@ public TransactionSubTypes? TransactionSubType { } public decimal? SplitSaleAmount { - get { return _splitSaleAmount.ToString().ToAmount(); } - set { _splitSaleAmount = value; } + get { return _splitSaleAmount.ToAmount(); } + set { _splitSaleAmount = value.ToString(); } } public DynamicCurrencyStatus? DccStatus { @@ -90,18 +90,17 @@ private void ParseData() { var tlv = new TypeLengthValue(_buffer); Type stringType = typeof(string); - Type decimalType = typeof(decimal?); _authCode = (string)tlv.GetValue((byte)RepFieldCode.AuthCode, stringType); - _cashbackAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.CashbackAmount, decimalType); - _gratuityAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.GratuityAmount, decimalType); - _finalAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.FinalTransactionAmount, decimalType); - _availableAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.AvailableAmount, decimalType); + _cashbackAmount = (string)tlv.GetValue((byte)RepFieldCode.CashbackAmount, stringType); + _gratuityAmount = (string)tlv.GetValue((byte)RepFieldCode.GratuityAmount, stringType); + _finalAmount = (string)tlv.GetValue((byte)RepFieldCode.FinalTransactionAmount, stringType); + _availableAmount = (string)tlv.GetValue((byte)RepFieldCode.AvailableAmount, stringType); _dccCode = (string)tlv.GetValue((byte)RepFieldCode.DccCurrency, stringType); - _dccAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.DccConvertedAmount, decimalType); + _dccAmount = (string)tlv.GetValue((byte)RepFieldCode.DccConvertedAmount, stringType); _txnSubType = (TransactionSubTypes?)tlv.GetValue((byte)RepFieldCode.TransactionSubType, typeof(TransactionSubTypes?)); _dccStatus = (DynamicCurrencyStatus?)tlv.GetValue((byte)RepFieldCode.DccOperationStatus, typeof(DynamicCurrencyStatus?)); - _splitSaleAmount = (decimal?)tlv.GetValue((byte)RepFieldCode.SplitSalePaidAmount, decimalType); + _splitSaleAmount = (string)tlv.GetValue((byte)RepFieldCode.SplitSalePaidAmount, stringType); _paymentMethod = (PaymentMethod?)tlv.GetValue((byte)RepFieldCode.PaymentMethod, typeof(PaymentMethod?)); } } From f53842e804268ac3aeae92c3503d291771afac0a Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 7 Jul 2020 21:42:55 +0800 Subject: [PATCH 099/120] Update Ingenico DeviceResponse Move parsing of data in IngenicoBaseResponse. Move properties from IngenicoTerminalResponse to IngenicoBaseResponse --- .../INGENICO/Responses/DeviceResponse.cs | 115 +++++++----------- 1 file changed, 47 insertions(+), 68 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index 4a580887..c3743a53 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -8,54 +8,63 @@ using System; namespace GlobalPayments.Api.Terminals.Ingenico { - public abstract class IngenicoBaseResponse : IDeviceResponse { - public string Status { get; set; } - public string Command { get; set; } - public string Version { get; set; } - public string DeviceResponseCode { get; set; } - public string DeviceResponseText { get; set; } - public string ReferenceNumber { get; set; } - } + public abstract class IngenicoBaseResponse : DeviceResponse { + protected byte[] _buffer; + protected ParseFormat _parseFormat; + internal DataResponse _respField; - public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse { + #region Added Properties Specific for Ingenico + public string DccCurrency { get; set; } + public DynamicCurrencyStatus? DccStatus { get; set; } + public decimal? DccAmount { get; set; } + public TransactionSubTypes? TransactionSubType { get; set; } + public decimal? SplitSaleAmount { get; set; } + public PaymentMode PaymentMode { get; set; } + public string CurrencyCode { get; set; } + public string PrivateData { get; set; } + public decimal? FinalTransactionAmount { get; set; } + + internal string Amount { get; set; } - internal string _transactionStatus; - internal decimal _amount; - internal PaymentMode _paymentMode; - internal string _privateData; - internal string terminalRawData; - internal string _currencyCode; - internal DataResponse _respField; - - private byte[] _buffer; - private ParseFormat _parseFormat; + #endregion - internal IngenicoTerminalResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) { + internal IngenicoBaseResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) { _buffer = buffer; _parseFormat = format; - ParseResponse(buffer); + ParseResponse(_buffer); + } + + + public virtual void ParseResponse(byte[] response) { + if (response != null) { + + ReferenceNumber = Encoding.UTF8.GetString(response.SubArray(0, 2)); + Status = ((TransactionStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); + Amount = Encoding.UTF8.GetString(response.SubArray(3, 8)); + PaymentMode = (PaymentMode)Encoding.UTF8.GetString(response.SubArray(11, 1)).ToInt32(); + DccCurrency = Encoding.UTF8.GetString(response.SubArray(67, 3)); + PrivateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); + + // This is for parsing of Response field for Transaction request + if (_parseFormat == ParseFormat.Transaction) { + _respField = new DataResponse(response.SubArray(12, 55)); + } + } } public override string ToString() { - base.DeviceResponseText = Encoding.UTF8.GetString(_buffer, 0, _buffer.Length); - return base.DeviceResponseText; + DeviceResponseText = Encoding.UTF8.GetString(_buffer, 0, _buffer.Length); + return DeviceResponseText; } - #region Added Properties Specific for Ingenico - public string DccCurrency { get { return _respField.DccCode; } set { } } - public DynamicCurrencyStatus? DccStatus { get { return _respField.DccStatus; } set { } } - public decimal? DccAmount { get { return _respField.DccAmount; } set { } } - public TransactionSubTypes? TransactionSubType { get { return _respField.TransactionSubType; } set { } } - public decimal? SplitSaleAmount { get { return 0; } set { } } - public PaymentMode PaymentMode { get { return _paymentMode; } set { } } - public string CurrencyCode { get { return _currencyCode; } set { } } - public string PrivateData { get { return _privateData; } } - public decimal? FinalTransactionAmount { get { return _respField.FinalAmount; } } - #endregion + } + + public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse { + + internal IngenicoTerminalResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) : base(buffer, format) { } #region Properties - public string ResponseText { get { return terminalRawData; } set { } } - public decimal? TransactionAmount { get { return _amount.ToString().ToAmount(); } set { } } + public decimal? TransactionAmount { get { return Amount.ToAmount(); } set { } } public decimal? BalanceAmount { get { return _respField.AvailableAmount; } set { } } public string AuthorizationCode { get { return _respField.AuthorizationCode ?? ""; } set { } } public decimal? TipAmount { get { return _respField.GratuityAmount; } set { } } @@ -92,42 +101,14 @@ public override string ToString() { public string ApplicationLabel { get; set; } public string ApplicationId { get; set; } public decimal? MerchantFee { get; set; } + public string ResponseText { get; set; } #endregion - /** Index - 0 - 1 = Epos Number - 2 - Transaction Status - 3 - 10 = Amount - 11 = Payment Mode - 12 - 20 = AuthCode or starts with (C) - 21 - 34 = Final Transaction Amount or starts with (M) - 35 - 38 = Payment Method or starts with (P) - 67 - 69 = Currency - 70 - 79 = Private Data - */ - public virtual void ParseResponse(byte[] response) { - if (response != null) { - - ReferenceNumber = Encoding.UTF8.GetString(response.SubArray(0, 2)); - _transactionStatus = ((TransactionStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); - decimal.TryParse(Encoding.UTF8.GetString(response.SubArray(3, 8)), out _amount); - _paymentMode = (PaymentMode)Encoding.UTF8.GetString(response.SubArray(11, 1)).ToInt32(); - _currencyCode = Encoding.UTF8.GetString(response.SubArray(67, 3)); - _privateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); - Status = _transactionStatus; - - // This is for parsing of Response field for Transaction request - if (_parseFormat == ParseFormat.Transaction) { - _respField = new DataResponse(response.SubArray(12, 55)); - } - } - } } public class IngenicoTerminalReportResponse : IngenicoBaseResponse, ITerminalReport { - private byte[] _buffer; - internal IngenicoTerminalReportResponse(byte[] buffer) { + internal IngenicoTerminalReportResponse(byte[] buffer) : base(buffer) { _buffer = buffer; Status = _buffer.Length > 0 ? "SUCCESS" : "FAILED"; } @@ -135,7 +116,5 @@ internal IngenicoTerminalReportResponse(byte[] buffer) { public override string ToString() { return Encoding.ASCII.GetString(_buffer); } - - } } \ No newline at end of file From 3851ed4ddf1b2de22974933d9c6525ac21307c96 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 7 Jul 2020 21:58:32 +0800 Subject: [PATCH 100/120] Update ReportResponse remove unnecessary ovveride method --- .../Terminals/INGENICO/Responses/ReportResponse.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs index 1d4623b0..cd4f021d 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs @@ -7,10 +7,5 @@ public class ReportResponse : IngenicoTerminalResponse, ITerminalReport { internal ReportResponse(byte[] buffer) : base(buffer) { ParseResponse(buffer); } - - public override void ParseResponse(byte[] response) { - base.ParseResponse(response); - _privateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); - } } } From 7bcb9e94e4f11f401a36804fbdeb68c5d84e4377 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Wed, 15 Jul 2020 16:37:18 +0800 Subject: [PATCH 101/120] Update Ingenico Enum Add Description attribute in TransactionSubTypes enum. Update TransactionSubType parsing of TLV Data. Remove convertion for TransactionSubType --- .../Terminals/INGENICO/IngenicoEnums.cs | 12 +++++++++--- .../Terminals/INGENICO/Responses/DataResponse.cs | 2 +- src/GlobalPayments.Api/Utils/TypeLengthValue.cs | 3 --- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 782fa4ef..081fa116 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -1,4 +1,5 @@  +using GlobalPayments.Api.Utils; using System; namespace GlobalPayments.Api.Terminals.Ingenico { @@ -92,9 +93,14 @@ public enum DynamicCurrencyStatus { } public enum TransactionSubTypes { - SPLIT_SALE_TXN = 0x53, // 0x53 byte for 'S' - DCC_TXN = 0x44, // 0x44 byte for 'D' - REFERRAL_RESULT = 0x82 // 0x52 byte for 'R' + [Description("S")] + SPLIT_SALE_TXN, + + [Description("D")] + DCC_TXN, + + [Description("R")] + REFERRAL_RESULT } public enum TerminalStatus { diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs index 7b9d37d7..31685656 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs @@ -98,7 +98,7 @@ private void ParseData() { _availableAmount = (string)tlv.GetValue((byte)RepFieldCode.AvailableAmount, stringType); _dccCode = (string)tlv.GetValue((byte)RepFieldCode.DccCurrency, stringType); _dccAmount = (string)tlv.GetValue((byte)RepFieldCode.DccConvertedAmount, stringType); - _txnSubType = (TransactionSubTypes?)tlv.GetValue((byte)RepFieldCode.TransactionSubType, typeof(TransactionSubTypes?)); + _txnSubType = EnumConverter.FromDescription(tlv.GetValue((byte)RepFieldCode.TransactionSubType, stringType)); _dccStatus = (DynamicCurrencyStatus?)tlv.GetValue((byte)RepFieldCode.DccOperationStatus, typeof(DynamicCurrencyStatus?)); _splitSaleAmount = (string)tlv.GetValue((byte)RepFieldCode.SplitSalePaidAmount, stringType); _paymentMethod = (PaymentMethod?)tlv.GetValue((byte)RepFieldCode.PaymentMethod, typeof(PaymentMethod?)); diff --git a/src/GlobalPayments.Api/Utils/TypeLengthValue.cs b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs index 22cc768f..1a3b76f1 100644 --- a/src/GlobalPayments.Api/Utils/TypeLengthValue.cs +++ b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs @@ -62,9 +62,6 @@ public object GetValue(byte type, Type returnType, TLVFormat? format = null) { else if (returnType == typeof(string)) { return strValue; } - else if (returnType == typeof(TransactionSubTypes?)) { - return (TransactionSubTypes)int.Parse(Convert.ToInt64(value[0]).ToString("X2"), System.Globalization.NumberStyles.HexNumber); - } else if (returnType == typeof(DynamicCurrencyStatus?)) { return (DynamicCurrencyStatus)int.Parse(strValue); } From 85942d2222a65ab8b07b6b23badeae9b9ad76741 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:05:06 +0800 Subject: [PATCH 102/120] Add new delegate for registering event handler for Pay@Table request. --- .../Terminals/Messaging/PayAtTableRequestEventHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/Messaging/PayAtTableRequestEventHandler.cs diff --git a/src/GlobalPayments.Api/Terminals/Messaging/PayAtTableRequestEventHandler.cs b/src/GlobalPayments.Api/Terminals/Messaging/PayAtTableRequestEventHandler.cs new file mode 100644 index 00000000..5cb60564 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/Messaging/PayAtTableRequestEventHandler.cs @@ -0,0 +1,5 @@ +using GlobalPayments.Api.Terminals.Ingenico.Requests; + +namespace GlobalPayments.Api.Terminals.Messaging { + public delegate void PayAtTableRequestEventHandler(PATRequest request); +} From 1907cc2c17d798dc8f2d79e01f218755309efdc6 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:06:52 +0800 Subject: [PATCH 103/120] Create new class for handling Pay@Table request message and parsed it. --- .../Terminals/INGENICO/Requests/PATRequest.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs new file mode 100644 index 00000000..1cc5cbbe --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs @@ -0,0 +1,54 @@ +using GlobalPayments.Api.Utils; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GlobalPayments.Api.Terminals.Ingenico.Requests { + public class PATRequest { + private byte[] _buffer; + + public PATRequestType RequestType { get; set; } + public string WaiterId { get; set; } + public string TableId { get; set; } + public string TID { get; set; } + public string TerminalCurrency { get; set; } + + public TransactionStatus TransactionOutcomeStatus { get; set; } + + + public PATRequest(byte[] buffer) { + _buffer = buffer; + ParseData(); + } + + private void ParseData() { + string strBuffer = Encoding.UTF8.GetString(_buffer); + + // Validating if data is XML Type + if (strBuffer.Contains(INGENICO_GLOBALS.XML_TAG)) { + + } + else { + // Otherwise check if what type of message frame the request is. + // Length of Ingenico Message Frame 2 + if (_buffer.Length == INGENICO_GLOBALS.MSG_FRAME_TWO_LEN) { + TransactionOutcomeStatus = (TransactionStatus)Encoding.UTF8.GetString(_buffer.SubArray(2, 1)).ToInt32(); + } + else { + RequestType = (PATRequestType)Convert.ToInt32(strBuffer.Substring(11, 1)); + + var tlvData = new TypeLengthValue(_buffer); + + WaiterId = tlvData.GetValue((byte)'O', typeof(string))?.ToString(); + TableId = tlvData.GetValue((byte)'L', typeof(string))?.ToString(); + TID = tlvData.GetValue((byte)'T', typeof(string))?.ToString(); + TerminalCurrency = tlvData.GetValue((byte)'C', typeof(string))?.ToString(); + } + } + } + + public override string ToString() { + return Encoding.UTF8.GetString(_buffer); + } + } +} From e6c64235fb4955513a6590944a8e6e730fbc469e Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:10:11 +0800 Subject: [PATCH 104/120] Implement PayAtTableRequestHandler in all child classes the inherited from IDeviceCommInterface. --- .../Genius/Interfaces/GeniusHttpInterface.cs | 1 + .../HPA/Interfaces/HpaTcpInterface.cs | 1 + .../Interfaces/IngenicoSerialInterface.cs | 159 +++++++++--------- .../PAX/Interfaces/PaxHttpInterface.cs | 1 + .../PAX/Interfaces/PaxTcpInterface.cs | 1 + 5 files changed, 84 insertions(+), 79 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs index e9d2266e..eaa44b98 100644 --- a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs @@ -18,6 +18,7 @@ internal class GeniusHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public GeniusHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs index 42886756..af4bae6a 100644 --- a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs @@ -17,6 +17,7 @@ internal class HpaTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public HpaTcpInterface(ITerminalConfiguration settings) { this._settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs index 809cc954..c8eb8188 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs @@ -13,29 +13,30 @@ namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoSerialInterface : IDeviceCommInterface { - private SerialPort serialPort; - private ITerminalConfiguration settings; + private SerialPort _serialPort; + private ITerminalConfiguration _settings; - private bool transComplete; - private bool isResult; - private bool isAcknowledge; - private bool isBroadcast; - private bool isXML; + private bool _transComplete; + private bool _isResult; + private bool _isAcknowledge; + private bool _isBroadcast; + private bool _isXML; - private List messageResponse; - private string bufferReceived = string.Empty; - private StringBuilder report = new StringBuilder(); + private List _messageResponse; + private string _bufferReceived = string.Empty; + private StringBuilder _report = new StringBuilder(); public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public IngenicoSerialInterface(ITerminalConfiguration settings) { - this.settings = settings; + this._settings = settings; Connect(); } public void Connect() { - if (serialPort == null) { + if (_serialPort == null) { ManagementObjectSearcher searcher = new ManagementObjectSearcher(INGENICO_GLOBALS.MGMT_SCOPE, INGENICO_GLOBALS.MGMT_QUERY); string manufacturer = string.Empty; @@ -43,30 +44,30 @@ public void Connect() { manufacturer = mgmtObject["Manufacturer"].ToString().ToLower(); if (manufacturer.Equals("ingenico")) { string caption = mgmtObject["Caption"].ToString(); - string portName = "COM{0}".FormatWith(settings.Port); + string portName = "COM{0}".FormatWith(_settings.Port); if (caption.Equals(portName)) { - serialPort = new SerialPort() { + _serialPort = new SerialPort() { PortName = portName, - BaudRate = (int)settings.BaudRate, - DataBits = (int)settings.DataBits, - StopBits = settings.StopBits, - Parity = settings.Parity, - Handshake = settings.Handshake, + BaudRate = (int)_settings.BaudRate, + DataBits = (int)_settings.DataBits, + StopBits = _settings.StopBits, + Parity = _settings.Parity, + Handshake = _settings.Handshake, RtsEnable = true, DtrEnable = true, - ReadTimeout = settings.Timeout + ReadTimeout = _settings.Timeout }; } } } - if (serialPort == null) { + if (_serialPort == null) { throw new ConfigurationException("Can't connect to the terminal. Port not found."); } - if (!serialPort.IsOpen) { - serialPort.DataReceived += new SerialDataReceivedEventHandler(Serial_DataReceived); - serialPort.Open(); + if (!_serialPort.IsOpen) { + _serialPort.DataReceived += new SerialDataReceivedEventHandler(Serial_DataReceived); + _serialPort.Open(); } } else { throw new ConfigurationException("Serial port is already open."); @@ -77,27 +78,27 @@ private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort serial = (SerialPort)sender; do { Thread.Sleep(0100); - bufferReceived = serial.ReadExisting(); + _bufferReceived = serial.ReadExisting(); - if (!string.IsNullOrEmpty(bufferReceived)) { - serialPort.ReadTimeout = settings.Timeout; + if (!string.IsNullOrEmpty(_bufferReceived)) { + _serialPort.ReadTimeout = _settings.Timeout; - if (bufferReceived.Equals(INGENICO_RESP.ACKNOWLEDGE)) { - isAcknowledge = true; + if (_bufferReceived.Equals(INGENICO_RESP.ACKNOWLEDGE)) { + _isAcknowledge = true; break; - } else if (bufferReceived.Equals(INGENICO_RESP.ENQUIRY)) { - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + } else if (_bufferReceived.Equals(INGENICO_RESP.ENQUIRY)) { + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); break; - } else if (bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST)) { - isBroadcast = true; + } else if (_bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST)) { + _isBroadcast = true; break; - } else if (INGENICO_RESP.XML.Any(bufferReceived.Contains)) { - isXML = true; + } else if (INGENICO_RESP.XML.Any(_bufferReceived.Contains)) { + _isXML = true; break; - } else if (!bufferReceived.Contains(INGENICO_RESP.INVALID) - && !bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST) - && !INGENICO_RESP.XML.Any(bufferReceived.Contains)) { - isResult = true; + } else if (!_bufferReceived.Contains(INGENICO_RESP.INVALID) + && !_bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST) + && !INGENICO_RESP.XML.Any(_bufferReceived.Contains)) { + _isResult = true; break; } } @@ -105,9 +106,9 @@ private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) { } public void Disconnect() { - serialPort.Close(); - serialPort?.Dispose(); - serialPort = null; + _serialPort.Close(); + _serialPort?.Dispose(); + _serialPort = null; } private bool ValidateResponseLRC(string calculate, string actual) { @@ -127,17 +128,17 @@ private async Task WriteMessage(IDeviceMessage message) { return await Task.Run(() => { try { int enquiryCount = 0; - messageResponse = new List(); + _messageResponse = new List(); - if (serialPort == null) { + if (_serialPort == null) { return false; } do { - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); - if (!isAcknowledge) { + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ENQ), 0, 1); + if (!_isAcknowledge) { Thread.Sleep(1000); - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); enquiryCount++; if (enquiryCount.Equals(3)) { @@ -148,60 +149,60 @@ private async Task WriteMessage(IDeviceMessage message) { byte[] msg = message.GetSendBuffer(); foreach (byte b in msg) { byte[] _b = new byte[] { b }; - serialPort.Write(_b, 0, 1); + _serialPort.Write(_b, 0, 1); } - if (isAcknowledge) { - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); - isAcknowledge = false; + if (_isAcknowledge) { + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.EOT), 0, 1); + _isAcknowledge = false; break; } } while (true); do { Thread.Sleep(100); - if (isBroadcast) { - byte[] bMsg = Encoding.ASCII.GetBytes(bufferReceived); + if (_isBroadcast) { + byte[] bMsg = Encoding.ASCII.GetBytes(_bufferReceived); BroadcastMessage broadcastMsg = new BroadcastMessage(bMsg); OnBroadcastMessage?.Invoke(broadcastMsg.Code, broadcastMsg.Message); - isBroadcast = false; + _isBroadcast = false; } - if (isXML) { + if (_isXML) { do { - report.Append(bufferReceived); - if (report.ToString().Contains(INGENICO_RESP.ENDXML)) { - string xmlData = report.ToString().Substring(1, report.ToString().Length - 3); + _report.Append(_bufferReceived); + if (_report.ToString().Contains(INGENICO_RESP.ENDXML)) { + string xmlData = _report.ToString().Substring(1, _report.ToString().Length - 3); if (MessageReceived(xmlData)) { - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - isXML = false; - transComplete = true; + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + _isXML = false; + _transComplete = true; } } Thread.Sleep(0500); - } while (!transComplete); + } while (!_transComplete); } - if (isResult) { + if (_isResult) { string check = Encoding.UTF8.GetString(message.GetSendBuffer()); - if (bufferReceived.Contains(check.Substring(0, 2))) { + if (_bufferReceived.Contains(check.Substring(0, 2))) { do { - string rData = bufferReceived.Substring(1, bufferReceived.Length - 3); - bufferReceived = bufferReceived.Substring(1, bufferReceived.Length - 3); - bool validateLRC = ValidateResponseLRC(rData, bufferReceived); + string rData = _bufferReceived.Substring(1, _bufferReceived.Length - 3); + _bufferReceived = _bufferReceived.Substring(1, _bufferReceived.Length - 3); + bool validateLRC = ValidateResponseLRC(rData, _bufferReceived); if (validateLRC) { if (MessageReceived(rData)) { - serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); - isResult = false; - transComplete = true; + _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1); + _isResult = false; + _transComplete = true; } } - } while (!transComplete); + } while (!_transComplete); } } - } while (!transComplete); + } while (!_transComplete); - return transComplete; + return _transComplete; } } while (true); } catch (MessageException e) { @@ -212,30 +213,30 @@ private async Task WriteMessage(IDeviceMessage message) { public byte[] Send(IDeviceMessage message) { try { - if (serialPort != null) { + if (_serialPort != null) { string bufferSend = Encoding.ASCII.GetString(message.GetSendBuffer()); OnMessageSent?.Invoke(bufferSend.Substring(1, bufferSend.Length - 3)); Task task = WriteMessage(message); - if (!task.Wait(settings.Timeout)) { + if (!task.Wait(_settings.Timeout)) { throw new MessageException("Terminal did not response within timeout."); } - return messageResponse.ToArray(); + return _messageResponse.ToArray(); } else { throw new MessageException("Terminal not connected."); } } finally { - transComplete = false; + _transComplete = false; } } private bool MessageReceived(string messageData) { - if (messageResponse == null) { + if (_messageResponse == null) { return false; } foreach (char b in messageData) { - messageResponse.Add((byte)b); + _messageResponse.Add((byte)b); } return true; diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs index 6c318d20..20de9ad1 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs @@ -14,6 +14,7 @@ internal class PaxHttpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public PaxHttpInterface(ITerminalConfiguration settings) { _settings = settings; diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs index 4a4810be..13335ae8 100644 --- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs @@ -15,6 +15,7 @@ internal class PaxTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public PaxTcpInterface(ITerminalConfiguration settings) { _settings = settings; From 7fe0202ed2988a4f079943c7d230cee784c3f2aa Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:12:21 +0800 Subject: [PATCH 105/120] Create new enum called DeviceMode for tagging Pay@Table functionalities in the device. --- src/GlobalPayments.Api/Entities/Enums.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs index cd0aed0f..ae18da1b 100644 --- a/src/GlobalPayments.Api/Entities/Enums.cs +++ b/src/GlobalPayments.Api/Entities/Enums.cs @@ -73,6 +73,16 @@ public enum DeviceType { Ingenico_EPOS_Move3500 } + /// + /// Indicates a device mode. + /// + public enum DeviceMode { + /// + /// For devices that supports Pay@Table functionalities. + /// + PAY_AT_TABLE + } + /// /// Indicates an inquiry type. /// From e92bad7792f2a57ac12bfcbde77262b7fc039916 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:13:10 +0800 Subject: [PATCH 106/120] Declare PayAtTableRequestEventHandler in IDeviceCommInterface --- .../Terminals/Abstractions/IDeviceCommInterface.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs index cb82d647..2200cb9b 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs @@ -11,5 +11,7 @@ public interface IDeviceCommInterface { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; + + event PayAtTableRequestEventHandler OnPayAtTableRequest; } } From 832c20eb1c3e064b2ec5d4651fcf5e43d58eff6d Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:15:17 +0800 Subject: [PATCH 107/120] Remove extra using. Declare PayAtTableRequestEventHandler Declare PayAtTableResponse method for initiating that the message to be sent is for Pay@Table request. --- .../Terminals/Abstractions/IDeviceInterface.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs index f9e74b1e..8f566fb7 100644 --- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs @@ -4,13 +4,12 @@ using GlobalPayments.Api.Terminals.Builders; using GlobalPayments.Api.Terminals.Ingenico; using GlobalPayments.Api.Terminals.Messaging; -// TODO: remove this. -using ReportType = GlobalPayments.Api.Terminals.Ingenico.ReportType; namespace GlobalPayments.Api.Terminals { public interface IDeviceInterface : IDisposable { event MessageSentEventHandler OnMessageSent; event BroadcastMessageEventHandler OnBroadcastMessage; + event PayAtTableRequestEventHandler OnPayAtTableRequest; #region Admin Calls @@ -71,7 +70,7 @@ public interface IDeviceInterface : IDisposable { /// /// Report Type /// - TerminalReportBuilder GetReport(ReportType type); + TerminalReportBuilder GetReport(Ingenico.ReportType type); #endregion #region Batch Calls @@ -152,5 +151,16 @@ public interface IDeviceInterface : IDisposable { TerminalManageBuilder Reverse(decimal? amount = null); #endregion + + #region Pay@Table Feature + + /// + /// Response to terminal after Pay@Table request has been made. + /// + /// + /// + TerminalAuthBuilder PayAtTableResponse(); + + #endregion } } From 7dbdfd7ba799ae450adff428c782bbd84e89ee0c Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:19:09 +0800 Subject: [PATCH 108/120] Declared AdditionalMessage, PayAtTableResponse, FilePath. Overloaded WithPaymentMode for getting AdditionalMessage value. Created WithPayAtTableResponseType for getting response type. Created WithXMLPath method for getting FilePath value. --- .../Terminals/Builders/TerminalAuthBuilder.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs index c42020a0..70c6f8da 100644 --- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs +++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs @@ -42,6 +42,9 @@ internal string TransactionId { internal PaymentMode PaymentMode { get; set; } internal string TableNumber { get; set; } internal TaxFreeType? TaxFreeType { get; set; } + internal PATPaymentMode? AdditionalMessage { get; set; } + internal PATResponseType? PayAtTableResponse { get; private set; } + public string FilePath { get; private set; } public TerminalAuthBuilder WithAddress(Address address) { Address = address; @@ -166,6 +169,11 @@ public TerminalAuthBuilder WithPaymentMode(PaymentMode value) { return this; } + public TerminalAuthBuilder WithPaymentMode(PATPaymentMode additionalMessage) { + AdditionalMessage = additionalMessage; + return this; + } + /// /// Sets the table number for the transaction. /// @@ -188,6 +196,16 @@ public TerminalAuthBuilder WithTaxFree(TaxFreeType taxFreeType) { return this; } + public TerminalAuthBuilder WithPayAtTableResponseType(PATResponseType response) { + PayAtTableResponse = response; + return this; + } + + public TerminalAuthBuilder WithXMLPath(string filePath) { + FilePath = filePath; + return this; + } + internal TerminalAuthBuilder(TransactionType type, PaymentMethodType paymentType) : base(type, paymentType) { } From cac698d2dbad9b4ff97de37235859ddcb4bed9ee Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:24:22 +0800 Subject: [PATCH 109/120] Declared MSG_FRAME_TWO_LEN, XML_TAG Created static class for Pay@Table hard-coded response value. Added PayAtTableRequest in ParseFormat enum. Created PATRequestType enum for Pay@Table different types of request. Created PATResponseType enum. Create PATPaymentMode enum. --- .../Terminals/INGENICO/IngenicoEnums.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 081fa116..17eb4f10 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -42,6 +42,8 @@ internal class INGENICO_GLOBALS { public const int RAW_RESPONSE_LENGTH = 80; public const string MGMT_SCOPE = "root\\CIMV2"; public const string MGMT_QUERY = "SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\""; + public const int MSG_FRAME_TWO_LEN = 80; + public const string XML_TAG = ""; } + internal static class PAYATTABLE_RESP { + public readonly static string PAT_EPOS_NUMBER = "00"; + public readonly static string PAT_STATUS = "0"; + } + public enum ReceiptType { TICKET, SPLITR, @@ -190,5 +197,60 @@ public enum ParseFormat { /// For PID Command response parsing format /// PID = 2, + + /// + /// For Pay@Table functionalities in terminal + /// + PayAtTableRequest + } + + /// + /// Type of request message from terminal during Pay@Table mode. + /// + public enum PATRequestType { + /// + /// Indicates a Table Lock + /// + TableLock = 1, + + /// + /// Indicates a Table Unlock + /// + TableUnlock = 2, + + /// + /// Indicates a Receipt for table + /// + TableReceipt = 3, + + /// + /// Indicates a List of Table. + /// + TableList = 4, + + } + + /// + /// Confirmation options + /// + public enum PATResponseType { + /// + /// Positive confirmation + /// + CONF_OK, + + /// + /// Negative confirmation + /// + CONF_NOK + } + + /// + /// Indicates if the EPOS want to uses the additional Message + /// + public enum PATPaymentMode { + NO_ADDITIONAL = 0, + + USE_ADDITIONAL = 1 } } \ No newline at end of file From 1243032b4241d54669774bcb1fb40ba9b6255b12 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:28:43 +0800 Subject: [PATCH 110/120] Created new property named DeviceMode for tagging Pay@Table functionalities is configured on the device that will connect. --- src/GlobalPayments.Api/Terminals/ConnectionConfig.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs index 22395c0e..ee9f0d65 100644 --- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs +++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs @@ -49,6 +49,9 @@ public interface ITerminalConfiguration { // Associated Gateway GatewayConfig GatewayConfig { get; set; } + + // Pay@Table + DeviceMode? DeviceMode { get; set; } } public class ConnectionConfig : Configuration, ITerminalConfiguration { @@ -63,6 +66,7 @@ public class ConnectionConfig : Configuration, ITerminalConfiguration { public string Port { get; set; } public IRequestIdProvider RequestIdProvider { get; set; } public GatewayConfig GatewayConfig { get; set; } + public DeviceMode? DeviceMode { get; set; } public ConnectionConfig() { Timeout = -1; From b324ba3d8901e9a6c2db6324f677ec928a8b1414 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:29:24 +0800 Subject: [PATCH 111/120] Handle declared event of PayAtTableRequestEventHandler --- src/GlobalPayments.Api/Terminals/DeviceController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/DeviceController.cs b/src/GlobalPayments.Api/Terminals/DeviceController.cs index cb2c5686..8d6e6028 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceController.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceController.cs @@ -34,6 +34,7 @@ public IRequestIdProvider RequestIdProvider { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; internal DeviceController(ITerminalConfiguration settings) { _settings = settings; @@ -45,6 +46,10 @@ internal DeviceController(ITerminalConfiguration settings) { _connector.OnBroadcastMessage += (code, message) => { OnBroadcastMessage?.Invoke(code, message); }; + + _connector.OnPayAtTableRequest += (request) => { + OnPayAtTableRequest?.Invoke(request); + }; } public byte[] Send(IDeviceMessage message) { From 8d18ac23e94269bdde5a9cd8a72e6a6671185fc6 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:30:44 +0800 Subject: [PATCH 112/120] Validate builder value and build message response for Pay@Table request. --- .../Terminals/INGENICO/IngenicoController.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs index 557f577d..e76bad42 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs @@ -118,6 +118,31 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) { internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder) { string message = string.Empty; + + // For Pay@Table functionalities + if (_settings.DeviceMode != null && _settings.DeviceMode == DeviceMode.PAY_AT_TABLE) { + if (builder.PayAtTableResponse != null && builder.AdditionalMessage != null) { + + StringBuilder payAtTableResp = new StringBuilder(); + payAtTableResp.Append(PAYATTABLE_RESP.PAT_EPOS_NUMBER); + payAtTableResp.Append(PAYATTABLE_RESP.PAT_STATUS); + payAtTableResp.Append((int?)builder.AdditionalMessage ?? 0); + payAtTableResp.Append(ValidateAmount(builder.Amount)?.ToString("00000000")); + payAtTableResp.Append(ValidateCurrency(builder.CurrencyCode) ); + payAtTableResp.Append(builder.PayAtTableResponse.ToString()); + + message = payAtTableResp.ToString(); + } + else if (!string.IsNullOrEmpty(builder.FilePath)) { + message = TerminalUtilities.GetTextContent(builder.FilePath); + } + else { + throw new BuilderException("PayAtTable Response type and Additional message can not be null."); + } + return TerminalUtilities.BuildRequest(message, settings: _settings.ConnectionMode); + } + + int referenceNumber = builder.ReferenceNumber; decimal? amount = builder.Amount; int returnRep = 1; @@ -247,7 +272,7 @@ private string ValidateCurrency(string currencyCode) { currencyCode = "826"; } - return currencyCode; + return currencyCode.Substring(0, 3); } private decimal? ValidateAmount(decimal? amount) { From d9897b2f3d55eb86e97efa729a219a3cc17806ea Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:32:48 +0800 Subject: [PATCH 113/120] Invoke PayAtTableRequestEventHandler. Instantiate TerminalAuthBuilder in PayAtTableResponse method. --- .../Terminals/DeviceInterface.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs index a7f85cc2..ecc77952 100644 --- a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs +++ b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs @@ -12,6 +12,7 @@ public abstract class DeviceInterface : IDeviceInterface where T : DeviceCont public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; internal DeviceInterface(T controller) { _controller = controller; @@ -23,6 +24,10 @@ internal DeviceInterface(T controller) { OnBroadcastMessage?.Invoke(code, message); }; + _controller.OnPayAtTableRequest += (request) => { + OnPayAtTableRequest?.Invoke(request); + }; + _requestIdProvider = _controller.RequestIdProvider; } @@ -154,7 +159,7 @@ public virtual TerminalAuthBuilder Sale(decimal? amount = null) { return new TerminalAuthBuilder(TransactionType.Sale, PaymentMethodType.Credit) .WithAmount(amount); } - + public virtual TerminalAuthBuilder Verify() { return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit) .WithAmount(6.18m); @@ -181,6 +186,15 @@ public virtual TerminalManageBuilder Reverse(decimal? amount = null) { return new TerminalManageBuilder(TransactionType.Reversal, PaymentMethodType.Credit) .WithAmount(amount); } + + #endregion + + #region Pay@Table Methods + + public virtual TerminalAuthBuilder PayAtTableResponse() { + return new TerminalAuthBuilder(TransactionType.Void, PaymentMethodType.Other); + } + #endregion #endregion From cc8de25aed73fb2c10bc7c471ce3f2dca7b91135 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:33:37 +0800 Subject: [PATCH 114/120] Override PayAtTableResponse method from DeviceInterface. --- .../Terminals/INGENICO/IngenicoInterface.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs index 1e86ac74..00964f3c 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs @@ -143,5 +143,9 @@ public override IDeviceResponse Duplicate() { } #endregion + + public override TerminalAuthBuilder PayAtTableResponse() { + return base.PayAtTableResponse(); + } } } \ No newline at end of file From 248ea118a1c5ec05a34e8bb25d72f62750d19dc8 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:38:38 +0800 Subject: [PATCH 115/120] Change DccCurrency into CurrencyCode. Assigned parsed value form terminal into Dcc Amount, Currency, and Status. --- .../Terminals/INGENICO/Responses/DeviceResponse.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs index c3743a53..72c8b9a5 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs @@ -42,12 +42,16 @@ public virtual void ParseResponse(byte[] response) { Status = ((TransactionStatus)Encoding.UTF8.GetString(response.SubArray(2, 1)).ToInt32()).ToString(); Amount = Encoding.UTF8.GetString(response.SubArray(3, 8)); PaymentMode = (PaymentMode)Encoding.UTF8.GetString(response.SubArray(11, 1)).ToInt32(); - DccCurrency = Encoding.UTF8.GetString(response.SubArray(67, 3)); + CurrencyCode = Encoding.UTF8.GetString(response.SubArray(67, 3)); PrivateData = Encoding.UTF8.GetString(response.SubArray(70, response.Length - 70)); // This is for parsing of Response field for Transaction request if (_parseFormat == ParseFormat.Transaction) { _respField = new DataResponse(response.SubArray(12, 55)); + + DccAmount = _respField.DccAmount; + DccCurrency = _respField.DccCode; + DccStatus = _respField.DccStatus; } } } From d0f754711772808da2204c6c6299a426169b197b Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:39:51 +0800 Subject: [PATCH 116/120] Created test file for Pay@Table module. --- .../Ingenico/PayAtTableRequestTests.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs new file mode 100644 index 00000000..7ec7954e --- /dev/null +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; +using GlobalPayments.Api.Entities; +using GlobalPayments.Api.Services; +using GlobalPayments.Api.Terminals; +using GlobalPayments.Api.Terminals.Ingenico; +using GlobalPayments.Api.Terminals.Ingenico.Requests; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GlobalPayments.Api.Tests.Terminals.Ingenico { + [TestClass] + public class PayAtTableRequestTests { + + private IDeviceInterface _device; + + public PayAtTableRequestTests() { + + + TerminalUtilities.log("TERMINAL START CONNECTION..."); + + _device = DeviceService.Create(new ConnectionConfig() { + DeviceType = DeviceType.Ingenico_EPOS_Desk5000, + ConnectionMode = ConnectionModes.TCP_IP_SERVER, + Port = "18101", + Timeout = 60 * 1000, + DeviceMode = DeviceMode.PAY_AT_TABLE + }); + + + Assert.IsNotNull(_device); + + _device.OnPayAtTableRequest += _device_OnPayAtTableRequest; + + TerminalUtilities.log("TERMINAL CONNECTED"); + } + + private void _device_OnPayAtTableRequest(PATRequest request) { + // Success ConfirmaitonOK + + TerminalUtilities.log("OnPayAtTableRequest " + request.ToString() + " " + request.RequestType.ToString()); + + Thread.Sleep(5 * 1000); + + + + if (request.RequestType == PATRequestType.TableReceipt) { + TerminalUtilities.log("Receipt response"); + _device.PayAtTableResponse() + .WithXMLPath("C:\\tmp\\receipt.txt") + .Execute(); + } + else { + _device.PayAtTableResponse() + .WithPayAtTableResponseType(PATResponseType.CONF_OK) + .WithAmount(25M) + .WithPaymentMode(PATPaymentMode.USE_ADDITIONAL) + .Execute(); + } + } + + [TestMethod] + public void TableLock() { + Thread.Sleep(3000 * 1000); + } + } +} From 9b3e8994cec22e0f99f24c1303af04240c709622 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:48:04 +0800 Subject: [PATCH 117/120] Modified Send Method if DeviceMode is set to PayAtTable then return null, cause SDK would just sent a response according to its request. Handled request message from terminal in analyzeReadData. Invoke OnPayAtTableRequest event if the data from terminal is from PayAtTable configured terminal. --- .../Interfaces/IngenicoTcpInterface.cs | 140 ++++++++++++------ 1 file changed, 95 insertions(+), 45 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs index 928e3bc7..ec75835a 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs @@ -9,6 +9,7 @@ using GlobalPayments.Api.Entities; using System.Threading; using System.Threading.Tasks; +using GlobalPayments.Api.Terminals.Ingenico.Requests; namespace GlobalPayments.Api.Terminals.Ingenico { internal class IngenicoTcpInterface : IDeviceCommInterface { @@ -30,6 +31,7 @@ internal class IngenicoTcpInterface : IDeviceCommInterface { public event MessageSentEventHandler OnMessageSent; public event BroadcastMessageEventHandler OnBroadcastMessage; + public event PayAtTableRequestEventHandler OnPayAtTableRequest; public IngenicoTcpInterface(ITerminalConfiguration settings) { _settings = settings; @@ -106,6 +108,10 @@ public byte[] Send(IDeviceMessage message) { // Should be move to Finally block before deployment OnMessageSent?.Invoke(Encoding.UTF8.GetString(RemoveHeader(buffer))); + if (_settings.DeviceMode == DeviceMode.PAY_AT_TABLE) { + return null; + } + while (_termResponse == null) { Thread.Sleep(100); if (_receivingException != null) { @@ -210,61 +216,101 @@ private async void AnalyzeReceivedData() { var headerBuffer = new byte[2]; while (_readData) { - _stream.Read(headerBuffer, 0, headerBuffer.Length); - if (!_readData) { - throw new Exception(); - } - int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); - if (dataLength > 0) { - byte[] dataBuffer = new byte[dataLength]; - - var incomplete = true; - int offset = 0; - int tempLength = dataLength; + // For Pay@Table functionalities handling. + if (_settings.DeviceMode == DeviceMode.PAY_AT_TABLE) { - do { + _stream.Read(headerBuffer, 0, headerBuffer.Length); + if (!_readData) { + throw new Exception(); + } + int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); + if (dataLength > 0) { + byte[] dataBuffer = new byte[dataLength]; + + var incomplete = true; + int offset = 0; + int tempLength = dataLength; + + do { + + // Read data + int bytesReceived = _stream.Read(dataBuffer, offset, tempLength); + if (!_readData) { + throw new Exception(); + } + if (bytesReceived != tempLength) { + offset += bytesReceived; + tempLength -= bytesReceived; + } + else { + incomplete = false; + } + } while (incomplete); + + var readBuffer = new byte[dataLength]; + Array.Copy(dataBuffer, readBuffer, dataLength); + PayAtTableHandler(readBuffer); + } - // Read data - int bytesReceived = _stream.Read(dataBuffer, offset, tempLength); - if (!_readData) { - throw new Exception(); - } - if (bytesReceived != tempLength) { - offset += bytesReceived; - tempLength -= bytesReceived; - } - else { - incomplete = false; + } + // For standard device functionalities handling + else { + _stream.Read(headerBuffer, 0, headerBuffer.Length); + if (!_readData) { + throw new Exception(); + } + int dataLength = await Task.Run(() => TerminalUtilities.HeaderLength(headerBuffer)); + if (dataLength > 0) { + byte[] dataBuffer = new byte[dataLength]; + + var incomplete = true; + int offset = 0; + int tempLength = dataLength; + + do { + + // Read data + int bytesReceived = _stream.Read(dataBuffer, offset, tempLength); + if (!_readData) { + throw new Exception(); + } + if (bytesReceived != tempLength) { + offset += bytesReceived; + tempLength -= bytesReceived; + } + else { + incomplete = false; + } + } while (incomplete); + + var readBuffer = new byte[dataLength]; + Array.Copy(dataBuffer, readBuffer, dataLength); + + if (isBroadcast(readBuffer)) { + _broadcastMessage = new BroadcastMessage(readBuffer); + OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); } - } while (incomplete); + else if (isKeepAlive(readBuffer) && INGENICO_GLOBALS.KeepAlive) { - var readBuffer = new byte[dataLength]; - Array.Copy(dataBuffer, readBuffer, dataLength); + _isKeepAlive = true; - if (isBroadcast(readBuffer)) { - _broadcastMessage = new BroadcastMessage(readBuffer); - OnBroadcastMessage?.Invoke(_broadcastMessage.Code, _broadcastMessage.Message); - } - else if (isKeepAlive(readBuffer) && INGENICO_GLOBALS.KeepAlive) { - - _isKeepAlive = true; + if (_isKeepAlive && !_isKeepAliveRunning) { + _stream.ReadTimeout = _settings.Timeout; + _isKeepAliveRunning = true; + } - if (_isKeepAlive && !_isKeepAliveRunning) { - _stream.ReadTimeout = _settings.Timeout; - _isKeepAliveRunning = true; + var keepAliveRep = KeepAliveResponse(readBuffer); + _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); + } + else { // Receiving request response data. + _termResponse = readBuffer; } - - var keepAliveRep = KeepAliveResponse(readBuffer); - _stream.WriteAsync(keepAliveRep, 0, keepAliveRep.Length).Wait(); } - else { // Receiving request response data. - _termResponse = readBuffer; + else { + _receivingException = new ApiException("No data received."); } - } - else { - _receivingException = new ApiException("No data received."); - } + } } } catch (Exception ex) { @@ -282,6 +328,10 @@ private async void AnalyzeReceivedData() { } } } + + private void PayAtTableHandler(byte[] buffer) { + OnPayAtTableRequest?.Invoke(new PATRequest(buffer)); + } #endregion } } From 5f2b8cba3844a9db4a633978b15e05aaed551083 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:50:16 +0800 Subject: [PATCH 118/120] Remove Overloaded method of HeaderLength that is not used. Created GetTextContent method in the TerminalUtilities for reading content of XML file path. --- .../Terminals/TerminalUtilities.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs index 8af0f1b0..fdd666a9 100644 --- a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs +++ b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs @@ -36,7 +36,7 @@ private static DeviceMessage BuildMessage(string messageId, string message) { // Begin Message buffer.Add((byte)ControlCodes.STX); - + // Add Message ID foreach (char c in messageId) buffer.Add((byte)c); @@ -196,18 +196,6 @@ public static int HeaderLength(byte[] buffer) { return int.Parse(_hex, System.Globalization.NumberStyles.HexNumber); } - public static void HeaderLength(byte[] buffer, out int result) { - // Conversion from decimal to hex value - var fHex = Convert.ToInt64(buffer[0]).ToString("X2"); - var sHex = Convert.ToInt64(buffer[1]).ToString("X2"); - - // Concat two hex value - var _hex = fHex + sHex; - - // Get decimal value of concatenated hex - result = int.Parse(_hex, System.Globalization.NumberStyles.HexNumber); - } - public static byte[] CalculateLRC(string requestMessage) { byte[] bytes = Encoding.ASCII.GetBytes((requestMessage + (char)ControlCodes.ETX)); byte lrc = 0; @@ -217,5 +205,14 @@ public static byte[] CalculateLRC(string requestMessage) { bytes = new byte[] { lrc }; return bytes; } + + public static string GetTextContent(string filePath) { + try { + return File.ReadAllText(filePath); + } + catch (Exception ex) { + throw ex; + } + } } } From 040c4dc954c39ee4d75e791dd75a55177061f8f9 Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Tue, 11 Aug 2020 20:51:01 +0800 Subject: [PATCH 119/120] Remove TerminalUtilities.log line. This is for debugging purposes. --- .../Terminals/Ingenico/PayAtTableRequestTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs index 7ec7954e..d0a347d8 100644 --- a/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs +++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs @@ -19,7 +19,6 @@ public class PayAtTableRequestTests { public PayAtTableRequestTests() { - TerminalUtilities.log("TERMINAL START CONNECTION..."); _device = DeviceService.Create(new ConnectionConfig() { DeviceType = DeviceType.Ingenico_EPOS_Desk5000, @@ -34,20 +33,17 @@ public PayAtTableRequestTests() { _device.OnPayAtTableRequest += _device_OnPayAtTableRequest; - TerminalUtilities.log("TERMINAL CONNECTED"); } private void _device_OnPayAtTableRequest(PATRequest request) { // Success ConfirmaitonOK - TerminalUtilities.log("OnPayAtTableRequest " + request.ToString() + " " + request.RequestType.ToString()); Thread.Sleep(5 * 1000); if (request.RequestType == PATRequestType.TableReceipt) { - TerminalUtilities.log("Receipt response"); _device.PayAtTableResponse() .WithXMLPath("C:\\tmp\\receipt.txt") .Execute(); From d01c761909bc74ef7cc12226c5d23f9083e4c36e Mon Sep 17 00:00:00 2001 From: Peter Yanong Date: Fri, 14 Aug 2020 15:19:39 +0800 Subject: [PATCH 120/120] Added TransactionOutcome and XMLData in enum for PATRequest. Create TransactionOutcomeRequest class for parsing Transaction outcome request message. Update PATRequest to handle TransctionOutcome and XML Data. --- .../Terminals/INGENICO/IngenicoEnums.cs | 6 ++++++ .../Terminals/INGENICO/Requests/PATRequest.cs | 16 +++++++------- .../Requests/TransactionOutcomeRequest.cs | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/GlobalPayments.Api/Terminals/INGENICO/Requests/TransactionOutcomeRequest.cs diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs index 17eb4f10..0336c692 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs @@ -228,6 +228,12 @@ public enum PATRequestType { /// TableList = 4, + /// + /// Indicates a Transaction Outcome request + /// + TransactionOutcome, + + XMLData } /// diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs index 1cc5cbbe..75d92ded 100644 --- a/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs @@ -1,6 +1,5 @@ using GlobalPayments.Api.Utils; using System; -using System.Collections.Generic; using System.Text; namespace GlobalPayments.Api.Terminals.Ingenico.Requests { @@ -12,10 +11,10 @@ public class PATRequest { public string TableId { get; set; } public string TID { get; set; } public string TerminalCurrency { get; set; } + public string XMLData { get; set; } + public TransactionOutcomeRequest TransactionOutcome { get; set; } - public TransactionStatus TransactionOutcomeStatus { get; set; } - - + // Assign passed value of buffer into private variable. public PATRequest(byte[] buffer) { _buffer = buffer; ParseData(); @@ -26,16 +25,19 @@ private void ParseData() { // Validating if data is XML Type if (strBuffer.Contains(INGENICO_GLOBALS.XML_TAG)) { - + RequestType = PATRequestType.XMLData; + XMLData = Encoding.UTF8.GetString(_buffer); } else { // Otherwise check if what type of message frame the request is. // Length of Ingenico Message Frame 2 if (_buffer.Length == INGENICO_GLOBALS.MSG_FRAME_TWO_LEN) { - TransactionOutcomeStatus = (TransactionStatus)Encoding.UTF8.GetString(_buffer.SubArray(2, 1)).ToInt32(); + RequestType = PATRequestType.TransactionOutcome; + TransactionOutcome = new TransactionOutcomeRequest(_buffer); } else { - RequestType = (PATRequestType)Convert.ToInt32(strBuffer.Substring(11, 1)); + RequestType = (PATRequestType)Convert + .ToInt32(strBuffer.Substring(11, 1)); var tlvData = new TypeLengthValue(_buffer); diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/Requests/TransactionOutcomeRequest.cs b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/TransactionOutcomeRequest.cs new file mode 100644 index 00000000..108cb8b6 --- /dev/null +++ b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/TransactionOutcomeRequest.cs @@ -0,0 +1,21 @@ +using GlobalPayments.Api.Utils; +using System.Text; + +namespace GlobalPayments.Api.Terminals.Ingenico.Requests { + public class TransactionOutcomeRequest { + private byte[] _buffer; + + public DataResponse RepField { get; set; } + public TransactionStatus Status { get; set; } + + public TransactionOutcomeRequest(byte[] buffer) { + _buffer = buffer; + ParseData(); + } + + private void ParseData() { + Status = (TransactionStatus)Encoding.UTF8.GetString(_buffer.SubArray(2, 1)).ToInt32(); + RepField = new DataResponse(_buffer.SubArray(11, 55)); + } + } +}