How to integrate First Data Gateway e4 with .NET WCF?

Recently I worked on a project that needed integration with First Data Getaway e4 payment processing. Starting from v12 First Data introduced HMAC hash security. I found .NET REST sample, but there is nothing for WCF or Web Services. This new HMAC security feature makes usage of Web Service a little challenging. I hope this guide will help somebody.

Step 1. Add service reference:


WCF: Add Service Reference

WCF: Add Service Reference Step 2

Step 2. Create demo account

Step 3. Use following code wrapper to charge a card:

public class Merchant
{
    private readonly string _gatewayId;
    private readonly string _password;
    private readonly string _keyId;
    private readonly string _hmac;
    private readonly bool _isDemo;

    private const string ProdUrl = "https://api.globalgatewaye4.firstdata.com/transaction/v12";
    private const string TestUrl = "https://api.demo.globalgatewaye4.firstdata.com/transaction/v12";


    public Merchant(string gatewayId, string password, string hmac, string keyId, bool isDemo = true)
    {
        _gatewayId = gatewayId;
        _password = password;
        _hmac = hmac;
        _keyId = keyId;
        _isDemo = isDemo;
    }

    public MerchantResponse Charge(int orderId, string cardHoldersName, string cardNumber, decimal amount, 
        int expirationMonth, int expirationYear, int ccv, string address, string city, string state, string zip)
    {            
        var client = new ServiceSoapClient(new BasicHttpBinding(BasicHttpSecurityMode.Transport), 
            new EndpointAddress(_isDemo ? TestUrl : ProdUrl));                                           
        client.ChannelFactory.Endpoint.Behaviors.Add(new HmacHeaderBehaivour(_hmac,_keyId));            

        TransactionResult result = client.SendAndCommit(new Transaction
                                                {                                                                        
                                                    ExactID = _gatewayId,
                                                    Password = _password,
                                                    Transaction_Type = "00",                                                                        
                                                    Card_Number = cardNumber,
                                                    CardHoldersName = cardHoldersName,                                                                                                                                                
                                                    DollarAmount = amount.ToString("F"),
                                                    Expiry_Date = string.Format("{0:D2}{1}",expirationMonth,expirationYear),
                                                    Customer_Ref = orderId.ToString(),
                                                    VerificationStr1 = string.Format("{0}|{1}|{2}|{3}|US",address,zip,city,state),
                                                    VerificationStr2 = ccv.ToString()
                                                });
        var response = new MerchantResponse
                        {
                            IsTransactionApproved = result.Transaction_Approved,
                            IsError = result.Transaction_Error
                        };
        if (!result.Transaction_Approved && !result.Transaction_Error)
        {
            response.Message = string.Format("Error {0}: {1}", result.Bank_Resp_Code, result.Bank_Message);
        }
        if (!result.Transaction_Approved && result.Transaction_Error)
        {
            response.Message = string.Format("Error {0}: {1}",result.EXact_Resp_Code,result.EXact_Message);
        }
        if (result.Transaction_Approved)
        {
            response.Message = result.Authorization_Num;
        }
        return response;
    }               

    class HmacHeaderBehaivour: IEndpointBehavior
    {
        private readonly string _hmac;
        private readonly string _keyId;

        public HmacHeaderBehaivour(string hmac, string keyId)
        {
            _hmac = hmac;
            _keyId = keyId;
        }

