From 104be7b4fc8c9bdb2f95e0688ba312c6e46a428c Mon Sep 17 00:00:00 2001 From: Dmitriy Benyuk Date: Tue, 13 Oct 2026 16:41:17 +0300 Subject: [PATCH 1/3] Do not mark ERP connection as failed on SOAP faults A business error returned by the ERP (e.g. NAV "wrong price" or locking errors) arrives as a FaultException via the Dynamicweb connector service. It was classified as a connectivity error, marking the endpoint as down for all users/sessions until the next ping interval elapsed. SOAP faults now fail only the current request and are not retried. Co-Authored-By: Claude Opus 5.5 --- .../Connectors/Connector.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs index d1fbd41..27c9748 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs @@ -9,6 +9,7 @@ using System.Collections.Concurrent; using System.Net; using System.Net.Http; +using System.ServiceModel; using System.Xml; using static Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Notifications.Communication; @@ -308,6 +309,12 @@ private static void HandleException(ConnectorBase connector, Settings settings, { httpStatusCode = HttpStatusCode.OK; bool skipError = false; + //Do not mark the connection as failed on SOAP faults (the service responded, but the ERP returned a business error) + if (IsSoapFault(ex)) + { + skipError = true; + retry = false; + } //Do not ping Endpoints with Internal Server Error 500 (that means that the request was received but generating response failed) if (ex?.InnerException is not null) { @@ -355,6 +362,18 @@ private static void HandleException(ConnectorBase connector, Settings settings, } } + private static bool IsSoapFault(Exception ex) + { + for (var current = ex; current is not null; current = current.InnerException) + { + if (current is FaultException) + { + return true; + } + } + return false; + } + /// /// Retrieves a PDF document as a Base64-encoded string based on the specified request and submission type. /// From 7a3d4c7c2261e3a668999714500ae612194415a3 Mon Sep 17 00:00:00 2001 From: Dmitriy Benyuk Date: Tue, 13 Oct 2026 17:36:54 +0300 Subject: [PATCH 2/3] bump 10.21.10 --- .../Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj index 840a3f8..c82557b 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj @@ -1,6 +1,6 @@  - 10.21.9 + 10.21.10 1.0.0.0 Live Integration Live Integration From 8dc1087294b453ea619882242b2b8dedd3bc5912 Mon Sep 17 00:00:00 2001 From: Dmitriy Benyuk Date: Thu, 15 Oct 2026 10:29:13 +0300 Subject: [PATCH 3/3] Address review: classify ERP faults precisely and keep retry - Only treat a fault as an ERP business error when the ERP itself returned it: a FaultException passed through by the connector service, or a connector-service ExceptionDetail chain containing an HTTP 500 WebException (BC OAuth path). Connector-service internal faults (InternalServiceFault, e.g. ERP unreachable, timeouts, config errors) still mark the endpoint down. - Keep the existing retry behavior for business faults. - Report business faults as HttpStatusCode.InternalServerError, matching the Endpoint path, so GetProductsInfo callers apply the same 500 handling. - Add explicit System.ServiceModel.Primitives package reference. Co-Authored-By: Claude Opus 5.5 --- .../Connectors/Connector.cs | 46 +++++++++++++++++-- ...Ecommerce.DynamicwebLiveIntegration.csproj | 1 + 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs index 27c9748..38dd622 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs @@ -309,11 +309,11 @@ private static void HandleException(ConnectorBase connector, Settings settings, { httpStatusCode = HttpStatusCode.OK; bool skipError = false; - //Do not mark the connection as failed on SOAP faults (the service responded, but the ERP returned a business error) - if (IsSoapFault(ex)) + //Do not mark the connection as failed when the ERP itself returned a fault (business error), report it as 500 like the Endpoint path does + if (IsErpBusinessFault(ex)) { + httpStatusCode = HttpStatusCode.InternalServerError; skipError = true; - retry = false; } //Do not ping Endpoints with Internal Server Error 500 (that means that the request was received but generating response failed) if (ex?.InnerException is not null) @@ -362,11 +362,47 @@ private static void HandleException(ConnectorBase connector, Settings settings, } } - private static bool IsSoapFault(Exception ex) + /// + /// Determines whether the exception is a fault returned by the ERP itself (e.g. a NAV/BC business error), + /// as opposed to a failure of the connector service or of its connection to the ERP. + /// + private static bool IsErpBusinessFault(Exception ex) { for (var current = ex; current is not null; current = current.InnerException) { - if (current is FaultException) + //Connector service internal error with exception details (DebugInfo=True in the connector service config) + if (current is FaultException detailFault) + { + return IsErpInternalServerError(detailFault.Detail); + } + //Fault passed through from the ERP, or connector service internal error without exception details + if (current is FaultException fault) + { + return !IsInternalServiceFault(fault.Code); + } + } + return false; + } + + private static bool IsInternalServiceFault(FaultCode code) + { + for (var current = code; current is not null; current = current.SubCode) + { + if (string.Equals(current.Name, "InternalServiceFault", StringComparison.Ordinal)) + { + return true; + } + } + return false; + } + + private static bool IsErpInternalServerError(ExceptionDetail detail) + { + //The connector service received an HTTP 500 response from the ERP (e.g. a SOAP fault from the BC OAuth endpoint) + for (var current = detail; current is not null; current = current.InnerException) + { + if (string.Equals(current.Type, typeof(WebException).FullName, StringComparison.Ordinal) && + !string.IsNullOrEmpty(current.Message) && current.Message.Contains("(500)")) { return true; } diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj index c82557b..bab04e3 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj @@ -28,6 +28,7 @@ +