/// <summary>
        /// Sends a CyberSource transaction request.
        /// </summary>
        /// <param name="config">Configuration object to use.</param>
        /// <param name="requestMessage">RequestMessage object containing the request.</param>
        /// <returns>ReplyMessage containing the reply.</returns>
        public static ReplyMessage RunTransaction(
            Configuration config, RequestMessage requestMessage)
        {
            Logger logger = null;
            TransactionProcessorClient proc = null;

            try
            {
                DetermineEffectiveMerchantID(ref config, requestMessage);
                SetVersionInformation(requestMessage);
                logger = PrepareLog(config);
                SetConnectionLimit(config);


                CustomBinding currentBinding = getWCFCustomBinding(config);


                //Setup endpoint Address with dns identity
                AddressHeaderCollection headers         = new AddressHeaderCollection();
                EndpointAddress         endpointAddress = new EndpointAddress(new Uri(config.EffectiveServerURL), EndpointIdentity.CreateDnsIdentity(config.EffectivePassword), headers);

                //Get instance of service
                using (proc = new TransactionProcessorClient(currentBinding, endpointAddress))
                {
                    // set the timeout
                    TimeSpan timeOut = new TimeSpan(0, 0, 0, config.Timeout, 0);
                    currentBinding.SendTimeout = timeOut;

                    //add certificate credentials
                    string keyFilePath = Path.Combine(config.KeysDirectory, config.EffectiveKeyFilename);

                    X509Certificate2 merchantCert = null;
                    X509Certificate2 cybsCert     = null;
                    DateTime         dateFile     = File.GetLastWriteTime(keyFilePath);
                    if (config.CertificateCacheEnabled)
                    {
                        if (!merchantIdentities.ContainsKey(config.MerchantID) || IsMerchantCertExpired(logger, config.MerchantID, dateFile, merchantIdentities))
                        {
                            if (logger != null)
                            {
                                logger.LogInfo("Loading certificate for merchantID " + config.MerchantID);
                            }

                            X509Certificate2Collection collection = new X509Certificate2Collection();
                            collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

                            X509Certificate2 newMerchantCert = null;
                            X509Certificate2 newCybsCert     = null;

                            foreach (X509Certificate2 cert1 in collection)
                            {
                                if (cert1.Subject.Contains(config.MerchantID))
                                {
                                    newMerchantCert = cert1;
                                }

                                if (cert1.Subject.Contains(CYBS_SUBJECT_NAME))
                                {
                                    newCybsCert = cert1;
                                }
                            }
                            CertificateEntry newCert = new CertificateEntry
                            {
                                ModifiedTime = dateFile,
                                CybsCert     = newCybsCert,
                                MerchantCert = newMerchantCert
                            };
                            merchantIdentities.AddOrUpdate(config.MerchantID, newCert, (x, y) => newCert);
                        }
                        merchantCert = GetOrFindValidMerchantCertFromStore(config.MerchantID, merchantIdentities);
                        if (config.UseSignedAndEncrypted)
                        {
                            cybsCert = GetOrFindValidCybsCertFromStore(config.MerchantID, merchantIdentities);
                        }
                    }
                    else
                    {
                        // Changes for SHA2 certificates support
                        X509Certificate2Collection collection = new X509Certificate2Collection();
                        collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

                        foreach (X509Certificate2 cert1 in collection)
                        {
                            if (cert1.Subject.Contains(config.MerchantID))
                            {
                                merchantCert = cert1;
                                break;
                            }
                        }

                        if (config.UseSignedAndEncrypted)
                        {
                            foreach (X509Certificate2 cert2 in collection)
                            {
                                //Console.WriteLine(cert1.Subject);
                                if (cert2.Subject.Contains(CYBERSOURCE_PUBLIC_KEY))
                                {
                                    cybsCert = cert2;
                                    break;
                                }
                            }
                        }
                    }

                    if (merchantCert == null)
                    {
                        throw new ApplicationException(
                                  "CONFIGURATION OR CODE BUG:  merchant certificate is missing, check the p12 file");
                    }
                    //Set protection level to sign only
                    proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
                    proc.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
                    proc.ClientCredentials.ClientCertificate.Certificate         = merchantCert;
                    proc.ClientCredentials.ServiceCertificate.DefaultCertificate = merchantCert;

                    if (config.UseSignedAndEncrypted)
                    {
                        if (cybsCert == null)
                        {
                            throw new ApplicationException(
                                      "CONFIGURATION OR CODE BUG:  cybs certificate is missing, check the p12 file");
                        }

                        //Set protection level to sign & encrypt only
                        proc.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
                        proc.ClientCredentials.ServiceCertificate.DefaultCertificate = cybsCert;
                    }
                    // Changes for NGT-3035
                    XmlNode req = SerializeObjectToXmlNode(requestMessage);
                    if (logger != null)
                    {
                        logger.LogRequest(req, config.Demo);
                    }

                    ReplyMessage reply = proc.runTransaction(requestMessage);
                    XmlNode      rep   = SerializeObjectToXmlNode(reply);
                    if (logger != null)
                    {
                        logger.LogReply(rep, config.Demo);
                    }

                    return(reply);
                }
            }
            catch (Exception e)
            {
                if (logger != null)
                {
                    logger.LogException(e);
                }
                if (proc != null)
                {
                    proc.Abort();
                }
                throw;
            }
            finally
            {
                if (proc != null)
                {
                    proc.Close();
                }
            }
        }
        /// <summary>
        /// Sends a CyberSource transaction request.
        /// </summary>
        /// <param name="request">XmlDocument object containing the request.</param>
        /// <param name="config">Configuration object to use.</param>
        /// <returns>XmlDocument object containing the reply.</returns>
        public static XmlDocument RunTransaction(
            Configuration config, XmlDocument request)
        {
            Logger logger = null;
            string nspace = null;

            try
            {
                nspace = GetRequestNamespace(request);
                DetermineEffectiveMerchantID(ref config, request, nspace);
                SetVersionInformation(request, nspace);
                logger = PrepareLog(config);

                if (string.IsNullOrEmpty(nspace))
                {
                    throw new ApplicationException(
                              REQUEST_MESSAGE + " is missing in the XML document.");
                }


                SetConnectionLimit(config);

                if (logger != null)
                {
                    logger.LogRequest(request, config.Demo);
                }

                // obtain a copy of the request document enclosed in a SOAP envelope
                XmlDocument doc = SoapWrap(request, nspace);

                //Get the X509 cert and sign the SOAP Body
                string           keyFilePath  = Path.Combine(config.KeysDirectory, config.EffectiveKeyFilename);
                X509Certificate2 merchantCert = null;
                X509Certificate2 cybsCert     = null;
                DateTime         dateFile     = File.GetLastWriteTime(keyFilePath);
                if (config.CertificateCacheEnabled)
                {
                    if (!merchantIdentities.ContainsKey(config.MerchantID) || IsMerchantCertExpired(logger, config.MerchantID, dateFile, merchantIdentities))
                    {
                        if (logger != null)
                        {
                            logger.LogInfo("Loading certificate for merchantID " + config.MerchantID);
                        }

                        X509Certificate2Collection collection = new X509Certificate2Collection();
                        collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

                        X509Certificate2 newMerchantCert = null;
                        X509Certificate2 newCybsCert     = null;

                        foreach (X509Certificate2 cert1 in collection)
                        {
                            if (cert1.Subject.Contains(config.MerchantID))
                            {
                                newMerchantCert = cert1;
                            }

                            if (cert1.Subject.Contains(CYBS_SUBJECT_NAME))
                            {
                                newCybsCert = cert1;
                            }
                        }
                        CertificateEntry newCert = new CertificateEntry
                        {
                            ModifiedTime = dateFile,
                            CybsCert     = newCybsCert,
                            MerchantCert = newMerchantCert
                        };
                        merchantIdentities.AddOrUpdate(config.MerchantID, newCert, (x, y) => newCert);
                    }
                    merchantCert = GetOrFindValidMerchantCertFromStore(config.MerchantID, merchantIdentities);
                    if (config.UseSignedAndEncrypted)
                    {
                        cybsCert = GetOrFindValidCybsCertFromStore(config.MerchantID, merchantIdentities);
                    }
                }
                else
                {
                    X509Certificate2Collection collection = new X509Certificate2Collection();
                    collection.Import(keyFilePath, config.EffectivePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
                    foreach (X509Certificate2 cert1 in collection)
                    {
                        if (cert1.Subject.Contains(config.MerchantID))
                        {
                            merchantCert = cert1;
                            break;
                        }
                    }
                    if (config.UseSignedAndEncrypted)
                    {
                        foreach (X509Certificate2 cert1 in collection)
                        {
                            //Console.WriteLine(cert1.Subject);
                            if (cert1.Subject.Contains("CyberSource_SJC_US"))
                            {
                                cybsCert = cert1;
                                break;
                            }
                        }
                    }
                }

                if (merchantCert == null)
                {
                    throw new ApplicationException(
                              "CONFIGURATION OR CODE BUG:  merchant certificate is missing, check the p12 file");
                }
                SignDocument(merchantCert, doc);

                if (config.UseSignedAndEncrypted)
                {
                    if (cybsCert == null)
                    {
                        throw new ApplicationException(
                                  "CONFIGURATION OR CODE BUG:  cybs certificate is missing, check the p12 file");
                    }
                    encryptDocument(cybsCert, doc);
                }

                // convert the document into an array of bytes using the
                // encoding specified in the XML declaration line.
                Encoding enc          = GetEncoding(doc);
                byte[]   requestBytes = enc.GetBytes(doc.OuterXml);


                // create an HttpWebRequest object and set its properties
                HttpWebRequest httpRequest
                    = (HttpWebRequest)WebRequest.Create(
                          config.EffectiveServerURL);
                httpRequest.Method        = "POST";
                httpRequest.ContentLength = requestBytes.Length;
                httpRequest.UserAgent     = ".NET XML";

                // set the timeout
                httpRequest.Timeout = config.Timeout * 1000;

                if (mProxy != null)
                {
                    // assign our pre-created WebProxy object to the
                    // HttpWebRequest object's Proxy property.
                    httpRequest.Proxy = mProxy;
                }

                // obtain the request stream and write the byte array to it.
                Stream stream = httpRequest.GetRequestStream();
                stream.Write(requestBytes, 0, requestBytes.Length);
                stream.Close();

                // send request and get response.
                WebResponse webResponse = httpRequest.GetResponse();

                // read returned XML document.
                XmlDocument reply = ReadXml(webResponse);

                XmlDocument unwrapped = SoapUnwrap(reply, nspace);

                if (logger != null)
                {
                    logger.LogReply(unwrapped, config.Demo);
                }

                // return the XML document without the SOAP envelope.
                return(unwrapped);
            }
            catch (WebException we)
            {
                // if we got HTTP status 500 (Internal Server Error), it could
                // mean, the server threw a fault, in which case, we load the
                // xml document from the HTTP body and throw a FaultException.

                // The status would be ProtocolError if we did get HTTP 500
                // and Response should not be null but we check for null just
                // in case.
                if (we.Status == WebExceptionStatus.ProtocolError &&
                    we.Response != null)
                {
                    HttpWebResponse response = (HttpWebResponse)we.Response;

                    // InternalServerError corresponds to HTTP 500.  And we
                    // proceed only if there's anything in the response body.
                    // That is, the contentLength is greater than zero.
                    if (response.StatusCode
                        == HttpStatusCode.InternalServerError &&
                        response.ContentLength > 0)
                    {
                        try
                        {
                            // read the fault document and throw a
                            // FaultException.
                            FaultException fe
                                = new FaultException(
                                      ReadXml(response), nspace);
                            if (logger != null)
                            {
                                logger.LogFault(fe.LogString);
                            }
                            throw;
                        }
                        catch (XmlException)
                        {
                            if (logger != null)
                            {
                                logger.LogException(we);
                            }

                            // the response body is not a valid xml document.
                            // It is therefore not a fault.  Rethrow the
                            // WebException object.
                            throw;
                        }
                    }
                }

                if (logger != null)
                {
                    logger.LogException(we);
                }

                // the server did not throw a fault.  Rethrow the WebException
                // object.
                throw;
            }
            catch (Exception e)
            {
                if (logger != null)
                {
                    logger.LogException(e);
                }
                throw;
            }
        }