        public void Validate(ServiceEndpoint endpoint)
        {                
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {                
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {                
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(new HmacHeaderInspector(_hmac,_keyId));                                
        }
    }      
         

    class HmacHeaderInspector: IClientMessageInspector
    {
        private readonly string _hmac;
        private readonly string _keyId;

        public HmacHeaderInspector(string hmac,string keyId)
        {
            _hmac = hmac;
            _keyId = keyId;
        }

        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {                                                                
            MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
            request = buffer.CreateMessage();
            Message msg = buffer.CreateMessage();                
            ASCIIEncoding encoder = new ASCIIEncoding();
                
            var sb = new StringBuilder();
            var xmlWriter = XmlWriter.Create(sb, new XmlWriterSettings
                                                    {
                                                    OmitXmlDeclaration  = true
                                                    });
            var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter);
            msg.WriteStartEnvelope(writer);
            msg.WriteStartBody(writer);
            msg.WriteBodyContents(writer);                                
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();
            writer.Flush();

            string body = sb.ToString().Replace(" />","/>");

            byte[] xmlByte = encoder.GetBytes(body);
            SHA1CryptoServiceProvider sha1Crypto = new SHA1CryptoServiceProvider();
            string hash = BitConverter.ToString(sha1Crypto.ComputeHash(xmlByte)).Replace("-", "");
            string hashedContent = hash.ToLower();

            //assign values to hashing and header variables
            string time = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
            string hashData = "POST\ntext/xml; charset=utf-8\n" + hashedContent + "\n" + time + "\n/transaction/v12";
            //hmac sha1 hash with key + hash_data
            HMAC hmacSha1 = new HMACSHA1(Encoding.UTF8.GetBytes(_hmac)); //key
            byte[] hmacData = hmacSha1.ComputeHash(Encoding.UTF8.GetBytes(hashData)); //data
            //base64 encode on hmac_data
            string base64Hash = Convert.ToBase64String(hmacData);

            HttpRequestMessageProperty httpRequestMessage;
            object httpRequestMessageObject;

            if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
            {
                httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
                httpRequestMessage.Headers["X-GGe4-Content-SHA1"] = hashedContent;
                httpRequestMessage.Headers["X-GGe4-Date"] = time;
                httpRequestMessage.Headers["Authorization"] = "GGE4_API " + _keyId + ":" + base64Hash;
            }
            else
            {
                httpRequestMessage = new HttpRequestMessageProperty();
                httpRequestMessage.Headers["X-GGe4-Content-SHA1"] = hashedContent;
                httpRequestMessage.Headers["X-GGe4-Date"] = time;                    
                httpRequestMessage.Headers["Authorization"] = "GGE4_API " + _keyId + ":" + base64Hash;
                request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
            }                
            return null;
        }

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {                
        }
    }
}

public class MerchantResponse
{
    public bool IsTransactionApproved { get; set; }
    public bool IsError { get; set; }
    public string Message { get; set; }
}

As you can see "Charge" method is simple, but HMAC requirement makes it challenging. HMAC hash is calculated based on SOAP request body, which is not accessible through ServiceSoapClient. Also we need to modify HTTP request header. Both things can be done using IClientMessageInspector or IClientMessageFormatter. In my implementation I used IClientMessageInspector, since it is a little easier to integrate it with ServiceSoapClient. I didn't find any easy way to get serialized SOAP body of the request, so using Fiddler I came up with following magic code:


var sb = new StringBuilder();
var xmlWriter = XmlWriter.Create(sb, new XmlWriterSettings
                                        {
                                        OmitXmlDeclaration  = true
                                        });
var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter);
msg.WriteStartEnvelope(writer);
msg.WriteStartBody(writer);
msg.WriteBodyContents(writer);                                
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
writer.Flush();

string body = sb.ToString().Replace(" />","/>");

The code above gives me serialized SOAP envelope without SOAP headers string. This string is used for HMAC calculation and at the end X-GGe4-Content-SHA1, X-GGe4-Date, Authorization are added to HTTP header.

