From 2a94f796117518d3b4a2d4f89153f72f1db28d07 Mon Sep 17 00:00:00 2001
From: globalpaymentsinc <52252853+globalpaymentsinc@users.noreply.github.com>
Date: Thu, 10 Nov 2022 12:33:21 -0700
Subject: [PATCH 01/19] Add files via upload
---
.../GlobalPayments.Api.4.6.csproj | 642 ++++++++++++++++++
1 file changed, 642 insertions(+)
create mode 100644 src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
new file mode 100644
index 00000000..83797323
--- /dev/null
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -0,0 +1,642 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {547DFAEC-5056-4322-A771-4C631C9BBABC}
+ Library
+ Properties
+ GlobalPayments.Api
+ GlobalPayments.Api
+ v4.6.1
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+
+
+ BlueBridge.pfx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 13.0.1
+
+
+
+
+
+
+
\ No newline at end of file
From 604278998382314b61894455fcdb693231fbffa5 Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Thu, 10 Nov 2022 13:54:53 -0700
Subject: [PATCH 02/19] Updates
---
dir.txt | 20 +++++++++++++++++++
.../GlobalPayments.Api.4.6.csproj | 2 +-
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 dir.txt
diff --git a/dir.txt b/dir.txt
new file mode 100644
index 00000000..604a7b7f
--- /dev/null
+++ b/dir.txt
@@ -0,0 +1,20 @@
+ Volume in drive D is D Drive
+ Volume Serial Number is C6B3-DE8E
+
+ Directory of D:\Source\Repositories\HSS\BlueBear\BlueBridge.CardPresent\dotnet-sdk
+
+11/10/2022 01:53 PM .
+11/10/2022 01:53 PM ..
+11/10/2022 11:01 AM 2,289 .gitignore
+11/10/2022 11:01 AM 438 .travis.yml
+11/10/2022 11:01 AM 12,930 CHANGELOG.md
+11/10/2022 01:54 PM 0 dir.txt
+11/10/2022 11:01 AM 109,984 Doxyfile
+11/10/2022 11:01 AM examples
+11/10/2022 11:01 AM 2,166 GlobalPayments.Api.sln
+11/10/2022 11:01 AM 15,439 LICENSE.md
+11/10/2022 11:01 AM 5,601 README.md
+11/10/2022 11:01 AM src
+11/10/2022 11:01 AM tests
+ 8 File(s) 148,847 bytes
+ 5 Dir(s) 122,987,163,648 bytes free
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index 83797323..bf273e5c 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -639,4 +639,4 @@
-
\ No newline at end of file
+
From 1e44d0e9a7d14f7efa3d1d751c55e62b4a2a8b5e Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Thu, 10 Nov 2022 14:40:05 -0700
Subject: [PATCH 03/19] Updated path to pfx file
---
src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index bf273e5c..4fecb0a7 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -34,7 +34,7 @@
true
- BlueBridge.pfx
+ ..\..\..\Certificates\BlueBridge.pfx
@@ -636,7 +636,9 @@
-
+
+ BlueBridge.pfx
+
-
+
\ No newline at end of file
From 5fef4498c32e86d1674bbf80fc434f127a0a6c6a Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Thu, 10 Nov 2022 14:49:28 -0700
Subject: [PATCH 04/19] Changed certificate
---
src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index 4fecb0a7..dc3e52d7 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -34,7 +34,7 @@
true
- ..\..\..\Certificates\BlueBridge.pfx
+ ..\..\..\Certificates\BlueBridge.snk
@@ -636,8 +636,8 @@
-
- BlueBridge.pfx
+
+ BlueBridge.snk
From 34019e811de28af240fcc1ae504cfafb5994876f Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Thu, 12 Jan 2023 14:52:01 -0700
Subject: [PATCH 05/19] Get AmountDue property
---
.../Terminals/UPA/Responses/TransactionResponse.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
index d478a272..ce5dcd73 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
@@ -67,7 +67,7 @@ protected void HydrateHostData(JsonDoc data) {
// TokenPANLast = host.GetValue("tokenPANLast");
// PartialApproval = host.GetValue("partialApproval");
// TraceNumber = host.GetValue("traceNumber");
- // BalanceDue = host.GetValue("balanceDue");
+ AmountDue = BalanceAmount = host.GetValue("balanceDue");
// BaseDue = host.GetValue("baseDue");
// TaxDue = host.GetValue("taxDue");
// TipDue = host.GetValue("tipDue");
From 9d26be15444d5e0deb7ed88082522a8e168139b8 Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Tue, 17 Jan 2023 12:30:57 -0700
Subject: [PATCH 06/19] Added RequestId to the response model
---
.../Terminals/Abstractions/IDeviceResponse.cs | 1 +
.../Terminals/PAX/Responses/DeviceResponse.cs | 3 +++
.../Terminals/UPA/Responses/TransactionResponse.cs | 2 +-
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs
index dee676d3..a5d0fda4 100644
--- a/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/Abstractions/IDeviceResponse.cs
@@ -49,6 +49,7 @@ public interface ITerminalResponse : IDeviceResponse {
string CardHolderVerificationMethod { get; set; }
string TerminalVerificationResults { get; set; }
decimal? MerchantFee { get; set; }
+ string RequestId { get; }
}
public interface ITerminalReport : IDeviceResponse { }
diff --git a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs
index 8d3da3ff..2f012f09 100644
--- a/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/PAX/Responses/DeviceResponse.cs
@@ -246,6 +246,9 @@ public class PaxTerminalResponse : PaxBaseResponse, ITerminalResponse {
public string TerminalVerificationResults { get; set; }
public decimal? MerchantFee { get; set; }
+
+ public string RequestId { get; }
+
public int TranNo { get; set; }
internal PaxTerminalResponse(byte[] buffer, params string[] messageIds) : base(buffer, messageIds) { }
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
index ce5dcd73..8bfaa888 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
@@ -4,7 +4,7 @@
namespace GlobalPayments.Api.Terminals.UPA
{
- internal class TransactionResponse: ITerminalResponse {
+ internal class TransactionResponse : ITerminalResponse {
public TransactionResponse(JsonDoc root) {
var response = root.Get("data");
if (response == null) {
From 5a5555444e0298230eea22a9da44a9422fcc7716 Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Tue, 30 May 2023 09:42:06 -0700
Subject: [PATCH 07/19] Updated project file
---
.../GlobalPayments.Api.4.6.csproj | 60 ++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index dc3e52d7..f689293f 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -49,17 +49,19 @@
-
+
+
+
@@ -78,13 +80,16 @@
+
+
+
@@ -102,15 +107,19 @@
+
+
+
+
@@ -128,6 +137,9 @@
+
+
+
@@ -135,10 +147,13 @@
+
+
+
@@ -147,12 +162,14 @@
+
+
@@ -167,21 +184,28 @@
+
+
+
+
+
+
+
@@ -194,6 +218,7 @@
+
@@ -208,8 +233,11 @@
+
+
+
@@ -220,6 +248,7 @@
+
@@ -228,10 +257,13 @@
+
+
+
@@ -243,8 +275,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -307,6 +357,7 @@
+
@@ -324,11 +375,13 @@
+
+
@@ -436,8 +489,10 @@
+
+
@@ -458,6 +513,7 @@
+
@@ -481,6 +537,7 @@
+
@@ -497,6 +554,7 @@
+
From 4e0e85571125f9ffeecec9187a51b700d792dd26 Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Tue, 30 May 2023 12:01:39 -0700
Subject: [PATCH 08/19] Added log4net and mapped missing transaction properties
---
.../GlobalPayments.Api.4.6.csproj | 3 +++
.../UPA/Responses/BatchReportResponse.cs | 3 +++
.../Terminals/UPA/UpaController.cs | 11 ++++++++++
.../Terminals/UPA/UpaInterface.cs | 20 +++++++++++++++++++
4 files changed, 37 insertions(+)
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index f689293f..a25f33bc 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -689,6 +689,9 @@
+
+ 2.0.14
+
13.0.1
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
index 1a3e62d7..4d1cc7a4 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
@@ -19,6 +19,9 @@ public BatchReportResponse(JsonDoc root) {
throw new MessageException(INVALID_RESPONSE_FORMAT);
}
+ EcrId = firstDataNode.GetValue("EcrId");
+ RequestId = firstDataNode.GetValue("requestId");
+
Status = cmdResult.GetValue("result");
if (string.IsNullOrEmpty(Status)) {
var errorCode = cmdResult.GetValue("errorCode");
diff --git a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
index cf8336c1..c7eb7d07 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
@@ -6,11 +6,14 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
+using log4net;
namespace GlobalPayments.Api.Terminals.UPA
{
public class UpaController : DeviceController
{
+ private readonly ILog _logger = LogManager.GetLogger(typeof(UpaController));
+
internal override IDeviceInterface ConfigureInterface()
{
if (_interface == null)
@@ -53,6 +56,10 @@ internal override ITerminalReport ProcessReport(TerminalReportBuilder builder)
var response = Send(BuildReportTransaction(builder));
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
var jsonParse = JsonDoc.Parse(jsonObject);
switch (builder.ReportType)
@@ -266,6 +273,10 @@ internal TransactionResponse DoTransaction(IDeviceMessage request) {
}
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
var jsonParse = JsonDoc.Parse(jsonObject);
return new TransactionResponse(jsonParse);
diff --git a/src/GlobalPayments.Api/Terminals/UPA/UpaInterface.cs b/src/GlobalPayments.Api/Terminals/UPA/UpaInterface.cs
index d9446c40..5776b22e 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/UpaInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/UpaInterface.cs
@@ -3,9 +3,13 @@
using GlobalPayments.Api.Terminals.Builders;
using GlobalPayments.Api.Utils;
using System.Text;
+using log4net;
namespace GlobalPayments.Api.Terminals.UPA {
public class UpaInterface : DeviceInterface, IDeviceInterface {
+
+ private readonly ILog _logger = LogManager.GetLogger(typeof(UpaInterface));
+
internal UpaInterface(UpaController controller) : base(controller) { }
public override TerminalAuthBuilder TipAdjust(decimal? amount) {
@@ -31,6 +35,10 @@ public override IEODResponse EndOfDay() {
var requestId = _controller.GetRequestId();
var response = _controller.Send(TerminalUtilities.BuildUpaAdminRequest(requestId, EcrId, UpaTransType.EodProcessing));
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
var jsonParse = JsonDoc.Parse(jsonObject);
return new UpaEODResponse(jsonParse);
}
@@ -39,6 +47,10 @@ public override IDeviceResponse Reboot() {
var requestId = _controller.GetRequestId();
var response = _controller.Send(TerminalUtilities.BuildUpaAdminRequest(requestId, EcrId, UpaTransType.Reboot));
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
var jsonParse = JsonDoc.Parse(jsonObject);
return new TransactionResponse(jsonParse);
}
@@ -47,6 +59,10 @@ public override IDeviceResponse LineItem(string leftText, string rightText = nul
var requestId = _controller.GetRequestId();
var response = _controller.Send(TerminalUtilities.BuildUpaAdminRequest(requestId, EcrId, UpaTransType.LineItemDisplay, leftText, rightText));
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
var jsonParse = JsonDoc.Parse(jsonObject);
return new TransactionResponse(jsonParse);
}
@@ -61,6 +77,10 @@ public override ISAFResponse SendStoreAndForward()
var requestId = _controller.GetRequestId();
var response = _controller.Send(TerminalUtilities.BuildUpaAdminRequest(requestId, EcrId, UpaTransType.SendSAF));
string jsonObject = Encoding.UTF8.GetString(response);
+
+ if(_logger.IsDebugEnabled)
+ _logger.Debug($"Raw Response: {jsonObject}");
+
JsonDoc doc = JsonDoc.Parse(jsonObject);
return new UpaSAFResponse(doc);
}
From 4234c3ed84c933ade014e2bc98139f83b8aab36c Mon Sep 17 00:00:00 2001
From: Michael Rose
Date: Wed, 31 May 2023 12:21:14 -0700
Subject: [PATCH 09/19] Made EcrId a string consistently
---
.../Terminals/Builders/TerminalAuthBuilder.cs | 2 +-
src/GlobalPayments.Api/Terminals/Builders/TerminalBuilder.cs | 2 +-
.../Terminals/Builders/TerminalManageBuilder.cs | 2 +-
.../Terminals/UPA/Responses/BatchReportResponse.cs | 4 ++--
.../Terminals/UPA/Responses/TransactionResponse.cs | 4 +++-
.../Terminals/UPA/Responses/UpaEODResponse.cs | 5 +++++
src/GlobalPayments.Api/Terminals/UPA/UpaController.cs | 4 ++--
7 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs
index 30b80ac9..e5dac7ca 100644
--- a/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs
+++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalAuthBuilder.cs
@@ -66,7 +66,7 @@ public TerminalAuthBuilder WithAddress(Address address) {
return this;
}
- public TerminalAuthBuilder WithEcrId(int ecrId) {
+ public TerminalAuthBuilder WithEcrId(string ecrId) {
EcrId = ecrId;
return this;
}
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalBuilder.cs
index 499cdce8..01825fb8 100644
--- a/src/GlobalPayments.Api/Terminals/Builders/TerminalBuilder.cs
+++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalBuilder.cs
@@ -6,7 +6,7 @@ namespace GlobalPayments.Api.Terminals.Builders {
public abstract class TerminalBuilder : TransactionBuilder where T : TerminalBuilder {
internal PaymentMethodType PaymentMethodType { get; set; }
internal int ReferenceNumber { get; set; }
- internal int EcrId { get; set; }
+ internal string EcrId { get; set; }
public T WithPaymentMethodType(PaymentMethodType value) {
PaymentMethodType = value;
diff --git a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs
index df937bf0..34d1683b 100644
--- a/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs
+++ b/src/GlobalPayments.Api/Terminals/Builders/TerminalManageBuilder.cs
@@ -27,7 +27,7 @@ public TerminalManageBuilder WithTerminalRefNumber(string terminalRefNumber) {
return this;
}
- public TerminalManageBuilder WithEcrId(int ecrId) {
+ public TerminalManageBuilder WithEcrId(string ecrId) {
EcrId = ecrId;
return this;
}
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
index 4d1cc7a4..a8fc1b51 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
@@ -19,7 +19,7 @@ public BatchReportResponse(JsonDoc root) {
throw new MessageException(INVALID_RESPONSE_FORMAT);
}
- EcrId = firstDataNode.GetValue("EcrId");
+ EcrId = firstDataNode.GetValue("EcrId");
RequestId = firstDataNode.GetValue("requestId");
Status = cmdResult.GetValue("result");
@@ -99,7 +99,7 @@ public BatchReportResponse(JsonDoc root) {
public string Message { get; set; }
public string Response { get; set; }
- public int EcrId { get; set; }
+ public string EcrId { get; set; }
public int RequestId { get; set; }
public string Result { get; set; }
public string ErrorCode { get; set; }
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
index 8bfaa888..432d3e87 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
@@ -4,7 +4,7 @@
namespace GlobalPayments.Api.Terminals.UPA
{
- internal class TransactionResponse : ITerminalResponse {
+ public class TransactionResponse : ITerminalResponse {
public TransactionResponse(JsonDoc root) {
var response = root.Get("data");
if (response == null) {
@@ -12,6 +12,7 @@ public TransactionResponse(JsonDoc root) {
}
RequestId = response.GetValue("requestId");
+ EcrId = response.GetValue("EcrId");
HydrateCmdResult(response);
var responseData = response.Get("data");
if (responseData == null) {
@@ -213,5 +214,6 @@ private string ConvertHEX(string hexString) {
public string ReferenceNumber { get; set; }
public string CardHolderName { get; set; }
public string RequestId { get; set; }
+ public string EcrId { get; set; }
}
}
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
index f7938569..33186310 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
@@ -19,6 +19,9 @@ public UpaEODResponse(JsonDoc root) {
Status = cmdResult.GetValue("result");
+ EcrId = firstDataNode.GetValue("EcrId");
+ RequestId = firstDataNode.GetValue("requestId");
+
// Log error info if it's there
var errorCode = cmdResult.GetValue("errorCode");
var errorMsg = cmdResult.GetValue("errorMessage");
@@ -62,6 +65,8 @@ public UpaEODResponse(JsonDoc root) {
public IBatchReportResponse BatchReportResponse { get; set; }
public string RespDateTime { get; set; }
public int BatchId { get; set; }
+ public string EcrId { get; set; }
+ public int RequestId { get; set; }
public int GatewayResponseCode { get; set; }
public string GatewayResponseMessage { get; set; }
diff --git a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
index c7eb7d07..b261c029 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
@@ -111,7 +111,7 @@ internal IDeviceMessage BuildProcessTransaction(TerminalAuthBuilder builder)
var baseRequest = doc.SubElement("data");
baseRequest.Set("command", MapTransactionType(transType, transModifier, builder.RequestMultiUseToken, builder.Gratuity));
- baseRequest.Set("EcrId", builder.EcrId.ToString());
+ baseRequest.Set("EcrId", builder.EcrId);
baseRequest.Set("requestId", requestId.ToString());
if (transType != TransactionType.Balance) {
@@ -226,7 +226,7 @@ internal IDeviceMessage BuildManageTransaction(TerminalManageBuilder builder) {
var baseRequest = doc.SubElement("data");
// Possibly update the requestToken parameter in the future if necessary
baseRequest.Set("command", MapTransactionType(transType, transModifier, false, builder.Gratuity));
- baseRequest.Set("EcrId", builder.EcrId.ToString());
+ baseRequest.Set("EcrId", builder.EcrId);
baseRequest.Set("requestId", requestId.ToString());
var txnData = baseRequest.SubElement("data");
From 44f1a3c7c6fe53a7571454810feb848b0ad08de9 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Mon, 5 Jun 2023 15:31:46 -0700
Subject: [PATCH 10/19] Added missing properties
---
GlobalPayments.Api.sln.GhostDoc.xml | 60 +++++++++++++++++++
.../UPA/Responses/BatchReportResponse.cs | 10 ++--
.../UPA/Responses/TransactionResponse.cs | 33 ++++++++--
.../Terminals/UPA/Responses/UpaEODResponse.cs | 5 +-
4 files changed, 93 insertions(+), 15 deletions(-)
create mode 100644 GlobalPayments.Api.sln.GhostDoc.xml
diff --git a/GlobalPayments.Api.sln.GhostDoc.xml b/GlobalPayments.Api.sln.GhostDoc.xml
new file mode 100644
index 00000000..badf7a93
--- /dev/null
+++ b/GlobalPayments.Api.sln.GhostDoc.xml
@@ -0,0 +1,60 @@
+
+
+ *.min.js
+ jquery*.js
+
+
+
+
+
+
+
+
+
+
+
+ .\Help
+ true
+ BlueBridge
+ MemberName
+
+ FlatGray
+
+ true
+ false
+ false
+ false
+ false
+
+
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+
+
+
+
+
+ true
+ false
+ false
+
+ true
+
+
+
+
+
+
+
+ BlueBridge.CardPresent
+
+
+
+
+
+
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
index a8fc1b51..6508179c 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
@@ -24,9 +24,8 @@ public BatchReportResponse(JsonDoc root) {
Status = cmdResult.GetValue("result");
if (string.IsNullOrEmpty(Status)) {
- var errorCode = cmdResult.GetValue("errorCode");
- var errorMsg = cmdResult.GetValue("errorMessage");
- DeviceResponseText = $"Error: {errorCode} - {errorMsg}";
+ DeviceResponseCode = cmdResult.GetValue("errorCode");
+ DeviceResponseText = cmdResult.GetValue("errorMessage");
}
else {
// If the Status is not "Success", there is either nothing to process, or something else went wrong.
@@ -83,9 +82,8 @@ public BatchReportResponse(JsonDoc root) {
}
}
else { // the only other option is "Failed"
- var errorCode = cmdResult.GetValue("errorCode");
- var errorMsg = cmdResult.GetValue("errorMessage");
- DeviceResponseText = $"Error: {errorCode} - {errorMsg}";
+ DeviceResponseCode = cmdResult.GetValue("errorCode");
+ DeviceResponseText = cmdResult.GetValue("errorMessage");
}
}
}
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
index 432d3e87..4be96e7c 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
@@ -13,6 +13,8 @@ public TransactionResponse(JsonDoc root) {
RequestId = response.GetValue("requestId");
EcrId = response.GetValue("EcrId");
+ TransactionType = response.GetValue("response");
+
HydrateCmdResult(response);
var responseData = response.Get("data");
if (responseData == null) {
@@ -41,16 +43,33 @@ protected void HydrateHostData(JsonDoc data) {
TransactionId = host.GetValue("responseId");
TerminalRefNumber = host.GetValue("tranNo");
// TransactionDate = host.GetValue("respDateTime");
- // GatewayResponseCode = host.GetValue("gatewayResponseCode");
- // GatewayResponsemessage = host.GetValue("gatewayResponsemessage");
+ GatewayResponseCode = host.GetValue("gatewayResponseCode");
+ GatewayResponseText = host.GetValue("gatewayResponseMessage");
ResponseCode = NormalizeResponseCode(host.GetValue("responseCode"), host.GetValue("partialApproval"));
ResponseText = host.GetValue("responseText");
ApprovalCode = host.GetValue("approvalCode");
ReferenceNumber = host.GetValue("referenceNumber");
- AvsResponseCode = host.GetValue("avsResultCode");
- CvvResponseCode = host.GetValue("cvvResultCode");
- AvsResponseText = host.GetValue("avsResultText");
- CvvResponseText = host.GetValue("cvvResultText");
+
+ AvsResponseCode = host.GetValue("AvsResultCode");
+
+ if (string.IsNullOrEmpty(AvsResponseCode))
+ AvsResponseCode = "0";
+
+ CvvResponseCode = host.GetValue("CvvResultCode");
+
+ if (string.IsNullOrEmpty(CvvResponseCode))
+ CvvResponseCode = "0";
+
+ AvsResponseText = host.GetValue("AvsResultText");
+
+ if (string.IsNullOrEmpty(AvsResponseText))
+ AvsResponseText = "AVS Not Requested.";
+
+ CvvResponseText = host.GetValue("CvvResultText");
+
+ if (string.IsNullOrEmpty(CvvResponseText))
+ CvvResponseText = "CVV Not Requested.";
+
// AdditionalTipAmount = host.GetValue("additionalTipAmount");
// BaseAmount = host.GetValue("baseAmount");
TipAmount = host.GetValue("tipAmount");
@@ -215,5 +234,7 @@ private string ConvertHEX(string hexString) {
public string CardHolderName { get; set; }
public string RequestId { get; set; }
public string EcrId { get; set; }
+ public string GatewayResponseCode { get; set; }
+ public string GatewayResponseText { get; set; }
}
}
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
index 33186310..43468276 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/UpaEODResponse.cs
@@ -23,9 +23,8 @@ public UpaEODResponse(JsonDoc root) {
RequestId = firstDataNode.GetValue("requestId");
// Log error info if it's there
- var errorCode = cmdResult.GetValue("errorCode");
- var errorMsg = cmdResult.GetValue("errorMessage");
- DeviceResponseText = $"Error: {errorCode} - {errorMsg}";
+ DeviceResponseCode = cmdResult.GetValue("errorCode");
+ DeviceResponseText = cmdResult.GetValue("errorMessage");
// Unlike in other response types, this data should always be here, even if the Status is "Failed"
var secondDataNode = firstDataNode.Get("data");
From b4c4a8391b96ec0e8e06a475604c8155620c3f74 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Tue, 6 Jun 2023 11:57:48 -0700
Subject: [PATCH 11/19] Better error handling
---
src/GlobalPayments.Api/Entities/Exceptions.cs | 16 +++++++++
.../UPA/Interfaces/UpaTcpInterface.cs | 35 ++++++++++++++-----
.../UPA/Responses/BatchReportResponse.cs | 3 +-
3 files changed, 45 insertions(+), 9 deletions(-)
diff --git a/src/GlobalPayments.Api/Entities/Exceptions.cs b/src/GlobalPayments.Api/Entities/Exceptions.cs
index c2662cf4..b14dba53 100644
--- a/src/GlobalPayments.Api/Entities/Exceptions.cs
+++ b/src/GlobalPayments.Api/Entities/Exceptions.cs
@@ -12,6 +12,22 @@ public class ApiException : Exception {
public ApiException(string message = null, Exception innerException = null) : base(message, innerException) { }
}
+ public class DeviceException : Exception
+ {
+ /// The exception message
+ /// The device state
+ public DeviceException(string message, string state) : base(message)
+ {
+ State = state;
+ }
+
+ ///
+ /// Gets the state.
+ ///
+ /// The state.
+ public string State { get; }
+ }
+
///
/// A builder error occurred. Check the method calls against the builder.
///
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
index 36bbe7ab..15902b1c 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
@@ -4,6 +4,8 @@
using GlobalPayments.Api.Utils;
using System;
using System.Net.Sockets;
+using log4net;
+using Newtonsoft.Json;
namespace GlobalPayments.Api.Terminals.UPA {
internal class UpaTcpInterface : IDeviceCommInterface {
@@ -11,6 +13,9 @@ internal class UpaTcpInterface : IDeviceCommInterface {
NetworkStream _stream;
ITerminalConfiguration _settings;
int _connectionCount = 0;
+
+ private readonly ILog _logger = LogManager.GetLogger(typeof(UpaTcpInterface));
+
public event MessageSentEventHandler OnMessageSent;
public UpaTcpInterface(ITerminalConfiguration settings) {
@@ -47,22 +52,26 @@ public void Disconnect() {
public byte[] Send(IDeviceMessage message) {
byte[] buffer = message.GetSendBuffer();
-
+ var msgValue = string.Empty;
var readyReceived = false;
byte[] responseMessage = null;
Connect();
- try {
+ try
+ {
var task = _stream.WriteAsync(buffer, 0, buffer.Length);
- if (!task.Wait(_settings.Timeout)) {
+ if (!task.Wait(_settings.Timeout))
+ {
throw new MessageException("Terminal did not respond in the given timeout.");
}
- do {
+ do
+ {
var rvalue = _stream.GetTerminalResponseAsync();
- if (rvalue != null) {
- var msgValue = GetResponseMessageType(rvalue);
+ if (rvalue != null)
+ {
+ msgValue = GetResponseMessageType(rvalue);
switch (msgValue)
{
@@ -74,14 +83,18 @@ public byte[] Send(IDeviceMessage message) {
readyReceived = true;
break;
case UpaMessageType.Busy:
+ Disconnect();
+ Connect();
break;
case UpaMessageType.TimeOut:
break;
case UpaMessageType.Msg:
responseMessage = TrimResponse(rvalue);
- if (IsNonReadyResponse(responseMessage)) {
+ if (IsNonReadyResponse(responseMessage))
+ {
readyReceived = true; // since reboot doesn't return READY
}
+
SendAckMessageToDevice();
break;
default:
@@ -96,10 +109,16 @@ public byte[] Send(IDeviceMessage message) {
}
} while (!readyReceived);
+ if (responseMessage == null)
+ {
+ throw new DeviceException("The device did not return the expected response.", msgValue);
+ }
+
return responseMessage;
}
catch (Exception exc) {
- throw new MessageException(exc.Message, exc);
+ _logger.Error(exc);
+ throw;
}
finally {
OnMessageSent?.Invoke(message.ToString());
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
index 6508179c..0d15be9d 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
@@ -5,7 +5,8 @@
namespace GlobalPayments.Api.Terminals.UPA
{
- public class BatchReportResponse : ITerminalReport {
+ public class BatchReportResponse : ITerminalReport
+ {
const string INVALID_RESPONSE_FORMAT = "The response received is not in the proper format.";
public BatchReportResponse(JsonDoc root) {
From 0536dc84cf483b8847d623549c0d6c05ae589705 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Tue, 6 Jun 2023 14:58:08 -0700
Subject: [PATCH 12/19] Added PartialApproval flag
---
.../Terminals/UPA/Responses/TransactionResponse.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
index 4be96e7c..aefa55bb 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/TransactionResponse.cs
@@ -85,7 +85,11 @@ protected void HydrateHostData(JsonDoc data) {
// RecurringDataCode = host.GetValue("recurringDataCode");
// CavvResultCode = host.GetValue("cavvResultCode");
// TokenPANLast = host.GetValue("tokenPANLast");
- // PartialApproval = host.GetValue("partialApproval");
+
+ var partialAmount = host.GetValue("partialApproval");
+
+ PartialApproval = !string.IsNullOrEmpty(partialAmount) && !"0".Equals(partialAmount);
+
// TraceNumber = host.GetValue("traceNumber");
AmountDue = BalanceAmount = host.GetValue("balanceDue");
// BaseDue = host.GetValue("baseDue");
@@ -236,5 +240,6 @@ private string ConvertHEX(string hexString) {
public string EcrId { get; set; }
public string GatewayResponseCode { get; set; }
public string GatewayResponseText { get; set; }
+ public bool PartialApproval { get; set; }
}
}
From e739d2f4ef563547a46549dbde51e951f2a930bb Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Wed, 7 Jun 2023 18:55:02 -0700
Subject: [PATCH 13/19] Fixed spelling error on creditCnt
---
.../Terminals/UPA/Responses/BatchReportResponse.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
index 0d15be9d..3abb7855 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Responses/BatchReportResponse.cs
@@ -52,7 +52,7 @@ public BatchReportResponse(JsonDoc root) {
OpenTnxId = batchRecord.GetValue("openTnxId"),
TotalAmount = batchRecord.GetValue("totalAmount"),
TotalCnt = batchRecord.GetValue("totalCnt"),
- CreditCnt = batchRecord.GetValue("credictCnt"),
+ CreditCnt = batchRecord.GetValue("creditCnt"),
CreditAmt = batchRecord.GetValue("creditAmt"),
DebitCnt = batchRecord.GetValue("debitCnt"),
DebitAmt = batchRecord.GetValue("debitAmt"),
From 9cd2efd7052f789a5123dc5a21961f55e187ad95 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Tue, 29 Aug 2023 06:39:52 -0700
Subject: [PATCH 14/19] Added exception handling for bad json response handling
---
.../Terminals/UPA/Interfaces/UpaTcpInterface.cs | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
index 15902b1c..44b77eba 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
@@ -134,10 +134,19 @@ private byte[] TrimResponse(byte[] value) {
);
}
- private string GetResponseMessageType(byte[] response) {
+ private string GetResponseMessageType(byte[] response)
+ {
var jsonObject = System.Text.Encoding.UTF8.GetString(TrimResponse(response));
- var jsonDoc = JsonDoc.Parse(jsonObject);
- return jsonDoc.GetValue("message");
+ try
+ {
+ var jsonDoc = JsonDoc.Parse(jsonObject);
+ return jsonDoc.GetValue("message");
+ }
+ catch (Exception ex)
+ {
+ _logger.Error($"{ex.Message} : Response - {jsonObject}");
+ throw;
+ }
}
private void SendAckMessageToDevice() {
From 0ee55e76a4d82738f71eecbf3a7ed97cdf74fea1 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Thu, 14 Dec 2023 09:20:52 -0700
Subject: [PATCH 15/19] Added new UpaTcpInterface
---
.../GlobalPayments.Api.4.6.csproj | 3 +
.../UPA/Interfaces/MessageEventArgs.cs | 17 +
.../Terminals/UPA/Interfaces/StreamBuffer.cs | 386 ++++++++++++++++++
.../UPA/Interfaces/TcpClientAsync.cs | 240 +++++++++++
.../UPA/Interfaces/UpaTcpInterface.cs | 192 +++++----
.../Terminals/UPA/UpaController.cs | 2 +-
src/GlobalPayments.Api/Utils/Extensions.cs | 98 +++++
7 files changed, 855 insertions(+), 83 deletions(-)
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Interfaces/MessageEventArgs.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Interfaces/StreamBuffer.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Interfaces/TcpClientAsync.cs
diff --git a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
index 73409536..120695d9 100644
--- a/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
+++ b/src/GlobalPayments.Api/GlobalPayments.Api.4.6.csproj
@@ -655,6 +655,9 @@
+
+
+
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/MessageEventArgs.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/MessageEventArgs.cs
new file mode 100644
index 00000000..781e37dc
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/MessageEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace GlobalPayments.Api.Terminals.UPA
+{
+ public class MessageEventArgs : EventArgs
+ {
+ public MessageEventArgs(string message, Exception exception = null)
+ {
+ Message = message;
+ Exception = exception;
+ }
+
+ public string Message { get; }
+
+ public Exception Exception { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/StreamBuffer.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/StreamBuffer.cs
new file mode 100644
index 00000000..88d0c054
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/StreamBuffer.cs
@@ -0,0 +1,386 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GlobalPayments.Api.Terminals.UPA
+{
+ public class StreamBuffer
+ {
+ #region Private data
+
+ private const int DefaultCapacity = 1024;
+
+ private readonly object _syncObj = new object();
+
+ private byte[] _buffer = new byte[DefaultCapacity];
+
+ private int _head;
+
+ private int _tail = -1;
+
+ private bool _isEmpty = true;
+
+ private TaskCompletionSource _dequeueManualTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ private TaskCompletionSource _availableTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ #endregion Private data
+
+ #region Constructors
+
+ public StreamBuffer()
+ {
+ }
+
+ public StreamBuffer(byte[] bytes)
+ {
+ Enqueue(bytes);
+ }
+
+ public StreamBuffer(int capacity)
+ {
+ AutoTrimMinCapacity = capacity;
+ SetCapacity(capacity);
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ public int Count
+ {
+ get
+ {
+ lock (_syncObj)
+ {
+ if (_isEmpty)
+ {
+ return 0;
+ }
+ if (_tail >= _head)
+ {
+ return _tail - _head + 1;
+ }
+ return Capacity - _head + _tail + 1;
+ }
+ }
+ }
+
+ public byte[] Buffer
+ {
+ get
+ {
+ lock (_syncObj)
+ {
+ var bytes = new byte[Count];
+ if (!_isEmpty)
+ {
+ if (_tail >= _head)
+ {
+ Array.Copy(_buffer, _head, bytes, 0, _tail - _head + 1);
+ }
+ else
+ {
+ Array.Copy(_buffer, _head, bytes, 0, Capacity - _head);
+ Array.Copy(_buffer, 0, bytes, Capacity - _head, _tail + 1);
+ }
+ }
+ return bytes;
+ }
+ }
+ }
+
+ public int Capacity => _buffer.Length;
+
+ public bool AutoTrim { get; set; } = true;
+
+ public int AutoTrimMinCapacity { get; set; } = DefaultCapacity;
+
+ #endregion Properties
+
+ #region Public methods
+
+ public void Clear()
+ {
+ lock (_syncObj)
+ {
+ _head = 0;
+ _tail = -1;
+ _isEmpty = true;
+ Reset(ref _availableTcs);
+ }
+ }
+
+ public void SetCapacity(int capacity)
+ {
+ if (capacity < 0)
+ throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity must not be negative.");
+
+ lock (_syncObj)
+ {
+ var count = Count;
+ if (capacity < count)
+ throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity is too small to hold the current buffer content.");
+
+ if (capacity == _buffer.Length)
+ return;
+
+ var newBuffer = new byte[capacity];
+ Array.Copy(Buffer, newBuffer, count);
+ _buffer = newBuffer;
+ _head = 0;
+ _tail = count - 1;
+ }
+ }
+
+ public void TrimExcess()
+ {
+ lock (_syncObj)
+ {
+ if (Count < Capacity * 0.9)
+ {
+ SetCapacity(Count);
+ }
+ }
+ }
+
+ public void Enqueue(byte[] bytes)
+ {
+ if (bytes == null)
+ throw new ArgumentNullException(nameof(bytes));
+
+ Enqueue(bytes, 0, bytes.Length);
+ }
+
+ public void Enqueue(ArraySegment segment)
+ {
+ Enqueue(segment.Array, segment.Offset, segment.Count);
+ }
+
+ public void Enqueue(byte[] bytes, int offset, int count)
+ {
+ if (bytes == null)
+ throw new ArgumentNullException(nameof(bytes));
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ if (offset + count > bytes.Length)
+ throw new ArgumentOutOfRangeException(nameof(count));
+
+ if (count == 0)
+ return; // Nothing to do
+
+ lock (_syncObj)
+ {
+ if (Count + count > Capacity)
+ {
+ SetCapacity(Math.Max(Capacity * 2, Count + count));
+ }
+
+ int tailCount;
+ int wrapCount;
+ if (_tail >= _head || _isEmpty)
+ {
+ tailCount = Math.Min(Capacity - 1 - _tail, count);
+ wrapCount = count - tailCount;
+ }
+ else
+ {
+ tailCount = Math.Min(_head - 1 - _tail, count);
+ wrapCount = 0;
+ }
+
+ if (tailCount > 0)
+ {
+ Array.Copy(bytes, offset, _buffer, _tail + 1, tailCount);
+ }
+ if (wrapCount > 0)
+ {
+ Array.Copy(bytes, offset + tailCount, _buffer, 0, wrapCount);
+ }
+ _tail = (_tail + count) % Capacity;
+ _isEmpty = false;
+ Set(_dequeueManualTcs);
+ Set(_availableTcs);
+ }
+ }
+
+ public byte[] Dequeue(int maxCount)
+ {
+ return DequeueInternal(maxCount, peek: false);
+ }
+
+ public int Dequeue(byte[] buffer, int offset, int maxCount)
+ {
+ return DequeueInternal(buffer, offset, maxCount, peek: false);
+ }
+
+ public byte[] Peek(int maxCount)
+ {
+ return DequeueInternal(maxCount, peek: true);
+ }
+
+ public async Task DequeueAsync(int count, CancellationToken cancellationToken = default)
+ {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), "The count must not be negative.");
+
+ while (true)
+ {
+ TaskCompletionSource myDequeueManualTcs;
+ lock (_syncObj)
+ {
+ if (count <= Count)
+ {
+ return Dequeue(count);
+ }
+ myDequeueManualTcs = Reset(ref _dequeueManualTcs);
+ }
+ await AwaitAsync(myDequeueManualTcs, cancellationToken);
+ }
+ }
+
+ public async Task DequeueAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
+ {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), "The count must not be negative.");
+ if (buffer.Length < offset + count)
+ throw new ArgumentException("The buffer is too small for the requested data.", nameof(buffer));
+
+ while (true)
+ {
+ TaskCompletionSource myDequeueManualTcs;
+ lock (_syncObj)
+ {
+ if (count <= Count)
+ {
+ Dequeue(buffer, offset, count);
+ }
+ myDequeueManualTcs = Reset(ref _dequeueManualTcs);
+ }
+ await AwaitAsync(myDequeueManualTcs, cancellationToken);
+ }
+ }
+
+ public async Task WaitAsync(CancellationToken cancellationToken = default)
+ {
+ TaskCompletionSource myAvailableTcs;
+ lock (_syncObj)
+ {
+ if (Count > 0)
+ {
+ return;
+ }
+ myAvailableTcs = Reset(ref _availableTcs);
+ }
+ await AwaitAsync(myAvailableTcs, cancellationToken);
+ }
+
+ #endregion Public methods
+
+ #region Private methods
+
+ private byte[] DequeueInternal(int count, bool peek)
+ {
+ if (count > Count)
+ count = Count;
+ var bytes = new byte[count];
+ DequeueInternal(bytes, 0, count, peek);
+ return bytes;
+ }
+
+ private int DequeueInternal(byte[] bytes, int offset, int count, bool peek)
+ {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), "The count must not be negative.");
+ if (count == 0)
+ return count; // Easy
+ if (bytes.Length < offset + count)
+ throw new ArgumentException("The buffer is too small for the requested data.", nameof(bytes));
+
+ lock (_syncObj)
+ {
+ if (count > Count)
+ count = Count;
+
+ if (_tail >= _head)
+ {
+ Array.Copy(_buffer, _head, bytes, offset, count);
+ }
+ else
+ {
+ if (count <= Capacity - _head)
+ {
+ Array.Copy(_buffer, _head, bytes, offset, count);
+ }
+ else
+ {
+ var headCount = Capacity - _head;
+ Array.Copy(_buffer, _head, bytes, offset, headCount);
+ var wrapCount = count - headCount;
+ Array.Copy(_buffer, 0, bytes, offset + headCount, wrapCount);
+ }
+ }
+
+ if (peek)
+ return count;
+
+ if (count == Count)
+ {
+ _isEmpty = true;
+ _head = 0;
+ _tail = -1;
+ Reset(ref _availableTcs);
+ }
+ else
+ {
+ _head = (_head + count) % Capacity;
+ }
+
+ if (!AutoTrim || Capacity <= AutoTrimMinCapacity || Count > Capacity / 2)
+ return count;
+
+ var newCapacity = Count;
+ if (newCapacity < AutoTrimMinCapacity)
+ {
+ newCapacity = AutoTrimMinCapacity;
+ }
+ if (newCapacity < Capacity)
+ {
+ SetCapacity(newCapacity);
+ }
+ return count;
+ }
+ }
+
+ // Must be called within the lock
+ private static TaskCompletionSource Reset(ref TaskCompletionSource tcs)
+ {
+ if (tcs.Task.IsCompleted)
+ {
+ tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ }
+ return tcs;
+ }
+
+ // Must be called within the lock
+ private static void Set(TaskCompletionSource tcs)
+ {
+ tcs.TrySetResult(true);
+ }
+
+ // Must NOT be called within the lock
+ private static async Task AwaitAsync(TaskCompletionSource tcs, CancellationToken cancellationToken)
+ {
+ if (await Task.WhenAny(tcs.Task, Task.Delay(-1, cancellationToken)) == tcs.Task)
+ {
+ await tcs.Task; // Already completed
+ return;
+ }
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+
+ #endregion Private methods
+ }
+}
\ No newline at end of file
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/TcpClientAsync.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/TcpClientAsync.cs
new file mode 100644
index 00000000..ecf95b4d
--- /dev/null
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/TcpClientAsync.cs
@@ -0,0 +1,240 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GlobalPayments.Api.Terminals.UPA
+{
+ public class TcpClientAsync : IDisposable
+ {
+ #region Private data
+
+ private TcpClient _tcpClient;
+ private NetworkStream _stream;
+ private TaskCompletionSource _closedTcs = new TaskCompletionSource();
+
+ #endregion Private data
+
+ #region Constructors
+
+ public TcpClientAsync()
+ {
+ _closedTcs.SetResult(true);
+ }
+
+ #endregion Constructors
+
+ #region Events
+
+ public event EventHandler Message;
+
+ #endregion Events
+
+ #region Properties
+
+ public TimeSpan ConnectTimeout { get; set; } = TimeSpan.FromSeconds(5);
+
+ public TimeSpan MaxConnectTimeout { get; set; } = TimeSpan.FromMinutes(1);
+
+ public bool AutoReconnect { get; set; }
+
+ public string HostName { get; set; }
+
+ public IPAddress IpAddress { get; set; }
+
+ public int Port { get; set; }
+
+ public bool IsConnected => _tcpClient.Client.Connected;
+
+ public StreamBuffer StreamBuffer { get; private set; } = new StreamBuffer();
+
+ public Task ClosedTask => _closedTcs.Task;
+
+ public bool IsClosing => ClosedTask.IsCompleted;
+
+ public Func ConnectedCallback { get; set; }
+
+ public Action ClosedCallback { get; set; }
+
+ public Func ReceivedCallback { get; set; }
+
+ #endregion Properties
+
+ #region Public methods
+
+ public async Task RunAsync()
+ {
+ var isReconnected = false;
+ var reconnectTry = -1;
+ do
+ {
+ reconnectTry++;
+ StreamBuffer = new StreamBuffer();
+
+ // Try to connect to remote host
+ var connectTimeout = TimeSpan.FromTicks(ConnectTimeout.Ticks +
+ (MaxConnectTimeout.Ticks - ConnectTimeout.Ticks) / 20 *
+ Math.Min(reconnectTry, 20));
+ _tcpClient = new TcpClient(AddressFamily.InterNetworkV6)
+ {
+ SendTimeout = 2000,
+ ReceiveTimeout = 2000,
+ LingerState = new LingerOption(true, 0)
+ };
+ _tcpClient.Client.DualMode = true;
+ Message?.Invoke(this, new MessageEventArgs("Connecting to server"));
+ var connectTask = !string.IsNullOrWhiteSpace(HostName) ? _tcpClient.ConnectAsync(HostName, Port) : _tcpClient.ConnectAsync(IpAddress, Port);
+
+ var timeoutTask = Task.Delay(connectTimeout);
+ if (await Task.WhenAny(connectTask, timeoutTask) == timeoutTask)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection timeout"));
+ continue;
+ }
+
+ try
+ {
+ await connectTask;
+ }
+ catch (Exception ex)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Error connecting to remote host", ex));
+ await timeoutTask;
+ continue;
+ }
+
+ reconnectTry = -1;
+ _stream = _tcpClient.GetStream();
+ _stream.ReadTimeout = 1000 * 3;
+
+ // Read until the connection is closed.
+ var networkReadTask = Task.Run(async () =>
+ {
+ var buffer = new byte[10240];
+ while (true)
+ {
+ int readLength;
+ try
+ {
+ var readTask = _stream.ReadAsync(buffer, 0, buffer.Length);
+
+ readLength = await readTask;
+ }
+ catch (IOException ex) when ((ex.InnerException as SocketException)?.ErrorCode == (int)SocketError.OperationAborted)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection closed locally", ex));
+ readLength = -1;
+ }
+ catch (IOException ex) when ((ex.InnerException as SocketException)?.ErrorCode == (int)SocketError.ConnectionAborted)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection aborted", ex));
+ readLength = -1;
+ }
+ catch (IOException ex) when ((ex.InnerException as SocketException)?.ErrorCode == (int)SocketError.ConnectionReset)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection reset remotely", ex));
+ readLength = -2;
+ }
+ catch (ObjectDisposedException)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection closed by client"));
+ readLength = -4;
+ }
+ catch (Exception ex)
+ {
+ Message?.Invoke(this, new MessageEventArgs(ex.Message, ex));
+ readLength = -2;
+ }
+
+ if (readLength <= 0)
+ {
+ if (readLength == 0)
+ {
+ Message?.Invoke(this, new MessageEventArgs("Connection closed remotely"));
+ }
+
+ _closedTcs.TrySetResult(true);
+ OnClosed(readLength != -1);
+ return;
+ }
+
+ var segment = new ArraySegment(buffer, 0, readLength);
+ StreamBuffer.Enqueue(segment);
+ await OnReceivedAsync(readLength);
+ }
+ });
+
+ _closedTcs = new TaskCompletionSource();
+ await OnConnectedAsync(isReconnected);
+
+ // Wait for closed connection
+ await networkReadTask;
+ _tcpClient.Close();
+
+ isReconnected = true;
+ } while (AutoReconnect);
+ }
+
+ public void Disconnect()
+ {
+ _tcpClient.Client.Shutdown(SocketShutdown.Send);
+ var read = 0;
+ try
+ {
+ var buffer = new byte[10240];
+ while ((read = _tcpClient.Client.Receive(buffer)) > 0)
+ {
+
+ }
+ }
+ catch
+ {
+ Message?.Invoke(this, new MessageEventArgs($"Connection Disconnected {read}"));
+ }
+
+ _tcpClient?.Client?.Close();
+ _stream.Close(0);
+ _stream.Dispose();
+ }
+
+ public void Dispose()
+ {
+ AutoReconnect = false;
+ _tcpClient?.Client?.Dispose();
+ _tcpClient?.Dispose();
+ _stream = null;
+ _tcpClient = null;
+ }
+
+ public async Task Send(ArraySegment data, CancellationToken cancellationToken = default)
+ {
+ if (!_tcpClient.Client.Connected)
+ throw new InvalidOperationException("Not connected.");
+
+ await _stream.WriteAsync(data.Array, data.Offset, data.Count, cancellationToken);
+ }
+
+ #endregion Public methods
+
+ #region Protected virtual methods
+
+ protected virtual Task OnConnectedAsync(bool isReconnected)
+ {
+ return ConnectedCallback != null ? ConnectedCallback(this, isReconnected) : Task.CompletedTask;
+ }
+
+ protected virtual void OnClosed(bool remote)
+ {
+ ClosedCallback?.Invoke(this, remote);
+ }
+
+ protected virtual Task OnReceivedAsync(int count)
+ {
+ return ReceivedCallback != null ? ReceivedCallback(this, count) : Task.CompletedTask;
+ }
+
+ #endregion Protected virtual methods
+ }
+}
\ No newline at end of file
diff --git a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
index 44b77eba..22ea5005 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/Interfaces/UpaTcpInterface.cs
@@ -1,77 +1,82 @@
-using GlobalPayments.Api.Entities;
-using GlobalPayments.Api.Terminals.Abstractions;
+using GlobalPayments.Api.Terminals.Abstractions;
using GlobalPayments.Api.Terminals.Messaging;
using GlobalPayments.Api.Utils;
using System;
-using System.Net.Sockets;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
using log4net;
-using Newtonsoft.Json;
-namespace GlobalPayments.Api.Terminals.UPA {
- internal class UpaTcpInterface : IDeviceCommInterface {
- TcpClient _client;
- NetworkStream _stream;
- ITerminalConfiguration _settings;
- int _connectionCount = 0;
+namespace GlobalPayments.Api.Terminals.UPA
+{
+ internal class UpaTcpInterface : IDeviceCommInterface
+ {
+
+ private readonly ITerminalConfiguration _settings;
private readonly ILog _logger = LogManager.GetLogger(typeof(UpaTcpInterface));
public event MessageSentEventHandler OnMessageSent;
- public UpaTcpInterface(ITerminalConfiguration settings) {
+ public UpaTcpInterface(ITerminalConfiguration settings)
+ {
_settings = settings;
}
- public void Connect() {
- int connectionTimestamp = Int32.Parse(DateTime.Now.ToString("mmssfff"));
+ public void Connect()
+ {
- if (_client == null) {
- _client = new TcpClient();
- _client.ConnectAsync(_settings.IpAddress, int.Parse(_settings.Port)).Wait(_settings.Timeout);
+ }
- if (Int32.Parse(DateTime.Now.ToString("mmssfff")) > connectionTimestamp + _settings.Timeout) {
- throw new MessageException("Connection not established within the specified timeout.");
- }
+ public void Disconnect()
+ {
- _stream = _client.GetStream();
- _stream.ReadTimeout = _settings.Timeout;
- }
- _connectionCount++;
}
- public void Disconnect() {
- _connectionCount--;
- if (_connectionCount == 0) {
- _stream?.Dispose();
- _stream = null;
+ public byte[] Send(IDeviceMessage deviceMessage)
+ {
+ var tokenSource = new CancellationTokenSource();
+ var token = tokenSource.Token;
- _client?.Dispose();
- _client = null;
- }
- }
+ var tcs = new TaskCompletionSource();
+
+ var serverIsBusy = false;
- public byte[] Send(IDeviceMessage message) {
- byte[] buffer = message.GetSendBuffer();
- var msgValue = string.Empty;
- var readyReceived = false;
byte[] responseMessage = null;
- Connect();
- try
- {
- var task = _stream.WriteAsync(buffer, 0, buffer.Length);
+ var buffer = deviceMessage.GetSendBuffer();
- if (!task.Wait(_settings.Timeout))
+ var readyReceived = false;
+
+ var client = new TcpClientAsync
+ {
+ IpAddress = IPAddress.Parse(_settings.IpAddress),
+ Port = int.Parse(_settings.Port),
+ AutoReconnect = false,
+ ConnectedCallback = async (c, isReconnected) =>
{
- throw new MessageException("Terminal did not respond in the given timeout.");
- }
+ await c.Send(new ArraySegment(buffer, 0, buffer.Length), token);
+
+ OnMessageSent?.Invoke(deviceMessage.ToString());
- do
+ while (true)
+ {
+ await c.StreamBuffer.WaitAsync(token);
+ if (c.IsClosing)
+ {
+ break;
+ }
+ }
+ },
+ ReceivedCallback = async (c, count) =>
{
- var rvalue = _stream.GetTerminalResponseAsync();
+ var bytes = await c.StreamBuffer.DequeueAsync(count, token);
+
+ var rvalue = bytes.GetTerminalResponseAsync();
if (rvalue != null)
{
- msgValue = GetResponseMessageType(rvalue);
+ var msgValue = GetResponseMessageType(rvalue);
switch (msgValue)
{
@@ -80,11 +85,17 @@ public byte[] Send(IDeviceMessage message) {
case UpaMessageType.Nak:
break;
case UpaMessageType.Ready:
+ if (serverIsBusy)
+ {
+ await c.Send(new ArraySegment(buffer, 0, buffer.Length), token);
+ OnMessageSent?.Invoke(deviceMessage.ToString());
+ serverIsBusy = false;
+ }
+
readyReceived = true;
break;
case UpaMessageType.Busy:
- Disconnect();
- Connect();
+ serverIsBusy = true;
break;
case UpaMessageType.TimeOut:
break;
@@ -95,48 +106,61 @@ public byte[] Send(IDeviceMessage message) {
readyReceived = true; // since reboot doesn't return READY
}
- SendAckMessageToDevice();
+ await SendAckMessageToDevice(c);
break;
default:
throw new Exception("Message field value is unknown in API response.");
}
}
- else
+
+ if (responseMessage != null)
{
- // Reset the connection before the next attempt
- Disconnect();
- Connect();
+ c.Disconnect();
}
- } while (!readyReceived);
+ },
+ ClosedCallback = (c, r) => { tcs.SetResult(true); }
+ };
- if (responseMessage == null)
- {
- throw new DeviceException("The device did not return the expected response.", msgValue);
- }
+ client.Message += ClientOnMessage();
- return responseMessage;
- }
- catch (Exception exc) {
- _logger.Error(exc);
- throw;
- }
- finally {
- OnMessageSent?.Invoke(message.ToString());
- Disconnect();
+ var t = Task.WhenAny(client.RunAsync(), tcs.Task);
+
+ t.Wait(token);
+
+ client.Message -= ClientOnMessage();
+
+ client.Dispose();
+
+ return responseMessage;
+
+ EventHandler ClientOnMessage()
+ {
+ return (s, a) =>
+ {
+ if (a.Exception == null)
+ {
+ _logger.Debug($"Client: {a.Message}");
+ }
+ else
+ {
+ _logger.Error($"Client: {a.Message}", a.Exception);
+ }
+ };
}
}
- private byte[] TrimResponse(byte[] value) {
- return System.Text.Encoding.UTF8.GetBytes(
- System.Text.Encoding.UTF8.GetString(value)
- .TrimStart((char)ControlCodes.STX, (char)ControlCodes.LF)
- .TrimEnd((char)ControlCodes.LF, (char)ControlCodes.ETX)
- );
+ private static byte[] TrimResponse(byte[] value)
+ {
+ var json = Encoding.UTF8.GetString(value)
+ .TrimStart((char)ControlCodes.STX, (char)ControlCodes.LF)
+ .TrimEnd((char)ControlCodes.LF, (char)ControlCodes.ETX);
+
+ return Encoding.UTF8.GetBytes(json);
}
private string GetResponseMessageType(byte[] response)
{
- var jsonObject = System.Text.Encoding.UTF8.GetString(TrimResponse(response));
+ var jsonObject = Encoding.UTF8.GetString(TrimResponse(response));
try
{
var jsonDoc = JsonDoc.Parse(jsonObject);
@@ -149,28 +173,32 @@ private string GetResponseMessageType(byte[] response)
}
}
- private void SendAckMessageToDevice() {
+ private static async Task SendAckMessageToDevice(TcpClientAsync c)
+ {
var doc = new JsonDoc();
doc.Set("message", UpaMessageType.Ack);
var message = TerminalUtilities.BuildUpaRequest(doc.ToString());
var ackBuffer = message.GetSendBuffer();
- _stream.Write(ackBuffer, 0, ackBuffer.Length);
+ await c.Send(new ArraySegment(ackBuffer, 0, ackBuffer.Length));
}
- private bool IsNonReadyResponse(byte[] responseMessage) {
- var responseMessageString = System.Text.Encoding.UTF8.GetString(responseMessage);
+ private static bool IsNonReadyResponse(byte[] responseMessage)
+ {
+ var responseMessageString = Encoding.UTF8.GetString(responseMessage);
var response = JsonDoc.Parse(responseMessageString);
var data = response.Get("data");
- if (data == null) {
+ if (data == null)
+ {
return false;
}
+
var cmdResult = data.Get("cmdResult");
return (
- data.GetValue("response") == UpaTransType.Reboot ||
- (cmdResult != null && cmdResult.GetValue("result") == "Failed")
- );
+ data.GetValue("response") == UpaTransType.Reboot ||
+ (cmdResult != null && cmdResult.GetValue("result") == "Failed")
+ );
}
}
}
diff --git a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
index 589b8fc4..e166a84b 100644
--- a/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
+++ b/src/GlobalPayments.Api/Terminals/UPA/UpaController.cs
@@ -264,7 +264,7 @@ internal TransactionResponse DoTransaction(IDeviceMessage request) {
return null;
}
- string jsonObject = Encoding.UTF8.GetString(response);
+ var jsonObject = Encoding.UTF8.GetString(response);
if(_logger.IsDebugEnabled)
_logger.Debug($"Raw Response: {jsonObject}");
diff --git a/src/GlobalPayments.Api/Utils/Extensions.cs b/src/GlobalPayments.Api/Utils/Extensions.cs
index 2abddfed..5979761b 100644
--- a/src/GlobalPayments.Api/Utils/Extensions.cs
+++ b/src/GlobalPayments.Api/Utils/Extensions.cs
@@ -239,5 +239,103 @@ public static string TrimEnd(this string str, string trimString) {
public static string ExtractDigits(this string str) {
return string.IsNullOrEmpty(str) ? str : new string(str.Where(char.IsDigit).ToArray());
}
+
+
+
+ public static byte[] GetTerminalResponse(this byte[] buffer)
+ {
+
+ var bytesReceived = buffer.Length;
+
+ if (bytesReceived <= 0)
+ return null;
+
+ var readBuffer = new byte[bytesReceived];
+ Array.Copy(buffer, readBuffer, bytesReceived);
+
+ var code = (ControlCodes)readBuffer[0];
+ switch (code)
+ {
+ case ControlCodes.NAK:
+ return null;
+ case ControlCodes.EOT:
+ throw new MessageException("Terminal returned EOT for the current message.");
+ case ControlCodes.ACK:
+ return buffer.GetTerminalResponse();
+ case ControlCodes.STX:
+ {
+ var queue = new Queue(readBuffer);
+
+ // break off only one message
+ var rec_buffer = new List();
+ do
+ {
+ rec_buffer.Add(queue.Dequeue());
+ if (rec_buffer[rec_buffer.Count - 1] == (byte)ControlCodes.ETX)
+ break;
+ }
+ while (queue.Count > 0);
+
+ // Should be the LRC
+ if (queue.Count > 0)
+ {
+ rec_buffer.Add(queue.Dequeue());
+ }
+ return rec_buffer.ToArray();
+ }
+ default:
+ throw new MessageException($"Unknown message received: {code}");
+ }
+ }
+
+ public static byte[] GetTerminalResponseAsync(this byte[] buffer)
+ {
+
+ var bytesReceived = buffer.Length;
+
+ if (bytesReceived <= 0)
+ return null;
+
+ var readBuffer = new byte[bytesReceived];
+ Array.Copy(buffer, readBuffer, bytesReceived);
+
+ var code = (ControlCodes)readBuffer[0];
+
+ switch (code)
+ {
+ case ControlCodes.NAK:
+ return null;
+ case ControlCodes.EOT:
+ throw new MessageException("Terminal returned EOT for the current message.");
+ case ControlCodes.ACK:
+ return readBuffer.GetTerminalResponse();
+ case ControlCodes.STX:
+ {
+ var queue = new Queue(readBuffer);
+
+ // break off only one message
+ var rec_buffer = new List();
+ do
+ {
+ rec_buffer.Add(queue.Dequeue());
+ if (rec_buffer[rec_buffer.Count - 1] == (byte)ControlCodes.ETX)
+ break;
+ }
+ while (queue.Count > 0);
+
+ // Should be the LRC
+ if (queue.Count > 0)
+ {
+ rec_buffer.Add(queue.Dequeue());
+ }
+ return rec_buffer.ToArray();
+ }
+ default:
+ throw new MessageException($"Unknown message received: {code}");
+ }
+
+ }
+
+
}
}
From 00f2aa317afeb280215773587cd35b42ce40d149 Mon Sep 17 00:00:00 2001
From: "Rose, Michael"
Date: Fri, 12 Jan 2024 07:44:28 -0700
Subject: [PATCH 16/19] Updated to global api master and integrated with
Payments 2.17.00.D and PA 2.17.17.00.A
---
.../Builders/AuthorizationBuilder.cs | 44 +-
.../Builders/FileProcessingBuilder.cs | 34 ++
.../Builders/ManagementBuilder.cs | 3 +-
.../Builders/PayFacBuilder.cs | 34 ++
.../GpApi/GpApiAuthorizationRequestBuilder.cs | 39 +-
.../GpApiFileProcessingRequestBuilder.cs | 44 ++
.../GpApi/GpApiPayFacRequestBuilder.cs | 47 ++
.../RequestBuilderValidations.cs | 56 +++
.../Entities/BankPaymentResponse.cs | 1 +
.../Entities/BlockedCardType.cs | 14 +
.../Entities/CommercialData.cs | 3 +-
.../Entities/DisputeDocument.cs | 6 +-
src/GlobalPayments.Api/Entities/Document.cs | 19 +
.../Entities/DocumentUploadData.cs | 35 +-
src/GlobalPayments.Api/Entities/Enums.cs | 24 +-
.../Entities/Enums/AcquisitionType.cs | 22 +
.../Entities/Enums/BlockCardType.cs | 19 +
.../Entities/Enums/CardTypeFilter.cs | 16 +
.../Entities/Enums/DocumentCategory.cs | 19 +
.../Enums/FileProcessingActionType.cs | 6 +
.../Entities/Enums/FileType.cs | 18 +
.../Entities/Enums/FraudFilterResult.cs | 4 +-
.../Entities/Enums/FundsStatus.cs | 11 +
.../Entities/Enums/MerchantCategory.cs | 16 +
.../Entities/Enums/Region.cs | 10 +
.../Entities/Enums/SafIndicator.cs | 9 +
.../Entities/Enums/SafMode.cs | 10 +
.../Entities/Enums/ServiceEndpoints.cs | 5 +
.../Entities/Enums/TransactionType.cs | 3 +-
src/GlobalPayments.Api/Entities/Exceptions.cs | 16 -
.../Entities/FileProcessor.cs | 15 +
.../Entities/FileUploaded.cs | 15 +
...countDetails.cs => FundsAccountDetails.cs} | 8 +-
.../Entities/GpApi/AccessTokenInfo.cs | 2 +
.../Entities/GpApi/GpApiRequest.cs | 1 +
.../Entities/GpApi/GpApiTokenResponse.cs | 3 +
.../Entities/HostedPaymentData.cs | 2 +
src/GlobalPayments.Api/Entities/Lodging.cs | 25 +
.../Entities/Reporting/TransactionSummary.cs | 9 +-
.../Entities/Transaction.cs | 6 +-
.../Entities/UPA/ProcessingIndicator.cs | 10 +
.../Entities/UPA/UpaParam.cs | 13 +
.../Entities/UPA/UpaTransactionData.cs | 11 +
src/GlobalPayments.Api/Entities/User.cs | 36 +-
.../Entities/UserAccount.cs | 18 +
.../BillPay/Responses/BillPayResponseBase.cs | 19 +
.../TokenInformationRequestResponse.cs | 1 +
src/GlobalPayments.Api/Gateways/Gateway.cs | 1 +
.../Gateways/GpApiConnector.cs | 20 +-
.../Gateways/GpEcomConnector.cs | 67 ++-
.../Interfaces/IFileProcessingService.cs | 12 +
.../Gateways/PorticoConnector.cs | 168 ++++++-
.../Gateways/ProPayConnector.cs | 30 +-
.../Gateways/TransitConnector.cs | 4 +-
.../GlobalPayments.Api.4.6.csproj | 81 +++-
.../Mapping/GpApiMapping.cs | 103 +++-
.../AlternativePaymentMethod.cs | 1 +
.../PaymentMethods/TransactionReference.cs | 2 +-
.../Properties/AssemblyInfo.cs | 4 +-
.../ServiceConfigs/Gateways/GpApiConfig.cs | 4 +
.../ServiceConfigs/Gateways/PorticoConfig.cs | 2 +-
.../Services/CreditService.cs | 11 +
.../Services/DeviceCloudService.cs | 35 ++
.../Services/DeviceService.cs | 5 +
.../Services/FileProcessingService.cs | 21 +
.../Services/GpApiService.cs | 4 +-
.../Services/HostedService.cs | 74 ++-
.../Services/PayFacService.cs | 2 +-
src/GlobalPayments.Api/ServicesContainer.cs | 11 +
.../Abstractions/IBatchClearResponse.cs | 7 +
.../Abstractions/IDeviceCommInterface.cs | 2 +
.../Abstractions/IDeviceInterface.cs | 39 +-
.../Terminals/Abstractions/IDeviceResponse.cs | 1 -
.../Abstractions/ISafDeleteFileResponse.cs | 7 +
.../Abstractions/ISafParamsResponse.cs | 17 +
.../Abstractions/ISafSummaryReport.cs | 8 +
.../Abstractions/ISafUploadResponse.cs | 13 +
.../Terminals/Builders/TerminalAuthBuilder.cs | 40 +-
.../Terminals/Builders/TerminalBuilder.cs | 8 +-
.../Builders/TerminalManageBuilder.cs | 75 ++-
.../Builders/TerminalReportBuilder.cs | 24 +-
.../Terminals/ConnectionConfig.cs | 29 +-
.../Terminals/DeviceController.cs | 11 +-
.../Terminals/DeviceInterface.cs | 122 +++--
.../Terminals/DeviceResponse.cs | 16 +-
.../Terminals/Diamond/DiamondController.cs | 292 ++++++++++++
.../Diamond/Entities/DiamondCloudRequest.cs | 20 +
.../Entities/Enums/AuthorizationMethod.cs | 9 +
.../Entities/Enums/AuthorizationType.cs | 7 +
.../Diamond/Entities/Enums/CardSource.cs | 9 +
.../Enums/DiamondCloudSearchCriteria.cs | 6 +
.../Enums/DiamondCloudTransactionType.cs | 13 +
.../Entities/Enums/TransactionResult.cs | 8 +
.../Interfaces/DiamondHttpInterface.cs | 118 +++++
.../Diamond/Interfaces/DiamondInterface.cs | 41 ++
.../Diamond/Responses/DiamondCloudResponse.cs | 265 +++++++++++
.../Terminals/DiamondCloudConfig.cs | 32 ++
.../Genius/Builders/MitcManageBuilder.cs | 66 +++
.../Terminals/Genius/Enums/MitcRequestType.cs | 74 +++
.../Genius/Enums/TransactionIdType.cs | 13 +
.../Terminals/Genius/GeniusController.cs | 449 ++++++++++++++----
.../Terminals/Genius/GeniusInterface.cs | 192 +++++++-
.../Genius/Interfaces/GeniusHttpInterface.cs | 2 +
.../Genius/Interfaces/MitcGateway.cs | 132 +++++
.../Genius/Request/GeniusMitcRequest.cs | 43 ++
.../Genius/Responses/MitcResponse.cs | 357 ++++++++++++++
.../Genius/ServiceConfigs/MitcConfig.cs | 75 +++
.../Terminals/HPA/HpaController.cs | 8 +-
.../Terminals/HPA/HpaEnums.cs | 1 +
.../Terminals/HPA/HpaInterface.cs | 6 +
.../HPA/Interfaces/HpaTcpInterface.cs | 18 +-
.../Terminals/HPA/Responses/EODResponse.cs | 13 +
.../Terminals/HPA/Responses/SAFResponse.cs | 96 ++--
.../HPA/Responses/SipBaseResponse.cs | 6 +
.../HPA/Responses/SipDeviceResponse.cs | 3 +-
.../Messaging/MessageReceivedEventHandler.cs | 4 +
.../PAX/Interfaces/PaxHttpInterface.cs | 11 +
.../PAX/Interfaces/PaxTcpInterface.cs | 28 +-
.../Terminals/PAX/PaxController.cs | 20 +-
.../Terminals/PAX/PaxEnums.cs | 2 +
.../Terminals/PAX/PaxInterface.cs | 85 +++-
.../PAX/Responses/BatchClearResponse.cs | 34 ++
.../Terminals/PAX/Responses/DeviceResponse.cs | 5 +-
.../PAX/Responses/SafDeleteFileResponse.cs | 25 +
.../PAX/Responses/SafParamsResponse.cs | 46 ++
.../PAX/Responses/SafSummaryReport.cs | 23 +
.../PAX/Responses/SafUploadResponse.cs | 35 ++
.../Terminals/PAX/SubGroups/TraceSubGroups.cs | 3 +
.../Terminals/SummaryResponse.cs | 10 +-
.../Terminals/TerminalUtilities.cs | 22 +-
.../UPA/Interfaces/UpaMicInterface.cs | 4 +-
.../UPA/Interfaces/UpaTcpInterface.cs | 29 +-
.../Terminals/UPA/Responses/BasicResponse.cs | 13 +
.../UPA/Responses/BatchReportResponse.cs | 10 +-
.../Terminals/UPA/Responses/CmdResult.cs | 10 +
.../Terminals/UPA/Responses/Data.cs | 19 +
.../UPA/Responses/SafReportResponse.cs | 4 +-
.../UPA/Responses/TransactionResponse.cs | 137 ++++--
.../Terminals/UPA/Responses/UpaEODResponse.cs | 5 +-
.../UPA/Responses/UpaGiftCardResponse.cs | 110 +++++
.../Terminals/UPA/Responses/UpaSAFResponse.cs | 1 +
.../UPA/Responses/UpaSignatureResponse.cs | 50 ++
.../Terminals/UPA/UpaController.cs | 100 ++--
.../Terminals/UPA/UpaInterface.cs | 50 +-
.../Terminals/UPA/UpaTransType.cs | 11 +-
src/GlobalPayments.Api/Utils/EnumUtils.cs | 21 +
src/GlobalPayments.Api/Utils/Extensions.cs | 7 +-
.../Utils/GenerationUtils.cs | 17 +-
src/GlobalPayments.Api/Utils/JsonUtils.cs | 10 +
149 files changed, 4667 insertions(+), 530 deletions(-)
create mode 100644 src/GlobalPayments.Api/Builders/FileProcessingBuilder.cs
create mode 100644 src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiFileProcessingRequestBuilder.cs
create mode 100644 src/GlobalPayments.Api/Builders/RequestBuilder/RequestBuilderValidations.cs
create mode 100644 src/GlobalPayments.Api/Entities/BlockedCardType.cs
create mode 100644 src/GlobalPayments.Api/Entities/Document.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/AcquisitionType.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/BlockCardType.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/CardTypeFilter.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/DocumentCategory.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/FileProcessingActionType.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/FileType.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/FundsStatus.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/MerchantCategory.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/Region.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/SafIndicator.cs
create mode 100644 src/GlobalPayments.Api/Entities/Enums/SafMode.cs
create mode 100644 src/GlobalPayments.Api/Entities/FileProcessor.cs
create mode 100644 src/GlobalPayments.Api/Entities/FileUploaded.cs
rename src/GlobalPayments.Api/Entities/{TransferFundsAccountDetails.cs => FundsAccountDetails.cs} (56%)
create mode 100644 src/GlobalPayments.Api/Entities/Lodging.cs
create mode 100644 src/GlobalPayments.Api/Entities/UPA/ProcessingIndicator.cs
create mode 100644 src/GlobalPayments.Api/Entities/UPA/UpaParam.cs
create mode 100644 src/GlobalPayments.Api/Entities/UPA/UpaTransactionData.cs
create mode 100644 src/GlobalPayments.Api/Entities/UserAccount.cs
create mode 100644 src/GlobalPayments.Api/Gateways/Interfaces/IFileProcessingService.cs
create mode 100644 src/GlobalPayments.Api/Services/DeviceCloudService.cs
create mode 100644 src/GlobalPayments.Api/Services/FileProcessingService.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Abstractions/IBatchClearResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Abstractions/ISafDeleteFileResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Abstractions/ISafParamsResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Abstractions/ISafSummaryReport.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Abstractions/ISafUploadResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/DiamondController.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/DiamondCloudRequest.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/AuthorizationMethod.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/AuthorizationType.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/CardSource.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/DiamondCloudSearchCriteria.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/DiamondCloudTransactionType.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Entities/Enums/TransactionResult.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Interfaces/DiamondHttpInterface.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Interfaces/DiamondInterface.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Diamond/Responses/DiamondCloudResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/DiamondCloudConfig.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Builders/MitcManageBuilder.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Enums/MitcRequestType.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Enums/TransactionIdType.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Interfaces/MitcGateway.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Request/GeniusMitcRequest.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/Responses/MitcResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Genius/ServiceConfigs/MitcConfig.cs
create mode 100644 src/GlobalPayments.Api/Terminals/Messaging/MessageReceivedEventHandler.cs
create mode 100644 src/GlobalPayments.Api/Terminals/PAX/Responses/BatchClearResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/PAX/Responses/SafDeleteFileResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/PAX/Responses/SafParamsResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/PAX/Responses/SafSummaryReport.cs
create mode 100644 src/GlobalPayments.Api/Terminals/PAX/Responses/SafUploadResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Responses/BasicResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Responses/CmdResult.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Responses/Data.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Responses/UpaGiftCardResponse.cs
create mode 100644 src/GlobalPayments.Api/Terminals/UPA/Responses/UpaSignatureResponse.cs
diff --git a/src/GlobalPayments.Api/Builders/AuthorizationBuilder.cs b/src/GlobalPayments.Api/Builders/AuthorizationBuilder.cs
index cc629e28..d5c3a1f2 100644
--- a/src/GlobalPayments.Api/Builders/AuthorizationBuilder.cs
+++ b/src/GlobalPayments.Api/Builders/AuthorizationBuilder.cs
@@ -7,6 +7,8 @@
using GlobalPayments.Api.Network.Elements;
using GlobalPayments.Api.Network.Entities;
using GlobalPayments.Api.PaymentMethods;
+using System.Reflection;
+using System.Linq;
namespace GlobalPayments.Api.Builders {
///
@@ -97,6 +99,8 @@ public class AuthorizationBuilder : TransactionBuilder {
internal StoredCredentialInitiator? TransactionInitiator { get; set; }
internal BNPLShippingMethod BNPLShippingMethod {get;set;}
internal bool MaskedDataResponse { get; set; }
+ internal BlockedCardType CardTypesBlocking { get; set; }
+ internal MerchantCategory? MerchantCategory { get; set; }
internal bool HasEmvFallbackData {
get {
return (EmvFallbackCondition != null || EmvLastChipRead != null || !string.IsNullOrEmpty(PaymentApplicationVersion));
@@ -847,12 +851,26 @@ public AuthorizationBuilder WithSurchargeAmount(decimal? value) {
return this;
}
- public AuthorizationBuilder WithMaskedDataResponse(bool value)
- {
+ public AuthorizationBuilder WithMaskedDataResponse(bool value) {
MaskedDataResponse = value;
return this;
}
+ public AuthorizationBuilder WithBlockedCardType(BlockedCardType cardTypesBlocking) {
+ var hasNulls = cardTypesBlocking.GetType().GetProperties().All(p => p.GetValue(cardTypesBlocking) == null);
+ if (hasNulls) {
+ throw new BuilderException("No properties set on the object");
+ }
+ CardTypesBlocking = cardTypesBlocking;
+
+ return this;
+ }
+
+ public AuthorizationBuilder WithMerchantCategory(MerchantCategory value) {
+ MerchantCategory = value;
+ return this;
+ }
+
internal AuthorizationBuilder(TransactionType type, IPaymentMethod payment = null) : base(type) {
WithPaymentMethod(payment);
}
@@ -987,7 +1005,27 @@ protected override void SetupValidations() {
Validations.For(TransactionType.Sale)
.With(TransactionModifier.EncryptedMobile)
- .Check(() => PaymentMethod).IsNotNull();
+ .Check(() => PaymentMethod).IsNotNull();
+
+ Validations.For(TransactionType.Sale)
+ .With(TransactionModifier.AlternativePaymentMethod)
+ .Check(() => PaymentMethod).IsNotNull()
+ .Check(() => Amount).IsNotNull()
+ .Check(() => Currency).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.StatusUpdateUrl)).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.ReturnUrl)).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.AccountHolderName)).IsNotNull();
+
+ Validations.For(TransactionType.Auth)
+ .With(TransactionModifier.AlternativePaymentMethod)
+ .Check(() => PaymentMethod).IsNotNull()
+ .Check(() => Amount).IsNotNull()
+ .Check(() => Currency).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.StatusUpdateUrl)).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.ReturnUrl)).IsNotNull()
+ .Check(() => PaymentMethod).PropertyOf(nameof(AlternativePaymentMethod.AccountHolderName)).IsNotNull();
+
+
}
///
diff --git a/src/GlobalPayments.Api/Builders/FileProcessingBuilder.cs b/src/GlobalPayments.Api/Builders/FileProcessingBuilder.cs
new file mode 100644
index 00000000..d66da05a
--- /dev/null
+++ b/src/GlobalPayments.Api/Builders/FileProcessingBuilder.cs
@@ -0,0 +1,34 @@
+using GlobalPayments.Api.Entities;
+using GlobalPayments.Api.Entities.Enums;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Builders {
+ public class FileProcessingBuilder : BaseBuilder {
+
+ public string ResourceId { get; set; }
+
+ public FileProcessingActionType FileProcessingActionType { get; set; }
+
+ public FileProcessingBuilder(FileProcessingActionType actionType) {
+ FileProcessingActionType = actionType;
+ }
+
+ public override FileProcessor Execute(string configName = "default") {
+ base.Execute(configName);
+ var client = ServicesContainer.Instance.GetFileProcessingClient(configName);
+ return client.ProcessFileUpload(this);
+ }
+
+ public FileProcessingBuilder WithResourceId(string resourceId) {
+ ResourceId = resourceId;
+ return this;
+ }
+
+ protected override void SetupValidations() {
+ Validations.For(FileProcessingActionType.GET_DETAILS)
+ .Check(() => ResourceId).IsNotNull();
+ }
+ }
+}
diff --git a/src/GlobalPayments.Api/Builders/ManagementBuilder.cs b/src/GlobalPayments.Api/Builders/ManagementBuilder.cs
index be58b571..7b5c2381 100644
--- a/src/GlobalPayments.Api/Builders/ManagementBuilder.cs
+++ b/src/GlobalPayments.Api/Builders/ManagementBuilder.cs
@@ -23,6 +23,7 @@ internal string AuthorizationCode {
return null;
}
}
+ internal decimal? batchDeviceId { get; set; }
internal string BatchReference { get; set; }
internal IEnumerable Bills { get; set; }
internal string CardType {
@@ -492,7 +493,7 @@ public override Transaction Execute(string configName = "default") {
PaymentMethod is TransactionReference &&
PaymentMethod.PaymentMethodType == PaymentMethodType.BankPayment) {
var obClient = ServicesContainer.Instance.GetOpenBanking(configName);
- if (obClient != client) {
+ if (obClient != null && obClient != client) {
return obClient.ManageOpenBanking(this);
}
}
diff --git a/src/GlobalPayments.Api/Builders/PayFacBuilder.cs b/src/GlobalPayments.Api/Builders/PayFacBuilder.cs
index fd3c7273..67bf24b9 100644
--- a/src/GlobalPayments.Api/Builders/PayFacBuilder.cs
+++ b/src/GlobalPayments.Api/Builders/PayFacBuilder.cs
@@ -33,6 +33,14 @@ public class PayFacBuilder : BaseBuilder where TResult : class
/// Required for partners ordering Portico devices. Valid values: [ UTC, PT, MST, MT, CT, ET, HST, AT, AST, AKST, ACT, EET, EAT, MET, NET, PLT, IST, BST, VST, CTT, JST, ACT, AET, SST, NST, MIT, CNT, AGT, CAT ]
///
public string TimeZone { get; set; }
+
+ public PaymentMethodName? PaymentMethodName { get; set; }
+
+ public PaymentMethodType? PaymentMethodType { get; set; }
+
+ public string Currency { get; set; }
+
+ public string ClientTransactionId{ get; set; }
///
/// Business Data - Required for business validated accounts. May also be required for personal validated accounts
@@ -161,6 +169,12 @@ protected override void SetupValidations() {
.Check(() => AccountNumber).IsNotNull()
.Check(() => DocumentUploadData).IsNotNull();
+ Validations.For(TransactionType.UploadDocument)
+ .With(TransactionModifier.Merchant)
+ .Check(() => DocumentUploadData).IsNotNull()
+ .Check(() => DocumentUploadData).PropertyOf(nameof(DocumentUploadData.DocType)).IsNotNull();
+
+
Validations.For(TransactionType.ObtainSSOKey)
.With(TransactionModifier.None)
.Check(() => AccountNumber).IsNotNull()
@@ -362,6 +376,26 @@ public PayFacBuilder WithTimeZone(string timezone) {
return this;
}
+ public PayFacBuilder WithPaymentMethodType(PaymentMethodType? paymentMethodType) {
+ PaymentMethodType = paymentMethodType;
+ return this;
+ }
+
+ public PayFacBuilder WithPaymentMethodName(PaymentMethodName? paymentMethodName) {
+ PaymentMethodName = paymentMethodName;
+ return this;
+ }
+
+ public PayFacBuilder WithCurrency(string currency) {
+ Currency = currency;
+ return this;
+ }
+
+ public PayFacBuilder WithClientTransactionId(string value) {
+ ClientTransactionId = value;
+ return this;
+ }
+
public PayFacBuilder WithBusinessData(BusinessData businessData) {
BusinessData = businessData;
return this;
diff --git a/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.cs b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.cs
index 3004588b..90d8318e 100644
--- a/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.cs
+++ b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.cs
@@ -213,6 +213,10 @@ public Request BuildRequest(AuthorizationBuilder builder, GpApiConnector gateway
}
verificationData.Set("payment_method", paymentMethod);
+ if (builder.StoredCredential != null) {
+ SetRequestStoredCredentials(builder.StoredCredential, verificationData);
+ }
+
Request.MaskedValues = MaskedValues;
return new Request {
@@ -302,7 +306,13 @@ public Request BuildRequest(AuthorizationBuilder builder, GpApiConnector gateway
var secureEcom = (builder.PaymentMethod as CreditCardData).ThreeDSecure;
if (secureEcom != null) {
var authentication = new JsonDoc().Set("id", secureEcom.ServerTransactionId);
- var three_ds = new JsonDoc().Set("exempt_status", secureEcom.ExemptStatus.ToString());
+ var three_ds = new JsonDoc()
+ .Set("exempt_status", secureEcom.ExemptStatus?.ToString())
+ .Set("message_version", secureEcom.MessageVersion)
+ .Set("eci", secureEcom.Eci)
+ .Set("server_trans_reference", secureEcom.ServerTransactionId)
+ .Set("ds_trans_reference", secureEcom.DirectoryServerTransactionId)
+ .Set("value", secureEcom.AuthenticationValue);
authentication.Set("three_ds", three_ds);
paymentMethod.Set("authentication", authentication);
@@ -520,9 +530,11 @@ public Request BuildRequest(AuthorizationBuilder builder, GpApiConnector gateway
.Set("convenience_amount", builder.ConvenienceAmount.ToNumericCurrencyString())
.Set("country", gateway.GpApiConfig.Country)
//.Set("language", EnumConverter.GetMapping(Target.GP_API, Language))
- .Set("ip_address", builder.CustomerIpAddress)
- //.Set("site_reference", "") //
- .Set("currency_conversion", !string.IsNullOrEmpty(builder.DccRateData?.DccId) ? new JsonDoc().Set("id", builder.DccRateData.DccId) : null)
+ .Set("ip_address", builder.CustomerIpAddress);
+ //.Set("site_reference", "") //
+ data.Set("merchant_category", builder.MerchantCategory.ToString() ?? null);
+
+ data.Set("currency_conversion", !string.IsNullOrEmpty(builder.DccRateData?.DccId) ? new JsonDoc().Set("id", builder.DccRateData.DccId) : null)
.Set("payment_method", paymentMethod)
.Set("risk_assessment", builder.FraudFilterMode != null ? MapFraudManagement(builder) : null)
.Set("link", !string.IsNullOrEmpty(builder.PaymentLinkId) ? new JsonDoc()
@@ -556,12 +568,7 @@ builder.PaymentMethod is BNPL ||
// stored credential
if (builder.StoredCredential != null) {
- data.Set("initiator", EnumConverter.GetMapping(Target.GP_API, builder.StoredCredential.Initiator));
- var storedCredential = new JsonDoc()
- .Set("model", EnumConverter.GetMapping(Target.GP_API, builder.StoredCredential.Type))
- .Set("reason", EnumConverter.GetMapping(Target.GP_API, builder.StoredCredential.Reason))
- .Set("sequence", EnumConverter.GetMapping(Target.GP_API, builder.StoredCredential.Sequence));
- data.Set("stored_credential", storedCredential);
+ SetRequestStoredCredentials(builder.StoredCredential, data);
}
Request.MaskedValues = MaskedValues;
@@ -573,6 +580,16 @@ builder.PaymentMethod is BNPL ||
};
}
+ private void SetRequestStoredCredentials(StoredCredential storedCredential, JsonDoc request)
+ {
+ request.Set("initiator", EnumConverter.GetMapping(Target.GP_API, storedCredential.Initiator));
+ var storedCredentialJson = new JsonDoc()
+ .Set("model", EnumConverter.GetMapping(Target.GP_API, storedCredential.Type))
+ .Set("reason", EnumConverter.GetMapping(Target.GP_API, storedCredential.Reason))
+ .Set("sequence", EnumConverter.GetMapping(Target.GP_API, storedCredential.Sequence));
+ request.Set("stored_credential", storedCredentialJson);
+ }
+
private static void DisposeMaskedValues()
{
MaskedValues = null;
@@ -932,6 +949,6 @@ private static JsonDoc SetOrderInformation(AuthorizationBuilder builder, ref Jso
}
return requestBody;
- }
+ }
}
}
diff --git a/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiFileProcessingRequestBuilder.cs b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiFileProcessingRequestBuilder.cs
new file mode 100644
index 00000000..9e94f478
--- /dev/null
+++ b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiFileProcessingRequestBuilder.cs
@@ -0,0 +1,44 @@
+using GlobalPayments.Api.Entities;
+using GlobalPayments.Api.Entities.Enums;
+using GlobalPayments.Api.Entities.GpApi;
+using GlobalPayments.Api.Gateways;
+using GlobalPayments.Api.Utils;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text;
+
+namespace GlobalPayments.Api.Builders.RequestBuilder.GpApi {
+ internal class GpApiFileProcessingRequestBuilder : IRequestBuilder {
+ public Request BuildRequest(FileProcessingBuilder builder, GpApiConnector gateway) {
+ switch (builder.FileProcessingActionType) {
+ case FileProcessingActionType.CREATE_UPLOAD_URL:
+ var data = new JsonDoc()
+ .Set("merchant_id", gateway.GpApiConfig.MerchantId)
+ .Set("account_id", gateway.GpApiConfig.AccessTokenInfo.FileProcessingAccountID);
+
+ var notifications = new JsonDoc()
+ .Set("status_url", gateway.GpApiConfig.StatusUrl);
+
+ if (notifications.HasKeys()) {
+ data.Set("notifications", notifications);
+ }
+
+ return new Request {
+ Verb = HttpMethod.Post,
+ Endpoint = $"{GpApiRequest.FILE_PROCESSING}",
+ RequestBody = data.ToString(),
+ };
+
+ case FileProcessingActionType.GET_DETAILS:
+ return new Request {
+ Verb = HttpMethod.Get,
+ Endpoint = $"{GpApiRequest.FILE_PROCESSING}/{builder.ResourceId}"
+ };
+
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiPayFacRequestBuilder.cs b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiPayFacRequestBuilder.cs
index dabd2055..81e477e3 100644
--- a/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiPayFacRequestBuilder.cs
+++ b/src/GlobalPayments.Api/Builders/RequestBuilder/GpApi/GpApiPayFacRequestBuilder.cs
@@ -21,6 +21,7 @@ public Request BuildRequest(PayFacBuilder builder, GpApiConnector gateway)
{
_builder = builder;
var merchantUrl = !string.IsNullOrEmpty(gateway.GpApiConfig.MerchantId) ? $"/merchants/{gateway.GpApiConfig.MerchantId}" : string.Empty;
+ Validate(builder.TransactionType, gateway);
switch (builder.TransactionType)
{
case TransactionType.Create:
@@ -93,6 +94,34 @@ public Request BuildRequest(PayFacBuilder builder, GpApiConnector gateway)
};
}
break;
+ case TransactionType.AddFunds:
+ var dataFunds = new JsonDoc();
+ dataFunds.Set("account_id", builder.AccountNumber)
+ .Set("type", builder.PaymentMethodType.ToString() ?? null)
+ .Set("amount", builder.Amount)
+ .Set("currency", builder.Currency ?? null)
+ .Set("payment_method", builder.PaymentMethodName.ToString() ?? null)
+ .Set("reference", builder.ClientTransactionId ?? GenerationUtils.GenerateOrderId());
+ return new Request
+ {
+ Verb = HttpMethod.Post,
+ Endpoint = $"{merchantUrl}{GpApiRequest.MERCHANT_MANAGEMENT_ENDPOINT}/{_builder.UserReference.UserId}/settlement/funds",
+ RequestBody = dataFunds.ToString(),
+ };
+
+ break;
+ case TransactionType.UploadDocument:
+ var requestData = new JsonDoc();
+ requestData.Set("function", builder.DocumentUploadData.DocCategory.ToString())
+ .Set("b64_content", builder.DocumentUploadData.Document)
+ .Set("format", builder.DocumentUploadData.DocType.ToString());
+ return new Request
+ {
+ Verb = HttpMethod.Post,
+ Endpoint = $"{merchantUrl}{GpApiRequest.MERCHANT_MANAGEMENT_ENDPOINT}/{_builder.UserReference.UserId}/documents",
+ RequestBody = requestData.ToString()
+ };
+ break;
default:
break;
}
@@ -100,6 +129,24 @@ public Request BuildRequest(PayFacBuilder builder, GpApiConnector gateway)
return null;
}
+ private void Validate(TransactionType transactionType, GpApiConnector gateway)
+ {
+ string errorMsg = string.Empty;
+ switch (transactionType) {
+ case TransactionType.AddFunds:
+ if (string.IsNullOrEmpty(gateway.GpApiConfig.MerchantId) && string.IsNullOrEmpty(_builder.UserReference?.UserId)) {
+ errorMsg = "property UserId or config MerchantId cannot be null for this transactionType";
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!string.IsNullOrEmpty(errorMsg)) {
+ throw new GatewayException(errorMsg);
+ }
+ }
+
private static Dictionary MapAddress(Address address, string countryCodeType = null, string functionKey = null)
{
var countryCode = string.Empty;
diff --git a/src/GlobalPayments.Api/Builders/RequestBuilder/RequestBuilderValidations.cs b/src/GlobalPayments.Api/Builders/RequestBuilder/RequestBuilderValidations.cs
new file mode 100644
index 00000000..518c108c
--- /dev/null
+++ b/src/GlobalPayments.Api/Builders/RequestBuilder/RequestBuilderValidations.cs
@@ -0,0 +1,56 @@
+using GlobalPayments.Api.Entities;
+using GlobalPayments.Api.Terminals;
+using System;
+using System.Reflection;
+
+namespace GlobalPayments.Api.Builders.RequestBuilder {
+ internal class RequestBuilderValidations {
+
+ private Validations _validations;
+ public RequestBuilderValidations(Validations validations) {
+ _validations = validations;
+ }
+ ///
+ /// Validate method for Terminals
+ ///
+ ///
+ ///
+ ///
+ public void Validate(T builder, TerminalReportType actionType) {
+ foreach (var key in _validations.rules.Keys) {
+ if (!key.Equals(actionType)) {
+ return;
+ }
+ foreach (var validation in _validations.rules[key]) {
+ if (validation.clause == null) continue;
+
+ // modifier
+ if (validation.constraint != null) {
+ Enum modifier = GetPropertyValue(builder, validation.constraint);
+ if (!Equals(validation.constraint, modifier))
+ continue;
+ }
+
+ // check precondition
+ if (validation.precondition != null) {
+ if (!validation.precondition.callback(actionType))
+ continue;
+ }
+
+ if (!validation.clause.callback(builder))
+ throw new BuilderException(validation.clause.message);
+ }
+ }
+ }
+
+ private Enum GetPropertyValue(object obj, object comp) {
+ if (obj == null) return null;
+
+ foreach (var propInfo in obj.GetType().GetRuntimeProperties()) {
+ if (propInfo.Name == comp.GetType().Name)
+ return (Enum)propInfo.GetValue(obj);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/BankPaymentResponse.cs b/src/GlobalPayments.Api/Entities/BankPaymentResponse.cs
index 83bd6a77..d291cfd0 100644
--- a/src/GlobalPayments.Api/Entities/BankPaymentResponse.cs
+++ b/src/GlobalPayments.Api/Entities/BankPaymentResponse.cs
@@ -19,5 +19,6 @@ public class BankPaymentResponse {
public string RemittanceReferenceType { get; set; }
public decimal? Amount { get; set; }
public string Currency { get; set; }
+ public string MaskedIbanLast4 { get; set; }
}
}
diff --git a/src/GlobalPayments.Api/Entities/BlockedCardType.cs b/src/GlobalPayments.Api/Entities/BlockedCardType.cs
new file mode 100644
index 00000000..2f9ff698
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/BlockedCardType.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities
+{
+ public class BlockedCardType
+ {
+ public bool? Consumerdebit { get; set; }
+ public bool? Consumercredit { get; set; }
+ public bool? Commercialcredit { get; set; }
+ public bool? Commercialdebit { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/CommercialData.cs b/src/GlobalPayments.Api/Entities/CommercialData.cs
index d535259d..510f811f 100644
--- a/src/GlobalPayments.Api/Entities/CommercialData.cs
+++ b/src/GlobalPayments.Api/Entities/CommercialData.cs
@@ -40,7 +40,8 @@ public class CommercialData {
public TaxType TaxType { get; private set; }
public string VAT_InvoiceNumber { get; set; }
-
+ public decimal? VATTaxAmtFreight { get; set; }
+ public decimal? VATTaxRateFreight { get; set; }
public CommercialData(TaxType taxType, CommercialIndicator level = CommercialIndicator.Level_II) {
TaxType = taxType;
CommercialIndicator = level;
diff --git a/src/GlobalPayments.Api/Entities/DisputeDocument.cs b/src/GlobalPayments.Api/Entities/DisputeDocument.cs
index ae407514..51aa0b66 100644
--- a/src/GlobalPayments.Api/Entities/DisputeDocument.cs
+++ b/src/GlobalPayments.Api/Entities/DisputeDocument.cs
@@ -1,10 +1,8 @@
using Newtonsoft.Json;
namespace GlobalPayments.Api.Entities {
- public class DisputeDocument {
- [JsonProperty("id")]
- public string Id { get; set; }
-
+ public class DisputeDocument : Document
+ {
[JsonProperty("type")]
public string Type { get; set; }
diff --git a/src/GlobalPayments.Api/Entities/Document.cs b/src/GlobalPayments.Api/Entities/Document.cs
new file mode 100644
index 00000000..eb876ce1
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Document.cs
@@ -0,0 +1,19 @@
+using GlobalPayments.Api.Entities.Enums;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities
+{
+ public class Document
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Status { get; set; }
+ public string TimeCreated { get; set; }
+ public FileType Format { get; set; }
+ public DocumentCategory Category { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/DocumentUploadData.cs b/src/GlobalPayments.Api/Entities/DocumentUploadData.cs
index c7f0f4b0..242dafb4 100644
--- a/src/GlobalPayments.Api/Entities/DocumentUploadData.cs
+++ b/src/GlobalPayments.Api/Entities/DocumentUploadData.cs
@@ -1,4 +1,5 @@
-using System;
+using GlobalPayments.Api.Entities.Enums;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
@@ -17,14 +18,14 @@ public class DocumentUploadData {
///
/// The file format of the Document to be uploaded. This property MUST be set if using the Document property directly, but will be set automatically if using the DocumentPath property
///
- private string _docType;
- public string DocType
+ private FileType? _docType;
+ public FileType? DocType
{
get { return _docType; }
set
{
- if (_validDocTypes.Contains(value)) {
- _docType = value;
+ if (_validDocTypes.Contains(value.Value.ToString())) {
+ _docType = value.Value;
}
else {
throw new Exception("The provided file type is not supported.");
@@ -41,15 +42,15 @@ public string DocType
/// The type of document you've been asked to provide by ProPay's Risk team. Valid values are:
/// Verification, FraudHolds, Underwriting, RetrievalRequest
///
- public string DocCategory { get; set; }
+ public DocumentCategory? DocCategory { get; set; }
public string DocumentPath {
set
{
var docPath = value;
if (docPath != null) {
- var docType = docPath.Substring(docPath.LastIndexOf('.') + 1);
+ var docType = docPath.Substring(docPath.LastIndexOf('.') + 1).ToUpper();
if (_validDocTypes.Contains(docType)) {
- DocType = docType;
+ DocType = (FileType)Enum.Parse(typeof(FileType), docType);
Document = Convert.ToBase64String(System.IO.File.ReadAllBytes(docPath));
}
else {
@@ -65,15 +66,15 @@ public string DocumentPath {
private ReadOnlyCollection _validDocTypes { get; } = new ReadOnlyCollection(
new string[]
{
- "tif",
- "tiff",
- "bmp",
- "jpg",
- "jpeg",
- "gif",
- "png",
- "doc",
- "docx"
+ "TIF",
+ "TIFF",
+ "BMP",
+ "JPG",
+ "JPEG",
+ "GIF",
+ "PNG",
+ "DOC",
+ "DOCX"
}
);
}
diff --git a/src/GlobalPayments.Api/Entities/Enums.cs b/src/GlobalPayments.Api/Entities/Enums.cs
index 1198beb8..bbf049f6 100644
--- a/src/GlobalPayments.Api/Entities/Enums.cs
+++ b/src/GlobalPayments.Api/Entities/Enums.cs
@@ -72,6 +72,12 @@ public enum DeviceType
/// Indicates a UPA device
///
UPA_DEVICE,
+ PAX_ARIES8,
+ PAX_A80,
+ PAX_A35,
+ PAX_A920,
+ PAX_A77,
+ NEXGO_N5,
///
/// Indicates a genius terminal
///
@@ -80,7 +86,11 @@ public enum DeviceType
///
/// Indicates a Nucleus terminal
///
- NUCLEUS_SATURN_1000
+ NUCLEUS_SATURN_1000,
+ ///
+ /// Indicates a genius verifone P400
+ ///
+ GENIUS_VERIFONE_P400,
}
@@ -993,7 +1003,8 @@ public enum AlternativePaymentType {
WECHAT_PAY,
ZIMPLER,
UK_DIRECT_DEBIT,
- PAYBYBANKAPP
+ PAYBYBANKAPP,
+ ALIPAY
}
public enum CardType {
@@ -1041,6 +1052,14 @@ internal static class SAFReportType {
public const string APPROVED_VOID = "APPROVED SAF VOID SUMMARY";
public const string PENDING_VOID = "PENDING SAF VOID SUMMARY";
public const string DECLINED_VOID = "DECLINED SAF VOID SUMMARY";
+ public const string PROVISIONAL = "PROVISIONAL SAF SUMMARY";
+ public const string DISCARDED = "DISCARDED SAF SUMMARY";
+ public const string REVERSAL = "REVERSAL SUMMARY";
+ public const string EMV_DECLINED = "EMV OFFLINE DECLINE SUMMARY";
+ public const string ATTACHMENT = "ATTACHMENT SUMMARY";
+ public const string PROVISIONAL_VOID = "PROVISIONAL SAF VOID SUMMARY";
+ public const string DISCARDED_VOID = "DISCARDED SAF VOID SUMMARY";
+
}
internal static class EODCommandType {
@@ -1055,6 +1074,7 @@ internal static class EODCommandType {
public const string EMV_PARAMETER_DOWNLOAD = "EMVPDL";
public const string EMV_CRYPTOGRAM_TYPE = "EMVTC";
public const string GET_BATCH_REPORT = "GetBatchReport";
+ public const string GET_SAF_REPORT = "GetSAFReport";
}
internal static class CardSummaryType {
diff --git a/src/GlobalPayments.Api/Entities/Enums/AcquisitionType.cs b/src/GlobalPayments.Api/Entities/Enums/AcquisitionType.cs
new file mode 100644
index 00000000..ef7736aa
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/AcquisitionType.cs
@@ -0,0 +1,22 @@
+using GlobalPayments.Api.Utils;
+
+namespace GlobalPayments.Api.Entities.Enums {
+ public enum AcquisitionType {
+ [Map(Target.UPA, "None")]
+ None,
+ [Map(Target.UPA, "Contact")]
+ Contact,
+ [Map(Target.UPA, "Contactless")]
+ Contactless,
+ [Map(Target.UPA, "Swipe")]
+ Swipe,
+ [Map(Target.UPA, "Manual")]
+ Manual,
+ [Map(Target.UPA, "Scan")]
+ Scan,
+ [Map(Target.UPA, "Insert")]
+ Insert,
+ [Map(Target.UPA, "Tap")]
+ Tap
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/BlockCardType.cs b/src/GlobalPayments.Api/Entities/Enums/BlockCardType.cs
new file mode 100644
index 00000000..0cbb01c0
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/BlockCardType.cs
@@ -0,0 +1,19 @@
+using GlobalPayments.Api.Utils;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum BlockCardType
+ {
+ [Description("consumercredit")]
+ CONSUMER_CREDIT,
+ [Description("consumerdebit")]
+ CONSUMER_DEBIT,
+ [Description("commercialdebit")]
+ COMMERCIAL_DEBIT,
+ [Description("commercialcredit")]
+ COMMERCIAL_CREDIT
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/CardTypeFilter.cs b/src/GlobalPayments.Api/Entities/Enums/CardTypeFilter.cs
new file mode 100644
index 00000000..61eb1bd4
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/CardTypeFilter.cs
@@ -0,0 +1,16 @@
+using GlobalPayments.Api.Utils;
+
+namespace GlobalPayments.Api.Entities.Enums {
+ public enum CardTypeFilter {
+ [Map(Target.UPA, "GIFT")]
+ GIFT,
+ [Map(Target.UPA, "VISA")]
+ VISA,
+ [Map(Target.UPA, "MC")]
+ MC,
+ [Map(Target.UPA, "AMEX")]
+ AMEX,
+ [Map(Target.UPA, "DISCOVER")]
+ DISCOVER
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/DocumentCategory.cs b/src/GlobalPayments.Api/Entities/Enums/DocumentCategory.cs
new file mode 100644
index 00000000..e005aba0
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/DocumentCategory.cs
@@ -0,0 +1,19 @@
+using GlobalPayments.Api.Utils;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum DocumentCategory
+ {
+ [Map(Target.GP_API, "IDENTITY_VERIFICATION")]
+ IDENTITY_VERIFICATION,
+ [Map(Target.GP_API, "RISK_REVIEW")]
+ RISK_REVIEW,
+ [Map(Target.GP_API, "UNDERWRITING")]
+ UNDERWRITING,
+
+ VERIFICATION
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/FileProcessingActionType.cs b/src/GlobalPayments.Api/Entities/Enums/FileProcessingActionType.cs
new file mode 100644
index 00000000..84227821
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/FileProcessingActionType.cs
@@ -0,0 +1,6 @@
+namespace GlobalPayments.Api.Entities.Enums {
+ public enum FileProcessingActionType {
+ CREATE_UPLOAD_URL = 1,
+ GET_DETAILS = 2
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/FileType.cs b/src/GlobalPayments.Api/Entities/Enums/FileType.cs
new file mode 100644
index 00000000..16757855
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/FileType.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum FileType {
+ TIF,
+ TIFF,
+ BMP,
+ JPG,
+ JPEG,
+ GIF,
+ PNG,
+ DOC,
+ DOCX
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/FraudFilterResult.cs b/src/GlobalPayments.Api/Entities/Enums/FraudFilterResult.cs
index 5f1d744f..5f47ccd4 100644
--- a/src/GlobalPayments.Api/Entities/Enums/FraudFilterResult.cs
+++ b/src/GlobalPayments.Api/Entities/Enums/FraudFilterResult.cs
@@ -21,10 +21,10 @@ public enum FraudFilterResult {
[Map(Target.GP_API, "ERROR")]
ERROR,
- [Map(Target.GP_API, "RELEASE_SUCCESSFULL")]
+ [Map(Target.GP_API, "RELEASE_SUCCESSFUL")]
RELEASE_SUCCESSFUL,
- [Map(Target.GP_API, "HOLD_SUCCESSFULL")]
+ [Map(Target.GP_API, "HOLD_SUCCESSFUL")]
HOLD_SUCCESSFUL
}
}
diff --git a/src/GlobalPayments.Api/Entities/Enums/FundsStatus.cs b/src/GlobalPayments.Api/Entities/Enums/FundsStatus.cs
new file mode 100644
index 00000000..6beff77b
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/FundsStatus.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum FundsStatus {
+ CAPTURED,
+ DECLINE
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/MerchantCategory.cs b/src/GlobalPayments.Api/Entities/Enums/MerchantCategory.cs
new file mode 100644
index 00000000..f72da6e0
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/MerchantCategory.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum MerchantCategory {
+ HOTEL,
+ AIRLINE,
+ RETAIL,
+ TOP_UP,
+ PLAYER,
+ CD_KEY,
+ OTHER
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/Region.cs b/src/GlobalPayments.Api/Entities/Enums/Region.cs
new file mode 100644
index 00000000..7f1900ab
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/Region.cs
@@ -0,0 +1,10 @@
+namespace GlobalPayments.Api.Entities.Enums {
+ public enum Region {
+ US,
+ CA,
+ AU,
+ NZ,
+ UK,
+ EU
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/SafIndicator.cs b/src/GlobalPayments.Api/Entities/Enums/SafIndicator.cs
new file mode 100644
index 00000000..13bbccbc
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/SafIndicator.cs
@@ -0,0 +1,9 @@
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum SafIndicator
+ {
+ NEWLY_STORED_TRANSACTIONS,
+ FAILED_TRANSACTIONS,
+ ALL_TRANSACTIONS
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/SafMode.cs b/src/GlobalPayments.Api/Entities/Enums/SafMode.cs
new file mode 100644
index 00000000..3f9b1455
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Enums/SafMode.cs
@@ -0,0 +1,10 @@
+namespace GlobalPayments.Api.Entities.Enums
+{
+ public enum SafMode
+ {
+ STAY_ONLINE,
+ STAY_OFFLINE,
+ OFFLINE_TILL_BATCH,
+ OFFLINE_ONDEMAND_OR_AUTO
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/Enums/ServiceEndpoints.cs b/src/GlobalPayments.Api/Entities/Enums/ServiceEndpoints.cs
index fdd023f0..78824484 100644
--- a/src/GlobalPayments.Api/Entities/Enums/ServiceEndpoints.cs
+++ b/src/GlobalPayments.Api/Entities/Enums/ServiceEndpoints.cs
@@ -30,5 +30,10 @@ public static class ServiceEndpoints {
public const string Transaction_API_TEST = "https://api.pit.paygateway.com/transactions";
public const string OPEN_BANKING_TEST = "https://api.sandbox.globalpay-ecommerce.com/openbanking";
public const string OPEN_BANKING_PRODUCTION = "https://api.globalpay-ecommerce.com/openbanking";
+ public const string DIAMOND_CLOUD_TEST = "https://qr-cert.simpletabcloud.com/tomcat/command";
+ public const string DIAMOND_CLOUD_PROD = "https://qr.simpletabcloud.com/tomcat/command";
+ public const string DIAMOND_CLOUD_PROD_EU= "https://qreu.simpletabcloud.com/tomcat/command";
+ public const string GENIUS_MITC_PRODUCTION = "https://api.paygateway.com/transactions";
+ public const string GENIUS_MITC_TEST = "https://api.pit.paygateway.com/transactions";
}
}
diff --git a/src/GlobalPayments.Api/Entities/Enums/TransactionType.cs b/src/GlobalPayments.Api/Entities/Enums/TransactionType.cs
index 73cc2575..01bb2a9d 100644
--- a/src/GlobalPayments.Api/Entities/Enums/TransactionType.cs
+++ b/src/GlobalPayments.Api/Entities/Enums/TransactionType.cs
@@ -334,6 +334,7 @@ public enum TransactionType : byte {
OrderDevice = 66,
- TransferFunds = 67
+ TransferFunds = 67,
+
}
}
diff --git a/src/GlobalPayments.Api/Entities/Exceptions.cs b/src/GlobalPayments.Api/Entities/Exceptions.cs
index b14dba53..c2662cf4 100644
--- a/src/GlobalPayments.Api/Entities/Exceptions.cs
+++ b/src/GlobalPayments.Api/Entities/Exceptions.cs
@@ -12,22 +12,6 @@ public class ApiException : Exception {
public ApiException(string message = null, Exception innerException = null) : base(message, innerException) { }
}
- public class DeviceException : Exception
- {
- /// The exception message
- /// The device state
- public DeviceException(string message, string state) : base(message)
- {
- State = state;
- }
-
- ///
- /// Gets the state.
- ///
- /// The state.
- public string State { get; }
- }
-
///
/// A builder error occurred. Check the method calls against the builder.
///
diff --git a/src/GlobalPayments.Api/Entities/FileProcessor.cs b/src/GlobalPayments.Api/Entities/FileProcessor.cs
new file mode 100644
index 00000000..b20f05c9
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/FileProcessor.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace GlobalPayments.Api.Entities {
+ public class FileProcessor {
+ public string ResourceId { get; set; }
+ public string UploadUrl { get; set; }
+ public string ExpirationDate { get; set; }
+ public string Status { get; set; }
+ public string CreatedDate { get; set; }
+ public string TotalRecordCount { get; set; }
+ public string ResponseCode { get; set; }
+ public string ResponseMessage { get; set; }
+ public List FilesUploaded { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/FileUploaded.cs b/src/GlobalPayments.Api/Entities/FileUploaded.cs
new file mode 100644
index 00000000..43219ee0
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/FileUploaded.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities
+{
+ public class FileUploaded
+ {
+ public string FileId { get; set; }
+ public string FileName { get; set; }
+ public string TimeCreated { get; set; }
+ public string Url { get; set; }
+ public string ExpirationDate { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/TransferFundsAccountDetails.cs b/src/GlobalPayments.Api/Entities/FundsAccountDetails.cs
similarity index 56%
rename from src/GlobalPayments.Api/Entities/TransferFundsAccountDetails.cs
rename to src/GlobalPayments.Api/Entities/FundsAccountDetails.cs
index 36ff4d4a..cb06cf73 100644
--- a/src/GlobalPayments.Api/Entities/TransferFundsAccountDetails.cs
+++ b/src/GlobalPayments.Api/Entities/FundsAccountDetails.cs
@@ -4,13 +4,17 @@
namespace GlobalPayments.Api.Entities
{
- public class TransferFundsAccountDetails {
+ public class FundsAccountDetails {
public string Id { get; set; }
public string Status { get; set; }
public string TimeCreated { get; set; }
+ public string TimeLastUpdated { get; set; }
public decimal? Amount { get; set; }
public string Reference { get; set; }
public string Description { get; set; }
-
+ public string Currency { get; set; }
+ public string PaymentMethodType { get; set; }
+ public string PaymentMethodName { get; set; }
+ public UserAccount Account { get; set; }
}
}
diff --git a/src/GlobalPayments.Api/Entities/GpApi/AccessTokenInfo.cs b/src/GlobalPayments.Api/Entities/GpApi/AccessTokenInfo.cs
index fad9a623..c1d5b321 100644
--- a/src/GlobalPayments.Api/Entities/GpApi/AccessTokenInfo.cs
+++ b/src/GlobalPayments.Api/Entities/GpApi/AccessTokenInfo.cs
@@ -13,11 +13,13 @@ public class AccessTokenInfo
public string TransactionProcessingAccountName { get; set; }
public string RiskAssessmentAccountName { get; set; }
public string MerchantManagementAccountName { get; set; }
+ public string FileProcessingAccountName { get; set; }
public string DataAccountID { get; set; }
public string DisputeManagementAccountID { get; set; }
public string TokenizationAccountID { get; set; }
public string TransactionProcessingAccountID { get; set; }
public string RiskAssessmentAccountID { get; set; }
public string MerchantManagementAccountID { get; set; }
+ public string FileProcessingAccountID { get; set; }
}
}
diff --git a/src/GlobalPayments.Api/Entities/GpApi/GpApiRequest.cs b/src/GlobalPayments.Api/Entities/GpApi/GpApiRequest.cs
index de62da1e..131db7a3 100644
--- a/src/GlobalPayments.Api/Entities/GpApi/GpApiRequest.cs
+++ b/src/GlobalPayments.Api/Entities/GpApi/GpApiRequest.cs
@@ -22,5 +22,6 @@ internal class GpApiRequest {
public const string ACCOUNTS_ENDPOINT = "/accounts";
public const string TRANSFER_ENDPOINT = "/transfers";
public const string DEVICES = "/devices";
+ public const string FILE_PROCESSING = "/files";
}
}
diff --git a/src/GlobalPayments.Api/Entities/GpApi/GpApiTokenResponse.cs b/src/GlobalPayments.Api/Entities/GpApi/GpApiTokenResponse.cs
index 04d30850..3896b5e5 100644
--- a/src/GlobalPayments.Api/Entities/GpApi/GpApiTokenResponse.cs
+++ b/src/GlobalPayments.Api/Entities/GpApi/GpApiTokenResponse.cs
@@ -11,6 +11,7 @@ internal class GpApiTokenResponse {
const string TRANSACTION_PROCESSING_ACCOUNT_NAME_PREFIX = "TRA_";
const string RIKS_ASSESSMENT_ACCOUNT_NAME_PREFIX = "RAA_";
const string MERCHANT_MANAGEMENT_ACCOUNT_NAME_PREFIX = "MMA_";
+ const string FILE_PROCESSING_ACCOUNT_NAME_PREFIX = "FPA_";
internal string Token { get; private set; }
internal string Type { get; private set; }
@@ -28,6 +29,7 @@ internal class GpApiTokenResponse {
internal string TransactionProcessingAccountName { get { return GetAccountName(TRANSACTION_PROCESSING_ACCOUNT_NAME_PREFIX); } }
internal string RiskAssessmentAccountName { get { return GetAccountName(RIKS_ASSESSMENT_ACCOUNT_NAME_PREFIX); } }
internal string MerchantManagementAccountName { get { return GetAccountName(MERCHANT_MANAGEMENT_ACCOUNT_NAME_PREFIX); } }
+ internal string FileProcessingAccountName { get { return GetAccountName(FILE_PROCESSING_ACCOUNT_NAME_PREFIX); } }
internal string DataAccountID { get { return GetAccountID(DATA_ACCOUNT_NAME_PREFIX); } }
internal string DisputeManagementAccountID { get { return GetAccountID(DISPUTE_MANAGEMENT_ACCOUNT_NAME_PREFIX); } }
@@ -35,6 +37,7 @@ internal class GpApiTokenResponse {
internal string TransactionProcessingAccountID { get { return GetAccountID(TRANSACTION_PROCESSING_ACCOUNT_NAME_PREFIX); } }
internal string RiskAssessmentAccountID { get { return GetAccountID(RIKS_ASSESSMENT_ACCOUNT_NAME_PREFIX); } }
internal string MerchantManagementAccountID { get { return GetAccountID(MERCHANT_MANAGEMENT_ACCOUNT_NAME_PREFIX); } }
+ internal string FileProcessingAccountID { get { return GetAccountID(FILE_PROCESSING_ACCOUNT_NAME_PREFIX); } }
private string GetAccountID(string accountPrefix) {
return Accounts?.Where(a => a.Id.StartsWith(accountPrefix)).Select(a => a.Id).FirstOrDefault();
diff --git a/src/GlobalPayments.Api/Entities/HostedPaymentData.cs b/src/GlobalPayments.Api/Entities/HostedPaymentData.cs
index 3e9e8ac7..68eb57c5 100644
--- a/src/GlobalPayments.Api/Entities/HostedPaymentData.cs
+++ b/src/GlobalPayments.Api/Entities/HostedPaymentData.cs
@@ -142,6 +142,8 @@ public HostedPaymentData() {
public bool EnableExemptionOptimization { get; set; }
+ public BlockCardType[] BlockCardTypes { get; set; }
+
///
/// Determine whether or not the address and contact information must be provided in the HPP request and whether they will/won't be editable by the customer.
///
diff --git a/src/GlobalPayments.Api/Entities/Lodging.cs b/src/GlobalPayments.Api/Entities/Lodging.cs
new file mode 100644
index 00000000..d48c7bb1
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/Lodging.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace GlobalPayments.Api.Entities
+{
+ public class Lodging
+ {
+ public int? FolioNumber { get; set; }
+ public int? StayDuration { get; set; }
+ public DateTime? CheckInDate { get; set; }
+ public DateTime? CheckOutDate { get; set; }
+ public decimal? DailyRate { get; set; }
+ public int? PreferredCustomer { get; set; }
+ public ExtraChargeTypes ExtraChargeTypes { get; set; }
+ public decimal? ExtraChargeTotal { get; set; }
+ }
+ public class ExtraChargeTypes
+ {
+ public bool HasRestaurantCharge { get; set; }
+ public bool HasGiftShopCharge { get; set; }
+ public bool HasMiniBarCharge { get; set; }
+ public bool HasTelephoneCharge { get; set; }
+ public bool HasLaundryCharge { get; set; }
+ public bool HasOtherCharge { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/GlobalPayments.Api/Entities/Reporting/TransactionSummary.cs b/src/GlobalPayments.Api/Entities/Reporting/TransactionSummary.cs
index ff9849db..75756d93 100644
--- a/src/GlobalPayments.Api/Entities/Reporting/TransactionSummary.cs
+++ b/src/GlobalPayments.Api/Entities/Reporting/TransactionSummary.cs
@@ -78,7 +78,8 @@ public class TransactionSummary {
/// The client transaction ID sent in the authorization request.
///
public string ClientTransactionId { get; set; }
-
+ public CommercialData CommercialData { get; set; }
+ public CommercialLineItem CommercialLineItem { get; set; }
public string CompanyName { get; set; }
///
@@ -217,6 +218,8 @@ public class TransactionSummary {
/// The reference number provided by the issuer.
///
public string ReferenceNumber { get; set; }
+ public string SafReferenceNumber { get; set; }
+ public string TranNo { get; set; }
public int? RepeatCount { get; set; }
@@ -245,6 +248,9 @@ public class TransactionSummary {
/// The originally requested shipping amount.
///
public decimal? ShippingAmount { get; set; }
+ public string ShippingInvoiceNbr { get; set; }
+ public int? ShippingMonth { get; set; }
+ public int? ShippingDay { get; set; }
public string SiteTrace { get; set; }
@@ -316,5 +322,6 @@ public class TransactionSummary {
public string Fingerprint { get; set; }
public string FingerprintIndicator { get; set; }
+ public string Email { get; set;}
}
}
diff --git a/src/GlobalPayments.Api/Entities/Transaction.cs b/src/GlobalPayments.Api/Entities/Transaction.cs
index c5276f41..aa2cd780 100644
--- a/src/GlobalPayments.Api/Entities/Transaction.cs
+++ b/src/GlobalPayments.Api/Entities/Transaction.cs
@@ -133,8 +133,8 @@ public string CardType
public PayByLinkResponse PayByLinkResponse { get; set; }
- private List _transfersFundsAccounts;
- public List TransfersFundsAccounts
+ private List _transfersFundsAccounts;
+ public List TransfersFundsAccounts
{
get {
return _transfersFundsAccounts;
@@ -145,7 +145,7 @@ public List TransfersFundsAccounts
}
if (value.Count > 0) {
if(TransactionReference.TransfersFundsAccounts == null) {
- TransactionReference.TransfersFundsAccounts = new List();
+ TransactionReference.TransfersFundsAccounts = new List();
}
TransactionReference.TransfersFundsAccounts = value;
}
diff --git a/src/GlobalPayments.Api/Entities/UPA/ProcessingIndicator.cs b/src/GlobalPayments.Api/Entities/UPA/ProcessingIndicator.cs
new file mode 100644
index 00000000..aec49043
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/UPA/ProcessingIndicator.cs
@@ -0,0 +1,10 @@
+using GlobalPayments.Api.Entities.Enums;
+
+namespace GlobalPayments.Api.Entities.UPA {
+ public class ProcessingIndicator {
+ public string QuickChip { get; set; }
+ public string CheckLuhn { get; set; }
+ public string SecurityCode { get; set; }
+ public CardTypeFilter CardTypeFilter { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/UPA/UpaParam.cs b/src/GlobalPayments.Api/Entities/UPA/UpaParam.cs
new file mode 100644
index 00000000..34c29732
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/UPA/UpaParam.cs
@@ -0,0 +1,13 @@
+using GlobalPayments.Api.Entities.Enums;
+
+namespace GlobalPayments.Api.Entities.UPA {
+ public class UpaParam {
+ public int Timeout { get; set; }
+ public AcquisitionType AcquisitionTypes { get; set; }
+ public string Header { get; set; }
+ public string DisplayTotalAmount { get; set; }
+ public bool PromptForManual { get; set; }
+ public int BrandIcon1 { get; set; }
+ public int BrandIcon2 { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/UPA/UpaTransactionData.cs b/src/GlobalPayments.Api/Entities/UPA/UpaTransactionData.cs
new file mode 100644
index 00000000..0b7aaf81
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/UPA/UpaTransactionData.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace GlobalPayments.Api.Entities.UPA {
+ public class UpaTransactionData {
+ public decimal TotalAmount { get; set; }
+ public decimal CashBackAmount { get; set; }
+ public DateTime TranDate { get; set; }
+ public DateTime TranTime { get; set; }
+ public TransactionType TransType { get; set; }
+ }
+}
diff --git a/src/GlobalPayments.Api/Entities/User.cs b/src/GlobalPayments.Api/Entities/User.cs
index d1589022..a5485e03 100644
--- a/src/GlobalPayments.Api/Entities/User.cs
+++ b/src/GlobalPayments.Api/Entities/User.cs
@@ -8,8 +8,7 @@
namespace GlobalPayments.Api.Entities
{
- public class User
- {
+ public class User {
///
/// This is a label to identify the user
///
@@ -24,11 +23,11 @@ public class User
/// The date and time the resource object was last changed.
///
public DateTime? TimeLastUpdated { get; set; }
-
+
public string Email { get; set; }
-
+
public List Addresses { get; set; }
-
+
public PhoneNumber ContactPhone { get; set; }
///
@@ -46,7 +45,11 @@ public class User
public List PersonList { get; set; }
- public List PaymentMethods { get; set; }
+ public List PaymentMethods { get; set; }
+
+ public Document Document { get; set; }
+
+ public FundsAccountDetails FundsAccountDetails { get; set; }
///
/// Creates an `User` object from an existing user ID.
@@ -75,5 +78,26 @@ public PayFacBuilder Edit()
return builder;
}
+
+ public PayFacBuilder UploadDocument(DocumentUploadData data)
+ {
+ PayFacBuilder builder = new PayFacBuilder(TransactionType.UploadDocument)
+ .WithUserReference(this.UserReference)
+ .WithDocumentUploadData(data);
+
+ if (UserReference.UserType != null) {
+ builder = builder.WithModifier(EnumConverter.FromDescription(UserReference.UserType.ToString()));
+ }
+ return builder;
+ }
+
+ public PayFacBuilder AddFunds()
+ {
+ PayFacBuilder builder = new PayFacBuilder(TransactionType.AddFunds)
+ .WithUserReference(this.UserReference);
+
+ return builder;
+ }
+
}
}
diff --git a/src/GlobalPayments.Api/Entities/UserAccount.cs b/src/GlobalPayments.Api/Entities/UserAccount.cs
new file mode 100644
index 00000000..e1af16b1
--- /dev/null
+++ b/src/GlobalPayments.Api/Entities/UserAccount.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Entities
+{
+ public class UserAccount
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Type { get; set; }
+
+ public UserAccount(string id, string name = null) {
+ Id = id;
+ Name = name;
+ }
+ }
+}
diff --git a/src/GlobalPayments.Api/Gateways/BillPay/Responses/BillPayResponseBase.cs b/src/GlobalPayments.Api/Gateways/BillPay/Responses/BillPayResponseBase.cs
index 6ee61bcc..75573ba4 100644
--- a/src/GlobalPayments.Api/Gateways/BillPay/Responses/BillPayResponseBase.cs
+++ b/src/GlobalPayments.Api/Gateways/BillPay/Responses/BillPayResponseBase.cs
@@ -61,5 +61,24 @@ internal PaymentMethodType SetPaymentMethodType(string paymentMethod) {
return paymentMethodType;
}
+
+ ///
+ /// Convert a string value cardType to a CardType enum value
+ ///
+ /// A string representing the card type
+ /// The enumeration value of the specified card type, if supported
+ internal string GetCardType(string cardType)
+ {
+ if (cardType.Contains("Visa"))
+ return CardType.VISA.ToString();
+ else if (cardType.Contains("Mastercard"))
+ return CardType.MC.ToString();
+ else if (cardType.Contains("Discover"))
+ return CardType.DISC.ToString();
+ else if (cardType.Contains("AmericanExpress"))
+ return CardType.AMEX.ToString();
+ else
+ return string.Empty;
+ }
}
}
diff --git a/src/GlobalPayments.Api/Gateways/BillPay/Responses/TokenInformationRequestResponse.cs b/src/GlobalPayments.Api/Gateways/BillPay/Responses/TokenInformationRequestResponse.cs
index 2e63eb5e..bc39e622 100644
--- a/src/GlobalPayments.Api/Gateways/BillPay/Responses/TokenInformationRequestResponse.cs
+++ b/src/GlobalPayments.Api/Gateways/BillPay/Responses/TokenInformationRequestResponse.cs
@@ -38,6 +38,7 @@ public override Transaction Map() {
CardExpYear = tokenDetailsElement.GetValue("a:ExpirationYear"),
CardLast4 = tokenDetailsElement.GetValue("a:Last4"),
PaymentMethodType = SetPaymentMethodType(tokenDetailsElement.GetValue("a:PaymentMethod")),
+ CardType = GetCardType(tokenDetailsElement.GetValue("a:PaymentMethod")),
Token = tokenDetailsElement.GetValue("a:Token"),
TokenData = new TokenData
{
diff --git a/src/GlobalPayments.Api/Gateways/Gateway.cs b/src/GlobalPayments.Api/Gateways/Gateway.cs
index 03ba9485..9e1cadba 100644
--- a/src/GlobalPayments.Api/Gateways/Gateway.cs
+++ b/src/GlobalPayments.Api/Gateways/Gateway.cs
@@ -59,6 +59,7 @@ private string GenerateRequestLog(HttpRequestMessage request, bool isXml = false
protected GatewayResponse SendRequest(HttpMethod verb, string endpoint, string data = null, Dictionary queryStringParams = null, string contentType = null, bool isCharSet = true, bool isXml = false) {
HttpClient httpClient = new HttpClient(HttpClientHandlerBuilder.Build(WebProxy)) {
Timeout = TimeSpan.FromMilliseconds(Timeout)
+
};
var queryString = BuildQueryString(queryStringParams);
diff --git a/src/GlobalPayments.Api/Gateways/GpApiConnector.cs b/src/GlobalPayments.Api/Gateways/GpApiConnector.cs
index a1c756ed..22401ace 100644
--- a/src/GlobalPayments.Api/Gateways/GpApiConnector.cs
+++ b/src/GlobalPayments.Api/Gateways/GpApiConnector.cs
@@ -1,6 +1,7 @@
using GlobalPayments.Api.Builders;
using GlobalPayments.Api.Builders.RequestBuilder.GpApi;
using GlobalPayments.Api.Entities;
+using GlobalPayments.Api.Gateways.Interfaces;
using GlobalPayments.Api.Logging;
using GlobalPayments.Api.Mapping;
using GlobalPayments.Api.PaymentMethods;
@@ -12,7 +13,7 @@
using System.Reflection;
namespace GlobalPayments.Api.Gateways {
- internal partial class GpApiConnector : RestGateway, IPaymentGateway, IReportingService, ISecure3dProvider, IPayFacProvider, IFraudCheckService, IDeviceCloudService {
+ internal partial class GpApiConnector : RestGateway, IPaymentGateway, IReportingService, ISecure3dProvider, IPayFacProvider, IFraudCheckService, IDeviceCloudService, IFileProcessingService {
private const string IDEMPOTENCY_HEADER = "x-gp-idempotency";
private string _AccessToken;
@@ -89,6 +90,18 @@ public string ProcessPassThrough(JsonDoc rawRequest) {
}
#endregion
+ public FileProcessor ProcessFileUpload(FileProcessingBuilder builder) {
+ if (string.IsNullOrEmpty(AccessToken)) {
+ SignIn();
+ }
+ var request = new GpApiFileProcessingRequestBuilder().BuildRequest(builder, this);
+ if (request != null) {
+ var response = DoTransaction(request.Verb, request.Endpoint, request.RequestBody, request.QueryStringParams);
+ return GpApiMapping.MapFileProcessingResponse(response);
+ }
+ return null;
+ }
+
public void SignIn() {
AccessTokenInfo accessTokenInfo = GpApiConfig.AccessTokenInfo;
@@ -130,6 +143,11 @@ public void SignIn() {
string.IsNullOrEmpty(accessTokenInfo.MerchantManagementAccountID)) {
accessTokenInfo.MerchantManagementAccountID = response.MerchantManagementAccountID;
}
+ if (string.IsNullOrEmpty(accessTokenInfo.FileProcessingAccountName) &&
+ string.IsNullOrEmpty(accessTokenInfo.FileProcessingAccountID))
+ {
+ accessTokenInfo.FileProcessingAccountID = response.FileProcessingAccountID;
+ }
GpApiConfig.AccessTokenInfo = accessTokenInfo;
}
diff --git a/src/GlobalPayments.Api/Gateways/GpEcomConnector.cs b/src/GlobalPayments.Api/Gateways/GpEcomConnector.cs
index fce2b074..343c8587 100644
--- a/src/GlobalPayments.Api/Gateways/GpEcomConnector.cs
+++ b/src/GlobalPayments.Api/Gateways/GpEcomConnector.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using System.Text.RegularExpressions;
using GlobalPayments.Api.Builders;
using GlobalPayments.Api.Entities;
@@ -114,6 +115,25 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
MaskedValues = ProtectSensitiveData.HideValue("request.card.cvn.number", card.Cvn);
}
+
+ // Card block
+ if (builder.CardTypesBlocking != null) {
+ var cardTypes = builder.CardTypesBlocking;
+
+ var cardTypeBlock = et.SubElement(request, "blockcard");
+ if (cardTypes.Commercialcredit.HasValue) {
+ et.SubElement(cardTypeBlock, "commercialcredit", cardTypes.Commercialcredit.ToString().ToLower());
+ }
+ if (cardTypes.Commercialdebit.HasValue) {
+ et.SubElement(cardTypeBlock, "commercialdebit", cardTypes.Commercialdebit.ToString().ToLower());
+ }
+ if (cardTypes.Consumercredit.HasValue) {
+ et.SubElement(cardTypeBlock, "consumercredit", cardTypes.Consumercredit.ToString().ToLower());
+ }
+ if (cardTypes.Consumerdebit.HasValue) {
+ et.SubElement(cardTypeBlock, "consumerdebit", cardTypes.Consumerdebit.ToString().ToLower());
+ }
+ }
}
string hash = string.Empty;
@@ -534,18 +554,45 @@ public string SerializeRequest(AuthorizationBuilder builder) {
if (builder.HostedPaymentData != null) {
AlternativePaymentType[] PaymentTypes = builder.HostedPaymentData.PresetPaymentMethods;
HostedPaymentMethods[] HostedPaymentMethods = builder.HostedPaymentData.HostedPaymentMethods;
- if (PaymentTypes != null) {
- PaymentValues = string.Join("|", PaymentTypes);
+
+ if (HostedPaymentMethods != null) {
+ if (HostedPaymentMethods.ToList().Contains(Entities.HostedPaymentMethods.CARDS)) {
+ List cardItem = new List();
+ List Items = new List();
+ foreach (var hosted in HostedPaymentMethods) {
+ if (hosted == Entities.HostedPaymentMethods.CARDS) {
+ cardItem.Add(hosted);
+ }
+ else {
+ Items.Add(hosted);
+ }
+ }
+ if(cardItem.Count > 0) {
+ PaymentValues = string.Join("|", new string[] { string.Join("|", cardItem)});
+ if(Items.Count > 0) {
+ PaymentValues = string.Join("|", new string[] { PaymentValues, string.Join("|", Items) });
+ }
+ }
+ else {
+ PaymentValues = string.Join("|", Items);
+ }
+ }
+ else {
+ PaymentValues = string.Join("|", HostedPaymentMethods);
+ }
}
- if (HostedPaymentMethods != null)
- {
+ if (PaymentTypes != null) {
if (PaymentValues != null) {
- PaymentValues = string.Join("|", new string[] { PaymentValues, string.Join("|", HostedPaymentMethods) });
+ PaymentValues = string.Join("|", new string[] { PaymentValues, string.Join("|", PaymentTypes) });
}
else {
- PaymentValues = string.Join("|", HostedPaymentMethods);
+ PaymentValues = string.Join("|", PaymentTypes);
}
}
+ var blockCardTypes = builder.HostedPaymentData.BlockCardTypes?.ToList();
+ if(blockCardTypes != null) {
+ request.Set("BLOCK_CARD_TYPE", string.Join("|", blockCardTypes.Select(x => EnumConverter.GetDescription(x)))) ;
+ }
request.Set("CUST_NUM", builder.HostedPaymentData.CustomerNumber);
if (HostedPaymentConfig.DisplaySavedCards.HasValue && builder.HostedPaymentData.CustomerKey != null) {
request.Set("HPP_SELECT_STORED_CARD", builder.HostedPaymentData.CustomerKey);
@@ -628,7 +675,9 @@ public string SerializeRequest(AuthorizationBuilder builder) {
request.Set("HPP_BILLING_POSTALCODE", builder.BillingAddress.PostalCode);
request.Set("HPP_BILLING_COUNTRY", CountryUtils.GetNumericCodeByCountry(builder.BillingAddress.Country));
}
- request.Set("CUST_NUM", builder.CustomerId);
+ if (!request.Has("CUST_NUM")) {
+ request.Set("CUST_NUM", builder.CustomerId);
+ }
request.Set("VAR_REF", builder.ClientTransactionId);
request.Set("HPP_LANG", HostedPaymentConfig.Language);
request.Set("MERCHANT_RESPONSE_URL", HostedPaymentConfig.ResponseUrl);
@@ -702,7 +751,7 @@ public string SerializeRequest(AuthorizationBuilder builder) {
if (builder.DynamicDescriptor != null) {
request.Set("CHARGE_DESCRIPTION", builder.DynamicDescriptor);
}
- request.Set("SHA1HASH", GenerationUtils.GenerateHash(SharedSecret, toHash.ToArray()));
+ request.Set($"{ShaHashType.ToString()}HASH", GenerationUtils.GenerateHash(SharedSecret, ShaHashType, toHash.ToArray()));
return request.ToString();
}
public T ProcessReport(ReportBuilder builder) where T : class {
@@ -1028,7 +1077,7 @@ private Transaction MapResponse(string rawResponse, TransactionBuilder("exchangeratesourcetimestamp");
if (!string.IsNullOrEmpty(exchangeTimestamp)) {
- dccRateData.ExchangeRateSourceTimestamp = DateTime.ParseExact(exchangeTimestamp, "yyyyMMdd hh:mm", CultureInfo.InvariantCulture);
+ dccRateData.ExchangeRateSourceTimestamp = DateTime.ParseExact(exchangeTimestamp, "yyyyMMdd HH:mm", CultureInfo.InvariantCulture);
}
result.DccRateData = dccRateData;
diff --git a/src/GlobalPayments.Api/Gateways/Interfaces/IFileProcessingService.cs b/src/GlobalPayments.Api/Gateways/Interfaces/IFileProcessingService.cs
new file mode 100644
index 00000000..24bcafe9
--- /dev/null
+++ b/src/GlobalPayments.Api/Gateways/Interfaces/IFileProcessingService.cs
@@ -0,0 +1,12 @@
+using GlobalPayments.Api.Builders;
+using GlobalPayments.Api.Entities;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GlobalPayments.Api.Gateways.Interfaces
+{
+ public interface IFileProcessingService {
+ FileProcessor ProcessFileUpload(FileProcessingBuilder builder);
+ }
+}
diff --git a/src/GlobalPayments.Api/Gateways/PorticoConnector.cs b/src/GlobalPayments.Api/Gateways/PorticoConnector.cs
index 4e562ae1..c33bfef2 100644
--- a/src/GlobalPayments.Api/Gateways/PorticoConnector.cs
+++ b/src/GlobalPayments.Api/Gateways/PorticoConnector.cs
@@ -22,7 +22,7 @@ internal class PorticoConnector : XmlGateway, IPaymentGateway, IReportingService
public string UniqueDeviceId { get; set; }
public string SDKNameVersion { get; set; }
public bool SupportsOpenBanking => false;
- public bool IsSafDataSupported { get; set; }
+ public bool? IsSafDataSupported = null;
public PorticoConnector() {
}
@@ -32,8 +32,10 @@ public PorticoConnector() {
public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
var et = new ElementTree();
+ string transactionName = MapTransactionType(builder);
+
// build request
- var transaction = et.Element(MapTransactionType(builder));
+ var transaction = et.Element(transactionName);
var block1 = et.SubElement(transaction, "Block1");
if (builder.TransactionType.HasFlag(TransactionType.Sale) || builder.TransactionType.HasFlag(TransactionType.Auth)) {
if (builder.PaymentMethod.PaymentMethodType != PaymentMethodType.Gift && builder.PaymentMethod.PaymentMethodType != PaymentMethodType.ACH) {
@@ -64,6 +66,8 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
&& builder.PaymentMethod.PaymentMethodType != PaymentMethodType.Gift
&& builder.TransactionType != TransactionType.Tokenize
&& builder.TransactionType != TransactionType.Reversal
+ && transactionName != "CreditAdditionalAuth"
+ && transactionName != "CreditIncrementalAuth"
) {
var isCheck = (builder.PaymentMethod.PaymentMethodType == PaymentMethodType.ACH);
var holder = et.SubElement(block1, isCheck ? "ConsumerInfo" : "CardHolderData");
@@ -75,6 +79,10 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
et.SubElement(holder, isCheck ? "Zip" : "CardHolderZip", builder.BillingAddress.PostalCode);
}
+ if (builder.CustomerData != null) {
+ et.SubElement(holder, isCheck ? "EmailAddress" : "CardHolderEmail", builder.CustomerData.Email);
+ }
+
if (isCheck) {
var check = builder.PaymentMethod as eCheck;
if (!string.IsNullOrEmpty(check.CheckHolderName)) {
@@ -161,7 +169,7 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
et.SubElement(Secure3D, "ECI", secureEcom.Eci);
et.SubElement(Secure3D, "DirectoryServerTxnId", secureEcom.Xid);
}
- if (IsAppleOrGooglePay(secureEcom.PaymentDataSource))
+ if (IsAppleOrGooglePay(secureEcom.PaymentDataSource) && builder.TransactionType != TransactionType.Refund)
{
var WalletData = et.SubElement(block1, "WalletData");
et.SubElement(WalletData, "PaymentSource", secureEcom.PaymentDataSource);
@@ -171,13 +179,13 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
}
//WalletData Element
if ((CreditCardData.MobileType == MobilePaymentMethodType.APPLEPAY || CreditCardData.MobileType == MobilePaymentMethodType.GOOGLEPAY)
- && !string.IsNullOrEmpty(CreditCardData.PaymentSource) && IsAppleOrGooglePay(CreditCardData.PaymentSource))
+ && !string.IsNullOrEmpty(CreditCardData.PaymentSource) && IsAppleOrGooglePay(CreditCardData.PaymentSource) && builder.TransactionType != TransactionType.Refund)
{
var WalletData = et.SubElement(block1, "WalletData");
et.SubElement(WalletData, "PaymentSource", CreditCardData.PaymentSource);
et.SubElement(WalletData, "Cryptogram", CreditCardData.Cryptogram);
et.SubElement(WalletData, "ECI", CreditCardData.Eci);
- if (CreditCardData.MobileType != null)
+ if (CreditCardData.Token != null)
{
et.SubElement(WalletData, "DigitalPaymentToken", CreditCardData.Token);
block1.Remove("CardData");
@@ -371,6 +379,14 @@ public Transaction ProcessAuthorization(AuthorizationBuilder builder) {
}
}
+ if (builder.CommercialData != null)
+ {
+ var cd = builder.CommercialData;
+ var cpc = et.SubElement(block1, "CPCData");
+ et.SubElement(cpc, "CardHolderPONbr", cd.PoNumber);
+ et.SubElement(cpc, "TaxType", cd.TaxType.ToString());
+ et.SubElement(cpc, "TaxAmt", cd.TaxAmount);
+ }
// dynamic descriptor
et.SubElement(block1, "TxnDescriptor", builder.DynamicDescriptor);
@@ -522,8 +538,8 @@ public Transaction ManageTransaction(ManagementBuilder builder) {
et.SubElement(data, "DestinationCountryCode", cd.DestinationCountryCode);
et.SubElement(data, "InvoiceRefNbr", cd.VAT_InvoiceNumber);
et.SubElement(data, "OrderDate", cd.OrderDate?.ToString("yyyy-MM-ddTHH:mm:ss.FFFK"));
- et.SubElement(data, "VATTaxAmtFreight", cd.AdditionalTaxDetails?.TaxAmount ?? cd.TaxAmount);
- et.SubElement(data, "VATTaxRateFreight", cd.AdditionalTaxDetails?.TaxRate);
+ et.SubElement(data, "VATTaxAmtFreight", cd.VATTaxAmtFreight ?? cd.AdditionalTaxDetails?.TaxAmount ?? cd.TaxAmount);
+ et.SubElement(data, "VATTaxRateFreight", cd.VATTaxRateFreight ?? cd.AdditionalTaxDetails?.TaxRate );
// et.SubElement(data, "TaxTreatment", null);
// et.SubElement(data, "DiscountTreatment", null);
}
@@ -581,6 +597,17 @@ public Transaction ManageTransaction(ManagementBuilder builder) {
}
}
}
+ else
+ {
+ if (builder.TransactionType == TransactionType.BatchClose)
+ {
+ if (builder.batchDeviceId != null)
+ {
+ var batchDeviceId = builder.batchDeviceId.ToString();
+ transaction.Set("deviceId", batchDeviceId);
+ }
+ }
+ }
var response = DoTransaction(BuildEnvelope(et, transaction, builder.ClientTransactionId));
return MapResponse(response, builder.PaymentMethod);
@@ -666,9 +693,12 @@ private string BuildEnvelope(ElementTree et, Element transaction, string clientT
et.SubElement(header, "UniqueDeviceId", UniqueDeviceId);
et.SubElement(header, "SDKNameVersion", SDKNameVersion != null ? SDKNameVersion : "net;version=" + getReleaseVersion());
- Element safData = et.SubElement(header, "SAFData");
- et.SubElement(safData, "SAFIndicator", IsSafDataSupported ? "Y" : "N");
- et.SubElement(safData, "SAFOrigDT", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.FFFK"));
+
+ if (IsSafDataSupported != null) {
+ Element safData = et.SubElement(header, "SAFData");
+ et.SubElement(safData, "SAFIndicator", (bool)IsSafDataSupported ? "Y" : "N");
+ et.SubElement(safData, "SAFOrigDT", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.FFFK"));
+ }
// Transaction
var trans = et.SubElement(version1, "Transaction");
@@ -809,8 +839,11 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
var doc = new ElementTree(rawResponse).Get(MapReportType(reportType));
T rvalue = Activator.CreateInstance();
- if (reportType.HasFlag(ReportType.FindTransactions) | reportType.HasFlag(ReportType.Activity)) {
- Func hydrateTransactionSummary = (root) => {
+ if (reportType.HasFlag(ReportType.FindTransactions) | reportType.HasFlag(ReportType.Activity)) {
+ Func hydrateTransactionSummary = (root) =>
+ {
+ var headerElement = response.Get("Header");
+
var summary = new TransactionSummary {
AccountDataSource = root.GetValue("AcctDataSrc"),
Amount = root.GetValue("Amt"),
@@ -835,7 +868,7 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
PaymentType = root.GetValue("PaymentType"),
PoNumber = root.GetValue("CardHolderPONbr"),
ReferenceNumber = root.GetValue("RefNbr"),
- ResponseDate = root.GetValue("RspDT"),
+ ResponseDate = headerElement.GetValue("RspDT"),
ServiceName = root.GetValue("ServiceName"),
SettlementAmount = root.GetValue("SettlementAmt"),
ShippingAmount = root.GetValue("ShippingAmtInfo"),
@@ -843,7 +876,7 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
Status = root.GetValue("Status", "TxnStatus"),
TaxAmount = root.GetValue("TaxAmt", "TaxAmtInfo"),
TaxType = root.GetValue("TaxType"),
- TransactionDate = root.GetValue("TxnUtcDT", "ReqUtcDT"),
+ TransactionDate = root.GetValue("RspDT"),
TransactionId = root.GetValue("GatewayTxnId"),
TransactionStatus = root.GetValue("TxnStatus"),
Username = root.GetValue("UserName"),
@@ -939,7 +972,11 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
}
if (reportType.HasFlag(ReportType.TransactionDetail))
{
+ var reportDetailsElement = response.Get("ReportTxnDetail");
+
var summary = new TransactionSummary {
+ ResponseDate = response.GetValue("RspDT"),
+ TransactionDate = reportDetailsElement.GetValue("RspDT"),
TransactionId = response.GetValue("GatewayTxnId"),
SiteId = response.GetValue("SiteId"),
MerchantName = response.GetValue("MerchName"),
@@ -954,6 +991,7 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
MerchantAddr1 = response.GetValue("MerchAddr1"),
MerchantAddr2 = response.GetValue("MerchAddr2"),
MerchantCity = response.GetValue("MerchCity"),
+ MerchantState = response.GetValue("MerchState"),
MerchantZip = response.GetValue("MerchZip"),
MerchantPhone = response.GetValue("MerchPhone"),
TransactionStatus = response.GetValue("TxnStatus"),
@@ -970,14 +1008,92 @@ private T MapReportResponse(string rawResponse, ReportType reportType) where
CashBackAmount = response.GetValue("CashbackAmtInfo"),
CardHolderFirstName = response.GetValue("CardHolderFirstName"),
CardHolderLastName = response.GetValue("CardHolderLastName"),
+ Email = response.GetValue("CardHolderEmail"),
ConvenienceAmount = response.GetValue("ConvenienceAmtInfo"),
IssuerResponseCode = response.GetValue("IssuerRspCode", "RspCode"),
IssuerResponseMessage = response.GetValue("IssuerRspText", "RspText"),
IssuerTransactionId = response.GetValue("IssTxnId"),
SDKNameVersion = response.GetValue("SDKNameVersion"),
- InvoiceNumber = response.GetValue("InvoiceNbr"),
-
+ InvoiceNumber = response.GetValue("InvoiceNbr"), // not in the raw response
+ ShippingInvoiceNbr = response.GetValue("DirectMktInvoiceNbr"),
+ ShippingDay = response.GetValue("DirectMktShipDay"),
+ ShippingMonth = response.GetValue("DirectMktShipMonth"),
+ TaxAmount = response.GetValue("TaxAmt", "TaxAmtInfo", "CPCTaxAmt"),
+ SurchargeAmount = response.GetValue("SurchargeAmtInfo"),
+ Currency = response.GetValue("MerchCurrencyText"),
+ TaxType = response.GetValue("CPCTaxType"),
+ PoNumber = response.GetValue("CPCCardHolderPONbr"),
};
+ if (response.Has("CorporateData") && response.Has("CPCTaxType"))
+ {
+
+ bool ignoreCase = true;
+ string taxType = response.GetValue("CPCTaxType").Substring(response.GetValue("CPCTaxType").IndexOf('-') + 1, response.GetValue("CPCTaxType").Length-(response.GetValue("CPCTaxType").IndexOf('-') + 1));
+ if(response.Has("Visa"))
+ {
+ CommercialData commercialData = new CommercialData((TaxType)Enum.Parse(typeof(TaxType), taxType,ignoreCase))
+ {
+ SummaryCommodityCode = response.GetValue("SummaryCommodityCode"),
+ FreightAmount = response.GetValue("FreightAmt"),
+ DutyAmount = response.GetValue("DutyAmt"),
+ DestinationPostalCode = response.GetValue("DestinationPostalZipCode"),
+ OriginPostalCode = response.GetValue("ShipFromPostalZipCode"),
+ DestinationCountryCode = response.GetValue("DestinationCountryCode"),
+ VAT_InvoiceNumber = response.GetValue("InvoiceRefNbr"),
+ OrderDate = response.GetValue("OrderDate"),
+ PoNumber = response.GetValue("CPCCardHolderPONbr"),
+ };
+ summary.CommercialData = commercialData;
+
+ foreach (var lineItemDetail in response.GetAll("LineItemDetail"))
+ {
+ var lid = new CommercialLineItem
+ {
+ Description = lineItemDetail.GetValue("ItemDescription"),
+ ProductCode = lineItemDetail.GetValue("ProductCode"),
+ Quantity = lineItemDetail.GetValue("Quantity"),
+ UnitOfMeasure = lineItemDetail.GetValue("UnitOfMeasure"),
+ DiscountDetails = new DiscountDetails
+ {
+ DiscountAmount = lineItemDetail.GetValue("DiscountAmt"),
+ },
+ };
+
+ summary.CommercialData.AddLineItems(lid);
+ }
+ }
+ else //Mastercard
+ {
+ CommercialData commercialData = new CommercialData((TaxType)System.Enum.Parse(typeof(TaxType), taxType, ignoreCase))
+ {
+ SummaryCommodityCode = response.GetValue("SummaryCommodityCode"),
+ FreightAmount = response.GetValue("FreightAmt"),
+ DutyAmount = response.GetValue