diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs
index 77ae4d74..ae18da1b 100644
--- a/src/GlobalPayments.Api/Entities/Enums.cs
+++ b/src/GlobalPayments.Api/Entities/Enums.cs
@@ -55,7 +55,32 @@ public enum DeviceType {
///
/// Indicates a genius terminal
///
- GENIUS
+ GENIUS,
+
+ ///
+ /// 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_EPOS_Move3500
+ }
+
+ ///
+ /// Indicates a device mode.
+ ///
+ public enum DeviceMode {
+ ///
+ /// For devices that supports Pay@Table functionalities.
+ ///
+ PAY_AT_TABLE
}
///
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.csproj
index 468dde4a..deaa26ea 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
@@ -18,7 +18,9 @@
-
+
+
+
@@ -29,4 +31,8 @@
+
+
+
+
diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs
index 2113d5bb..2200cb9b 100644
--- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceCommInterface.cs
@@ -9,5 +9,9 @@ public interface IDeviceCommInterface {
byte[] Send(IDeviceMessage message);
event MessageSentEventHandler OnMessageSent;
+
+ event BroadcastMessageEventHandler OnBroadcastMessage;
+
+ event PayAtTableRequestEventHandler OnPayAtTableRequest;
}
}
diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs
index 17ec5cf4..8f566fb7 100644
--- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceInterface.cs
@@ -2,14 +2,23 @@
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;
+ event PayAtTableRequestEventHandler OnPayAtTableRequest;
#region Admin Calls
- void Cancel();
+
+ ///
+ /// A method to Cancel a live transaction.
+ ///
+ /// Amount to be passed for cancel request.
+ /// TerminalManageBuilder
+ IDeviceResponse Cancel();
IDeviceResponse CloseLane();
IDeviceResponse DisableHostResponseBeep();
ISignatureResponse GetSignatureFile();
@@ -24,10 +33,44 @@ 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
+ ///
+ /// IDeviceResponse
+ IDeviceResponse Duplicate();
+
+ ///
+ /// Command used to gain feedback as to the status of the terminal.
+ ///
+ ///
+ IDeviceResponse GetTerminalStatus();
+
+ ///
+ /// Command used to request for CALL TMS in the terminal.
+ ///
+ /// IDeviceResponse
+ IDeviceResponse GetTerminalConfiguration();
+
+ IDeviceResponse TestConnection();
#endregion
#region reporting
TerminalReportBuilder LocalDetailReport();
+ ///
+ /// 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 initiate report and stores it in terminal's memory.
+ /// GetLastReceipt can be used to extract XML data after.
+ ///
+ /// Report Type
+ ///
+ TerminalReportBuilder GetReport(Ingenico.ReportType type);
#endregion
#region Batch Calls
@@ -65,14 +108,59 @@ public interface IDeviceInterface : IDisposable {
#region Generic Calls
TerminalAuthBuilder AddValue(decimal? amount = null);
+
+ ///
+ /// Instructs the terminal to transact a pre-authorization transaction.
+ ///
+ /// Amount
+ ///
TerminalAuthBuilder Authorize(decimal? amount = null);
TerminalAuthBuilder Balance();
TerminalManageBuilder Capture(decimal? amount = null);
+
+ ///
+ /// Instruct the terminal 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);
+
+ #endregion
+
+ #region Pay@Table Feature
+
+ ///
+ /// Response to terminal after Pay@Table request has been made.
+ ///
+ ///
+ ///
+ TerminalAuthBuilder PayAtTableResponse();
+
+ #endregion
}
}
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs
index 99058151..70c6f8da 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 {
@@ -10,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;
}
}
@@ -30,11 +32,19 @@ 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 string CurrencyCode { get; set; }
+ 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;
@@ -48,9 +58,16 @@ 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))
+ if (PaymentMethod == null || !(PaymentMethod is TransactionReference)) {
PaymentMethod = new TransactionReference();
+ }
(PaymentMethod as TransactionReference).AuthCode = value;
return this;
}
@@ -64,6 +81,12 @@ public TerminalAuthBuilder WithAutoSubstantiation(AutoSubstantiation value) {
AutoSubstantiation = value;
return this;
}
+
+ ///
+ /// Sets the cash back for the transaction.
+ ///
+ ///
+ ///
public TerminalAuthBuilder WithCashBack(decimal? amount) {
CashBackAmount = amount;
return this;
@@ -126,9 +149,71 @@ public TerminalAuthBuilder WithTransactionId(string value) {
return this;
}
+ ///
+ /// 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;
+ }
+
+ public TerminalAuthBuilder WithPaymentMode(PATPaymentMode additionalMessage) {
+ AdditionalMessage = additionalMessage;
+ return this;
+ }
+
+ ///
+ /// Sets the table number for the transaction.
+ ///
+ /// Table Number
+ ///
+ public TerminalAuthBuilder WithTableNumber(string value) {
+ TableNumber = 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;
+ }
+
+ 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) {
}
+ ///
+ /// Executes the transaction.
+ ///
+ ///
+ ///
public override ITerminalResponse Execute(string configName = "default") {
base.Execute(configName);
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs
index 8fd725c4..06e2ab68 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 {
@@ -10,12 +11,32 @@ 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;
+ }
+ }
+ internal string CurrencyCode { get; set; }
+ internal PaymentMode PaymentMode { 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;
+ return this;
+ }
+
public TerminalManageBuilder WithTransactionId(string value) {
if (PaymentMethod == null || !(PaymentMethod is TransactionReference))
PaymentMethod = new TransactionReference();
(PaymentMethod as TransactionReference).TransactionId = value;
+
return this;
}
@@ -57,7 +93,7 @@ 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();
}
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalReportBuilder.cs
index 56156875..454f4979 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,8 @@
namespace GlobalPayments.Api.Terminals.Builders {
public class TerminalReportBuilder {
internal TerminalReportType ReportType { get; set; }
+ internal ReceiptType ReceiptType { get; set; }
+ internal ReportType? Type { get; set; }
private TerminalSearchBuilder _searchBuilder;
internal TerminalSearchBuilder SearchBuilder {
@@ -22,6 +25,14 @@ public TerminalReportBuilder(TerminalReportType reportType) {
ReportType = reportType;
}
+ public TerminalReportBuilder(ReceiptType receiptType) {
+ ReceiptType = receiptType;
+ }
+
+ public TerminalReportBuilder(ReportType reportType) {
+ Type = reportType;
+ }
+
public TerminalSearchBuilder Where(PaxSearchCriteria criteria, T value) {
return SearchBuilder.And(criteria, value);
}
diff --git a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs
index f200899b..ee9f0d65 100644
--- a/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs
+++ b/src/GlobalPayments.Api/Terminals/ConnectionConfig.cs
@@ -3,31 +3,26 @@
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,
r115200 = 115200
}
- public enum Parity {
- None = 0,
- Odd,
- Even,
- }
- public enum StopBits {
- One = 1,
- Two
- }
public enum DataBits {
Seven = 7,
Eight = 8
@@ -47,12 +42,16 @@ public interface ITerminalConfiguration {
Parity Parity { get; set; }
StopBits StopBits { get; set; }
DataBits DataBits { get; set; }
+ Handshake Handshake { get; set; }
// Timeout
int Timeout { get; set; }
// Associated Gateway
GatewayConfig GatewayConfig { get; set; }
+
+ // Pay@Table
+ DeviceMode? DeviceMode { get; set; }
}
public class ConnectionConfig : Configuration, ITerminalConfiguration {
@@ -62,10 +61,12 @@ 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; }
public GatewayConfig GatewayConfig { get; set; }
+ public DeviceMode? DeviceMode { get; set; }
public ConnectionConfig() {
Timeout = -1;
@@ -84,8 +85,13 @@ 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_EPOS_Desk5000:
+ case DeviceType.Ingenico_EPOS_Lane3000:
+ case DeviceType.Ingenico_EPOS_Move3500:
+ services.DeviceController = new IngenicoController(this);
+ break;
default:
break;
}
@@ -97,7 +103,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/DeviceController.cs b/src/GlobalPayments.Api/Terminals/DeviceController.cs
index c494ca55..8d6e6028 100644
--- a/src/GlobalPayments.Api/Terminals/DeviceController.cs
+++ b/src/GlobalPayments.Api/Terminals/DeviceController.cs
@@ -33,6 +33,8 @@ public IRequestIdProvider RequestIdProvider {
}
public event MessageSentEventHandler OnMessageSent;
+ public event BroadcastMessageEventHandler OnBroadcastMessage;
+ public event PayAtTableRequestEventHandler OnPayAtTableRequest;
internal DeviceController(ITerminalConfiguration settings) {
_settings = settings;
@@ -40,6 +42,14 @@ internal DeviceController(ITerminalConfiguration settings) {
_connector.OnMessageSent += (message) => {
OnMessageSent?.Invoke(message);
};
+
+ _connector.OnBroadcastMessage += (code, message) => {
+ OnBroadcastMessage?.Invoke(code, message);
+ };
+
+ _connector.OnPayAtTableRequest += (request) => {
+ OnPayAtTableRequest?.Invoke(request);
+ };
}
public byte[] Send(IDeviceMessage message) {
diff --git a/src/GlobalPayments.Api/Terminals/DeviceInterface.cs b/src/GlobalPayments.Api/Terminals/DeviceInterface.cs
index 859d6020..ecc77952 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,17 +11,29 @@ public abstract class DeviceInterface : IDeviceInterface where T : DeviceCont
protected IRequestIdProvider _requestIdProvider;
public event MessageSentEventHandler OnMessageSent;
+ public event BroadcastMessageEventHandler OnBroadcastMessage;
+ public event PayAtTableRequestEventHandler OnPayAtTableRequest;
internal DeviceInterface(T controller) {
_controller = controller;
_controller.OnMessageSent += (message) => {
OnMessageSent?.Invoke(message);
};
+
+ _controller.OnBroadcastMessage += (code, message) => {
+ OnBroadcastMessage?.Invoke(code, message);
+ };
+
+ _controller.OnPayAtTableRequest += (request) => {
+ OnPayAtTableRequest?.Invoke(request);
+ };
+
+
_requestIdProvider = _controller.RequestIdProvider;
}
#region Admin Methods
- public virtual void Cancel() {
+ public virtual IDeviceResponse Cancel() {
throw new UnsupportedTransactionException("This function is not supported by the currently configured device.");
}
@@ -79,6 +92,22 @@ 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.");
+ }
+
+ 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
@@ -95,6 +124,15 @@ public virtual IEODResponse EndOfDay() {
public virtual TerminalReportBuilder 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);
+ }
+
+ public virtual TerminalReportBuilder GetReport(Ingenico.ReportType type) {
+ //return new TerminalReportBuilder(TerminalReportType.LocalDetailReport).WithReportType(type);
+ return new TerminalReportBuilder(type);
+ }
+
#endregion
#region Transactions
@@ -121,8 +159,10 @@ 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);
+ return new TerminalAuthBuilder(TransactionType.Verify, PaymentMethodType.Credit)
+ .WithAmount(6.18m);
}
public virtual TerminalManageBuilder Void() {
return new TerminalManageBuilder(TransactionType.Void, PaymentMethodType.Credit);
@@ -138,5 +178,25 @@ 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);
+ }
+
+ #endregion
+
+ #region Pay@Table Methods
+
+ public virtual TerminalAuthBuilder PayAtTableResponse() {
+ return new TerminalAuthBuilder(TransactionType.Void, PaymentMethodType.Other);
+ }
+
+ #endregion
+
+ #endregion
}
}
diff --git a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs
index 52515891..eaa44b98 100644
--- a/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/Genius/Interfaces/GeniusHttpInterface.cs
@@ -17,6 +17,8 @@ internal class GeniusHttpInterface : IDeviceCommInterface {
private GeniusConfig _gatewayConfig;
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/HpaInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/HpaInterface.cs
index 71be05bd..9f4c4226 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 IDeviceResponse Cancel() {
// TODO: Cancel for HPA?
Reset();
+ return null;
}
public override IDeviceResponse CloseLane() {
diff --git a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs
index 8ebdb03b..af4bae6a 100644
--- a/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/HPA/Interfaces/HpaTcpInterface.cs
@@ -16,6 +16,8 @@ internal class HpaTcpInterface : IDeviceCommInterface {
List message_queue;
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/IngenicoController.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs
new file mode 100644
index 00000000..e76bad42
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoController.cs
@@ -0,0 +1,315 @@
+using GlobalPayments.Api.Builders;
+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;
+
+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_SERVER:
+ return new IngenicoTcpInterface(_settings);
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ #region overrides
+ internal override ITerminalResponse ManageTransaction(TerminalManageBuilder builder) {
+ IDeviceMessage request = BuildManageTransaction(builder);
+
+ if (builder.TransactionType == TransactionType.Reversal) {
+ return DoReverseRequest(request);
+ }
+ else {
+ return DoRequest(request);
+ }
+ }
+
+ internal override ITerminalReport ProcessReport(TerminalReportBuilder builder) {
+ IDeviceMessage request;
+ 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 XmlRequest(request);
+ }
+ }
+
+ internal override ITerminalResponse ProcessTransaction(TerminalAuthBuilder builder) {
+ IDeviceMessage request = BuildProcessTransaction(builder);
+ return DoRequest(request);
+ }
+
+ internal override byte[] SerializeRequest(TerminalAuthBuilder builder) {
+ return BuildProcessTransaction(builder).GetSendBuffer();
+ }
+
+ internal override byte[] SerializeRequest(TerminalManageBuilder builder) {
+ return BuildManageTransaction(builder).GetSendBuffer();
+ }
+
+ internal override byte[] SerializeRequest(TerminalReportBuilder builder) {
+ throw new NotImplementedException();
+ }
+ #endregion
+
+ #region Methods
+ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) {
+ 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";
+
+ // 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 != null && builder.TransactionType == TransactionType.Reversal) {
+ extendedData = INGENICO_REQ_CMD.REVERSE_WITH_ID.FormatWith(builder.TransactionId);
+ }
+ else if (builder.TransactionType == TransactionType.Reversal) {
+ extendedData = INGENICO_REQ_CMD.REVERSE;
+ }
+
+ // 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.BuildRequest(sb.ToString(), settings: _settings.ConnectionMode);
+ }
+
+ 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;
+ 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);
+
+ // 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));
+
+ 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.BuildRequest(message, settings: _settings.ConnectionMode);
+ }
+
+ internal IDeviceMessage BuildReportTransaction(TerminalReportBuilder builder) {
+ if (!IsObjectNullOrEmpty(builder.Type)) {
+ string message = INGENICO_REQ_CMD.REPORT.FormatWith(builder.Type);
+ return TerminalUtilities.BuildRequest(message, settings: _settings.ConnectionMode);
+ }
+ else {
+ throw new BuilderException("Type of report is missing in 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;
+ if (value == null || string.IsNullOrWhiteSpace(value.ToString())) {
+ response = true;
+ }
+ else {
+ response = false;
+ }
+
+ return response;
+ }
+
+ private void ValidateTableId(string value) {
+ if (value.Length > 8) {
+ throw new BuilderException("The maximum length for table number is 8.");
+ }
+ }
+
+ 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 int ValidatePaymentMode(PaymentMode? paymentMode) {
+ if (IsObjectNullOrEmpty(paymentMode)) {
+ paymentMode = PaymentMode.APPLICATION;
+ }
+
+ return (int)paymentMode;
+ }
+
+ private string ValidateCurrency(string currencyCode) {
+ if (!string.IsNullOrWhiteSpace(currencyCode)) {
+ currencyCode = currencyCode.PadLeft(3, '0');
+ }
+ else {
+ currencyCode = "826";
+ }
+
+ return currencyCode.Substring(0, 3);
+ }
+
+ private decimal? ValidateAmount(decimal? amount) {
+ 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.");
+ }
+ else {
+ throw new BuilderException("Invalid input amount.");
+ }
+ return amount;
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Request
+ internal IngenicoTerminalResponse DoRequest(IDeviceMessage request) {
+ byte[] response = Send(request);
+ return new IngenicoTerminalResponse(response);
+ }
+
+ private CancelResponse DoCancelRequest(IDeviceMessage request) {
+ byte[] response = Send(request);
+ return new CancelResponse(response);
+ }
+
+ private IngenicoTerminalResponse DoReverseRequest(IDeviceMessage request) {
+ byte[] response = Send(request);
+ return new IngenicoTerminalResponse(response);
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs
new file mode 100644
index 00000000..0336c692
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoEnums.cs
@@ -0,0 +1,262 @@
+
+using GlobalPayments.Api.Utils;
+using System;
+
+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}";
+
+ // 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}";
+ 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;
+ 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 = "", "LF" };
+ public readonly static string INVALID = "\u0005\u0004";
+ public readonly static string ENDXML = "";
+ }
+
+ 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,
+ TAXFREE,
+ REPORT
+ }
+
+ public enum ReportType {
+ EOD,
+ BANKING,
+ XBAL,
+ ZBAL
+ }
+
+ 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,
+ CANCEL_FAILED = 7
+ }
+
+ public enum DynamicCurrencyStatus {
+ CONVERSION_APPLIED = 1,
+ REJECTED = 0
+ }
+
+ public enum TransactionSubTypes {
+ [Description("S")]
+ SPLIT_SALE_TXN,
+
+ [Description("D")]
+ DCC_TXN,
+
+ [Description("R")]
+ REFERRAL_RESULT
+ }
+
+ public enum TerminalStatus {
+ NOT_READY = 0,
+ READY = 1
+ }
+
+ public enum SalesMode {
+ STANDARD_SALE_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 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
+ }
+
+ 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,
+
+ ///
+ /// 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,
+
+ ///
+ /// Indicates a Transaction Outcome request
+ ///
+ TransactionOutcome,
+
+ XMLData
+ }
+
+ ///
+ /// 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
diff --git a/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs
new file mode 100644
index 00000000..00964f3c
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/IngenicoInterface.cs
@@ -0,0 +1,151 @@
+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 Terminal Management/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);
+ }
+
+ 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);
+ }
+
+ 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
+
+ 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);
+ }
+
+ public override TerminalAuthBuilder Verify() {
+ paymentMethod = PaymentType.AccountVerification;
+ return base.Verify();
+ }
+ #endregion
+
+ #region XML & Report Management
+ public override TerminalReportBuilder 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 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 IngenicoTerminalResponse(response);
+ }
+
+ 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 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
+
+ public override TerminalAuthBuilder PayAtTableResponse() {
+ return base.PayAtTableResponse();
+ }
+ }
+}
\ No newline at end of file
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..c8eb8188
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoSerialInterface.cs
@@ -0,0 +1,245 @@
+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;
+using System.Management;
+
+namespace GlobalPayments.Api.Terminals.Ingenico {
+ internal class IngenicoSerialInterface : IDeviceCommInterface {
+ private SerialPort _serialPort;
+ private ITerminalConfiguration _settings;
+
+ 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();
+
+ public event MessageSentEventHandler OnMessageSent;
+ public event BroadcastMessageEventHandler OnBroadcastMessage;
+ public event PayAtTableRequestEventHandler OnPayAtTableRequest;
+
+ public IngenicoSerialInterface(ITerminalConfiguration settings) {
+ this._settings = settings;
+ Connect();
+ }
+
+ public void Connect() {
+ 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
+ };
+ }
+ }
+ }
+
+ 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.");
+ }
+ }
+
+ private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e) {
+ SerialPort serial = (SerialPort)sender;
+ do {
+ Thread.Sleep(0100);
+ _bufferReceived = serial.ReadExisting();
+
+ if (!string.IsNullOrEmpty(_bufferReceived)) {
+ _serialPort.ReadTimeout = _settings.Timeout;
+
+ 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);
+ break;
+ } else if (_bufferReceived.Contains(INGENICO_GLOBALS.BROADCAST)) {
+ _isBroadcast = true;
+ break;
+ } 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;
+ break;
+ }
+ }
+ } while (true);
+ }
+
+ public void Disconnect() {
+ _serialPort.Close();
+ _serialPort?.Dispose();
+ _serialPort = null;
+ }
+
+ 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 (_serialPort == null) {
+ return false;
+ }
+
+ do {
+ _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 };
+ _serialPort.Write(_b, 0, 1);
+ }
+
+ 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);
+ BroadcastMessage broadcastMsg = new BroadcastMessage(bMsg);
+ OnBroadcastMessage?.Invoke(broadcastMsg.Code, broadcastMsg.Message);
+ _isBroadcast = false;
+ }
+
+ if (_isXML) {
+ do {
+ _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;
+ }
+ }
+ Thread.Sleep(0500);
+ } while (!_transComplete);
+ }
+
+ if (_isResult) {
+ string check = Encoding.UTF8.GetString(message.GetSendBuffer());
+ 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);
+ if (validateLRC) {
+ if (MessageReceived(rData)) {
+ _serialPort.Write(BitConverter.GetBytes((char)ControlCodes.ACK), 0, 1);
+ _isResult = false;
+ _transComplete = true;
+ }
+ }
+ } while (!_transComplete);
+ }
+ }
+ } while (!_transComplete);
+
+ return _transComplete;
+ }
+ } while (true);
+ } catch (MessageException e) {
+ throw new MessageException(e.Message);
+ }
+ });
+ }
+
+ public byte[] Send(IDeviceMessage message) {
+ try {
+ 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)) {
+ throw new MessageException("Terminal did not response within timeout.");
+ }
+
+ return _messageResponse.ToArray();
+ } else {
+ throw new MessageException("Terminal not connected.");
+ }
+ } finally {
+ _transComplete = false;
+ }
+ }
+
+ 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
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..ec75835a
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Interfaces/IngenicoTcpInterface.cs
@@ -0,0 +1,337 @@
+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 GlobalPayments.Api.Terminals.Ingenico.Requests;
+
+namespace GlobalPayments.Api.Terminals.Ingenico {
+ internal class IngenicoTcpInterface : IDeviceCommInterface {
+ 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 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 event PayAtTableRequestEventHandler OnPayAtTableRequest;
+
+ 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) {
+
+ _readData = false;
+ _stream.ReadTimeout = 1;
+
+ // Closing and disposing current clients
+ while (true) {
+ if (_disposable) {
+ _client.Close();
+ _client.Dispose();
+ break;
+ }
+ }
+ // 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;
+ _isResponseNeeded = true;
+
+ try {
+ // Validate if server is starting
+ if (!_listener.Active) {
+ throw new ConfigurationException("Server is not running.");
+ }
+
+ // Validate keep alive for setting of timeout during Transaction
+ _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)));
+
+ if (_settings.DeviceMode == DeviceMode.PAY_AT_TABLE) {
+ return null;
+ }
+
+ 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;
+ }
+
+ _isResponseNeeded = false;
+ _receivingException = 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;
+
+ // Initialize keep Alive value to false.
+ _isKeepAlive = false;
+ _isKeepAliveRunning = false;
+
+ _readData = true;
+ _disposable = false;
+ }
+ else {
+ throw new ConfigurationException("Server already initialize.");
+ }
+ }
+
+ 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();
+ }
+ }
+
+ private bool isBroadcast(byte[] terminalResponse) {
+ return Encoding.UTF8.GetString(terminalResponse).Contains(INGENICO_GLOBALS.BROADCAST);
+ }
+
+ 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 async void AnalyzeReceivedData() {
+ try {
+ var headerBuffer = new byte[2];
+ while (_readData) {
+
+ // For Pay@Table functionalities handling.
+ if (_settings.DeviceMode == DeviceMode.PAY_AT_TABLE) {
+
+ _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);
+ }
+
+ }
+ // 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);
+ }
+ 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();
+ }
+ else { // Receiving request response data.
+ _termResponse = readBuffer;
+ }
+ }
+ else {
+ _receivingException = new ApiException("No data received.");
+ }
+
+ }
+ }
+ }
+ catch (Exception ex) {
+ if (_isResponseNeeded || _isKeepAlive) {
+ _receivingException = ex;
+ _stream.ReadTimeout = -1;
+ _isKeepAlive = false;
+ }
+
+ if (_readData) {
+ AnalyzeReceivedData();
+ }
+ else {
+ _disposable = true;
+ }
+ }
+ }
+
+ private void PayAtTableHandler(byte[] buffer) {
+ OnPayAtTableRequest?.Invoke(new PATRequest(buffer));
+ }
+ #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..c15fcfad
--- /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; } }
+ }
+}
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..75d92ded
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Requests/PATRequest.cs
@@ -0,0 +1,56 @@
+using GlobalPayments.Api.Utils;
+using System;
+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 string XMLData { get; set; }
+ public TransactionOutcomeRequest TransactionOutcome { get; set; }
+
+ // Assign passed value of buffer into private variable.
+ 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)) {
+ 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) {
+ RequestType = PATRequestType.TransactionOutcome;
+ TransactionOutcome = new TransactionOutcomeRequest(_buffer);
+ }
+ 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);
+ }
+ }
+}
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));
+ }
+ }
+}
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..77a6e50a
--- /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.");
+ }
+ }
+ }
+}
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..e17badf0
--- /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();
+ }
+ }
+}
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..31685656
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DataResponse.cs
@@ -0,0 +1,107 @@
+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 string _finalAmount;
+ private PaymentMethod? _paymentMethod;
+ private string _cashbackAmount;
+ private string _gratuityAmount;
+ private string _availableAmount;
+ private string _dccCode;
+ private string _dccAmount;
+ private TransactionSubTypes? _txnSubType;
+ private string _splitSaleAmount;
+ private DynamicCurrencyStatus? _dccStatus;
+
+ private byte[] _buffer;
+
+ public DataResponse(byte[] buffer) {
+ _buffer = buffer;
+ ParseData();
+ }
+
+ #region Property Fields
+
+ public string AuthorizationCode {
+ get { return _authCode ?? ""; }
+ private set { }
+ }
+
+ public decimal? FinalAmount {
+ get { return _finalAmount.ToAmount(); }
+ set { _finalAmount = value.ToString(); }
+ }
+
+ public PaymentMethod? PaymentMethod {
+ get { return _paymentMethod; }
+ set { _paymentMethod = value; }
+ }
+
+ public decimal? CashbackAmount {
+ get { return _cashbackAmount.ToAmount(); }
+ set { _cashbackAmount = value.ToString(); }
+ }
+
+ public decimal? GratuityAmount {
+ get { return _gratuityAmount.ToAmount(); }
+ set { _gratuityAmount = value.ToString(); }
+ }
+
+ public decimal? AvailableAmount {
+ get { return _availableAmount.ToAmount(); }
+ set { _availableAmount = value.ToString(); }
+ }
+ public string DccCode {
+ get { return _dccCode; }
+ set { _dccCode = value; }
+ }
+
+ public decimal? DccAmount {
+ get { return _dccAmount.ToAmount(); }
+ set { _dccAmount = value.ToString(); }
+ }
+
+ public TransactionSubTypes? TransactionSubType {
+ get { return _txnSubType; }
+ set { _txnSubType = value; }
+ }
+
+ public decimal? SplitSaleAmount {
+ get { return _splitSaleAmount.ToAmount(); }
+ set { _splitSaleAmount = value.ToString(); }
+ }
+
+ public DynamicCurrencyStatus? DccStatus {
+ get { return _dccStatus; }
+ set { _dccStatus = value; }
+ }
+
+ #endregion
+
+ private void ParseData() {
+
+ var tlv = new TypeLengthValue(_buffer);
+
+ Type stringType = typeof(string);
+
+ _authCode = (string)tlv.GetValue((byte)RepFieldCode.AuthCode, stringType);
+ _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 = (string)tlv.GetValue((byte)RepFieldCode.DccConvertedAmount, stringType);
+ _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?));
+ }
+ }
+}
\ No newline at end of file
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..72c8b9a5
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/DeviceResponse.cs
@@ -0,0 +1,124 @@
+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;
+using System;
+
+namespace GlobalPayments.Api.Terminals.Ingenico {
+ public abstract class IngenicoBaseResponse : DeviceResponse {
+ protected byte[] _buffer;
+ protected ParseFormat _parseFormat;
+ internal DataResponse _respField;
+
+ #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; }
+
+ #endregion
+
+ internal IngenicoBaseResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) {
+ _buffer = buffer;
+ _parseFormat = format;
+ 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();
+ 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;
+ }
+ }
+ }
+
+ public override string ToString() {
+ DeviceResponseText = Encoding.UTF8.GetString(_buffer, 0, _buffer.Length);
+ return DeviceResponseText;
+ }
+
+ }
+
+ public class IngenicoTerminalResponse : IngenicoBaseResponse, ITerminalResponse {
+
+ internal IngenicoTerminalResponse(byte[] buffer, ParseFormat format = ParseFormat.Transaction) : base(buffer, format) { }
+
+ #region Properties
+ 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 { } }
+ 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; }
+ public decimal? MerchantFee { get; set; }
+ public string ResponseText { get; set; }
+ #endregion
+
+ }
+
+ public class IngenicoTerminalReportResponse : IngenicoBaseResponse, ITerminalReport {
+
+ internal IngenicoTerminalReportResponse(byte[] buffer) : base(buffer) {
+ _buffer = buffer;
+ Status = _buffer.Length > 0 ? "SUCCESS" : "FAILED";
+ }
+
+ public override string ToString() {
+ return Encoding.ASCII.GetString(_buffer);
+ }
+ }
+}
\ No newline at end of file
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();
+ }
+ }
+}
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..cd4f021d
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/INGENICO/Responses/ReportResponse.cs
@@ -0,0 +1,11 @@
+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);
+ }
+ }
+}
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..dfe7524f
--- /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();
+ }
+ }
+}
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..3323df4c
--- /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, ParseFormat.State) {
+ }
+
+ 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));
+ }
+ }
+}
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/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);
+}
diff --git a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs
index 5f8229ea..20de9ad1 100644
--- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxHttpInterface.cs
@@ -13,6 +13,8 @@ internal class PaxHttpInterface : IDeviceCommInterface {
WebRequest _client;
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 2f440631..13335ae8 100644
--- a/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/PAX/Interfaces/PaxTcpInterface.cs
@@ -14,6 +14,8 @@ internal class PaxTcpInterface : IDeviceCommInterface {
int _connectionCount = 0;
public event MessageSentEventHandler OnMessageSent;
+ public event BroadcastMessageEventHandler OnBroadcastMessage;
+ public event PayAtTableRequestEventHandler OnPayAtTableRequest;
public PaxTcpInterface(ITerminalConfiguration settings) {
_settings = settings;
diff --git a/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs b/src/GlobalPayments.Api/Terminals/PAX/PaxInterface.cs
index 29b52734..fd37cd56 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 IDeviceResponse Cancel() {
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;
}
}
diff --git a/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs b/src/GlobalPayments.Api/Terminals/TerminalUtilities.cs
index c2647ec6..fdd666a9 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 {
@@ -35,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);
@@ -61,6 +62,33 @@ 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;
+ 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();
@@ -142,5 +170,49 @@ 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;
+ }
+
+ public static string GetTextContent(string filePath) {
+ try {
+ return File.ReadAllText(filePath);
+ }
+ catch (Exception ex) {
+ throw ex;
+ }
+ }
}
}
diff --git a/src/GlobalPayments.Api/Utils/Extensions.cs b/src/GlobalPayments.Api/Utils/Extensions.cs
index be53c37b..eca72018 100644
--- a/src/GlobalPayments.Api/Utils/Extensions.cs
+++ b/src/GlobalPayments.Api/Utils/Extensions.cs
@@ -206,5 +206,10 @@ 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;
+ }
}
}
diff --git a/src/GlobalPayments.Api/Utils/TypeLengthValue.cs b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs
new file mode 100644
index 00000000..1a3b76f1
--- /dev/null
+++ b/src/GlobalPayments.Api/Utils/TypeLengthValue.cs
@@ -0,0 +1,78 @@
+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(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;
+ }
+ }
+}
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..5601183c
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/IngenicoTransactionTests.cs
@@ -0,0 +1,167 @@
+using System;
+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;
+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_EPOS_Desk5000,
+ ConnectionMode = ConnectionModes.TCP_IP_SERVER,
+ Port = "18101",
+ Timeout = 30 * 1000
+ });
+ Assert.IsNotNull(_device);
+ }
+
+ [TestMethod]
+ public void Test() {
+ SaleTest();
+ RefundTest();
+ PreAuthTest();
+ CompletionTest();
+ }
+
+ public void SaleTest() {
+ _device.OnMessageSent += (message) => {
+ Assert.IsNotNull(message);
+ };
+
+ var resp = _device.Sale(6.18m)
+ .WithReferenceNumber(1)
+ .WithCurrencyCode("826")
+ .WithCashBack(3m)
+ .Execute();
+
+ Assert.IsNotNull(resp);
+
+ Thread.Sleep(5000);
+ }
+
+ public void RefundTest() {
+ _device.OnMessageSent += (message) => {
+ Assert.IsNotNull(message);
+ };
+
+ var resp = _device.Refund(6.18m)
+ .WithReferenceNumber(1)
+ .WithCurrencyCode("826")
+ .WithCashBack(3m)
+ .Execute();
+
+ Assert.IsNotNull(resp);
+
+ Thread.Sleep(5000);
+ }
+
+ public void PreAuthTest() {
+ _device.OnMessageSent += (message) => {
+ Assert.IsNotNull(message);
+ };
+
+ var resp = _device.Authorize(6.18m)
+ .WithReferenceNumber(1)
+ .WithCurrencyCode("826")
+ .WithCashBack(3m)
+ .Execute();
+
+ Assert.IsNotNull(resp);
+
+ Thread.Sleep(5000);
+ }
+
+ [TestMethod]
+ public void CompletionTest() {
+ _device.OnMessageSent += (message) => {
+ Assert.IsNotNull(message);
+ };
+
+ var resp = _device.Capture(6.18m)
+ .WithAuthCode("025433")
+ .WithReferenceNumber(1)
+ .WithCurrencyCode("826")
+ .Execute();
+
+ Assert.IsNotNull(resp);
+
+ Thread.Sleep(5000);
+ }
+
+ [TestMethod]
+ public void AsyncCancelTest() {
+
+ Thread thSale = new Thread(new ThreadStart(() => {
+ var resp = _device.Sale(6.18m)
+ .WithReferenceNumber(1)
+ .WithCurrencyCode("826")
+ .WithTaxFree(TaxFreeType.CASH)
+ .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 CancelTest() {
+ var resp = _device.Cancel();
+
+ 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);
+ }
+ }
+ }
+}
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..d0a347d8
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/PayAtTableRequestTests.cs
@@ -0,0 +1,65 @@
+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() {
+
+
+
+ _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;
+
+ }
+
+ private void _device_OnPayAtTableRequest(PATRequest request) {
+ // Success ConfirmaitonOK
+
+
+ Thread.Sleep(5 * 1000);
+
+
+
+ if (request.RequestType == PATRequestType.TableReceipt) {
+ _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);
+ }
+ }
+}
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..61e2b85a
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SaleTransactionManagement.cs
@@ -0,0 +1,138 @@
+using System;
+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_EPOS_Lane3000,
+ 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.ToString());
+ }
+ }
+}
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..f36b9b33
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/SerialPaymentTransactions.cs
@@ -0,0 +1,180 @@
+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.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_EPOS_Lane3000,
+ ConnectionMode = ConnectionModes.SERIAL,
+ Port = "5",
+ BaudRate = BaudRate.r9600,
+ DataBits = DataBits.Seven,
+ StopBits = System.IO.Ports.StopBits.One,
+ Parity = System.IO.Ports.Parity.Even,
+ Handshake = System.IO.Ports.Handshake.None,
+ Timeout = 65000
+ });
+
+ 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(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(PaymentMode.APPLICATION)
+ .WithCurrencyCode("826")
+ .WithTableNumber("1")
+ .Execute();
+
+ Assert.IsNotNull(response);
+ }
+
+ [TestMethod]
+ public void preAuth() {
+ var response = _device.Authorize(20.00m)
+ .WithReferenceNumber(1)
+ .WithPaymentMode(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(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.ToString();
+
+ if (splitR.ToString().Contains(""))
+ Assert.IsNotNull(splitR);
+ }
+
+ [TestMethod]
+ public void Ticket() {
+ ITerminalReport res = _device
+ .GetLastReceipt(ReceiptType.TICKET)
+ .Execute();
+
+ string test = res.ToString();
+
+ if (res.ToString().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.
+ */
+ ITerminalReport res = _device
+ .GetReport(ReportType.EOD)
+ .Execute();
+
+ Assert.IsNotNull(res);
+ }
+
+ [TestMethod]
+ public void Cashback() {
+ var response = _device.Sale(20.00m)
+ .WithReferenceNumber(01)
+ .WithPaymentMode(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(PaymentMode.APPLICATION)
+ .WithCurrencyCode("826")
+ .Execute();
+
+ Assert.IsNotNull(response);
+ }
+ }
+}
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..cc5c1270
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TerminalManagementTest.cs
@@ -0,0 +1,87 @@
+using GlobalPayments.Api.Services;
+using GlobalPayments.Api.Terminals;
+using GlobalPayments.Api.Terminals.Ingenico;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Text;
+using System.Threading;
+
+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 = 5 * 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 PIDCommandTest() {
+ try {
+ var resp = _device.Initialize();
+
+ Assert.IsNotNull(resp);
+ }
+ catch (Exception e) {
+ Assert.Fail(e.Message);
+ }
+ }
+
+ [TestMethod]
+ public void CALLTMSCommandTest() {
+ try {
+ var resp = _device.GetTerminalConfiguration();
+
+ Assert.IsNotNull(resp);
+ Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData);
+ }
+ catch (Exception e) {
+ Assert.Fail(e.Message);
+ }
+ }
+
+ [TestMethod]
+ public void LOGONCommandTest() {
+ try {
+ var resp = _device.TestConnection();
+
+ Assert.IsNotNull(resp);
+ Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData);
+ }
+ catch (Exception e) {
+ Assert.Fail(e.Message);
+ }
+ }
+
+ [TestMethod]
+ public void RESETCommandTest() {
+ try {
+ var resp = _device.Reboot();
+
+ Assert.IsNotNull(resp);
+ Assert.IsNotNull((resp as IngenicoTerminalResponse).PrivateData);
+ }
+ catch (Exception e) {
+ Assert.Fail(e.Message);
+ }
+ }
+ }
+}
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..0483167a
--- /dev/null
+++ b/tests/GlobalPayments.Api.Tests/Terminals/Ingenico/TransactionTest.cs
@@ -0,0 +1,86 @@
+using System.Threading;
+using System.Threading.Tasks;
+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 {
+ [TestClass]
+ public class TransactionTest {
+ IDeviceInterface _device;
+
+ public TransactionTest() {
+ _device = DeviceService.Create(new ConnectionConfig() {
+ DeviceType = DeviceType.Ingenico_EPOS_Lane3000,
+ 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();
+
+ 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();
+
+ _device.Dispose();
+ Assert.IsNotNull(duplicate);
+ Assert.AreEqual(((IngenicoTerminalResponse)duplicate).PrivateData, "DUPLICDONE");
+ }
+ }
+}