Tips: You might get 401 errors. Use Fiddler to see actual error. Make sure that you saved generated HMAC key (generate button doesn't save it).


Posted on Tuesday, January 07, 2014 by | Comments (16) | Add Comment

Comments

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

I was beating my head against the wall on this one, no help from FirstData at all. You sir, are the man.

Posted on 2/6/2014 7:53:51 AM by Scott #
Gravatar

It took me a while to figure it out. I am glad that it helps somebody.

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

This post was a life saver. Thanks so much for sharing!

Posted on 4/9/2014 7:07:03 AM by Joe #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

How we can get orderId for making charge transaction?

Posted on 5/20/2014 4:51:41 AM by Hayk #
Gravatar

It should be you internal orderId. Internally you should keep track of all orders and by orderId you can pull up specific order details.

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

If you're interested in making a some money and helping to implement this into our current site, please email me. I've been working on this and smashing my head also. Even tried hiring a freelancer and he's just been more frustrating. My client is really hoping to get this done within the next week. If you're interested please let me know ASAP.

Posted on 6/16/2014 1:41:39 PM by Bryan #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

I was searching for first Data Implementation, and found this post its really helpful. But I need to implement the first Data payment processing using java framework with Soap.
I tested version 14 first Data in Soap UI , its showing date header missing !!!!!!!!!!

Can You Help with this ?

Posted on 12/2/2014 4:58:38 AM by Nandhini #
Gravatar

I am sorry, I am not a java expert.

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

This post is really helpful. Thank You.

Posted on 12/2/2014 5:02:00 AM by Aishwarya #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

I got error under ServiceSoapClient then how can i solve it

Posted on 1/21/2015 2:30:00 AM by alpesh #
Gravatar

What kind of error? Can you provide a stack trace?

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

i got error in Console Application 'The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was ''.'

Posted on 1/25/2015 9:00:01 PM by munish kumar #
Gravatar

You can debug HTTP requests with Fiddler. Also make sure that you use correct user name and password.

Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Viktor, Thanks for posting the code. Working perfectly for me for Version 14 with .Net

Posted on 2/9/2015 11:52:19 AM by Kondraganti #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Believe me you have saved me a ton of time and effort, thanks Viktar. Appreciate the content you've shared.

Can you also post a sample usage of that Merchant class and the Charge method. I am getting the error
"The HTTP request is unauthorized with client authentication scheme 'Anonymous'."

I guess its because of the value that I am passing into hmac and keyId paramters of the constuctor. Can you elaborate of this and it will be great if you post a sample code to use it the right way.

Once thanks for the post.

Posted on 2/12/2015 2:53:14 AM by Pramod M #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Hi Viktar,
Please ignore my previous question. The error was because I was trying v14 whereas the demo settings does not allow me to use that it defaults to v12. SO I regenerated the hmac key and it seems to work fine. Your code works like charm. Thanks again.

Posted on 2/12/2015 3:46:22 AM by Pramod M #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Can you please tell me how can i get the wsdl file. Any help will be appreciated. its urgent

Posted on 2/27/2015 12:21:16 AM by parshant #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Thanks for some very good info. I'm working on an implementation and unfortunately am getting "Invalid signature received" when I look at the response in Fiddler. Any ideas? It looks like the "base64Hash" value is different from what is generated in Content Digest on First Data's site. Is that supposed to match? TIA for any help!

Posted on 3/6/2015 5:48:30 PM by Jonathan Black #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

I have integrated your code and followed all the instruction but i am getting

The remote server returned an error: (401) Unauthorized.
[MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was ''.]

If i used same user name and password with v11 everything working fine. But now i need to integrate Telecheck that is the reason i need to integrate v13 or v14 because that is supported in that version only.

Please help me how can i resolve this?

Posted on 4/1/2015 11:09:08 PM by Hemant Yadav #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

Can anyone provide a sample of the values used to call the
initial Merchant method? I don't know what to put for the

gatewayId,
hmac
keyId
values?

Posted on 6/9/2015 7:42:40 AM by John #
Gravatar

Re: How to integrate First Data Gateway e4 with .NET WCF?

How and where to call this method BeforeSendRequest() in code ?

Posted on 8/5/2015 5:22:32 AM by abdul #
Gravatar

It is automatically after you call client.SendAndCommit, but before actual http call. It behaves this way since it added to Endpoint Behaviors.

New Comment

Your Name:
Email (for internal use only):
Comment:
 
Code above:

Categories

Recent Tweets

  • Simon Ince's Blog: Hierarchies with HierarchyID in SQL 2008 http://t.co/xSDwiF6rRS.
  • Visual Studio 2010 WAS painfully slow - CodeProject http://t.co/Usba1x6CZy
  • VisualSVN Subversion Server and Git | SubGit Blog http://t.co/S2FwsNKD2m

Valid HTML5