Calculating MD5 hash for SagePay VPSSignature when using simulator

Share on Facebook

Recently I was working on a payment integration effort for an e-commerce site using the SagePay (formerly Protx) payment gateway. I had previously done similar a while ago and relied heavily on a then Protx released assembly - Protx.Vsp.dll.

Well it turns out, under its new incarnation as SagePay, they have released a new version of their gateway protocol (2.23). The Protx.Vsp.dll implemented the 2.22 protocol and so has been discontinued so I was really left in the cold. Because the architecture of the .net e-commerce site I was working on used a paymentService architecture which supported multiple payment gateways by implementing provider specific logic in provider implementations, I needed a library like Protx.Vsp.dll to abstract away all the fine workins of the SagePay protocol. So I started work on developing a new version of the Protx.Vsp.dll assembly named SaePay.Vsp.dll. When it is good to release, I might consider releasing it as open source.

However, in the process of building this library, I needed to validate the VPSSignature parameter returned as part of the response from the Server Registration Transaction. I did everything I thought was right as per the Sage Server Integration guide, but failed to match the signature, that is until a EUREKA moment explained below.

The text below is directly from the Sage Server Integration Documentation:

VPSSignature = MD5 signature of the concatenation of the values of: VPSTxId + VendorTxCode + Status + TxAuthNo + VendorName+ AVSCV2 + SecurityKey + AddressResult + PostCodeResult +CV2Result + GiftAid + 3DSecureStatus + CAVV + AddressStatus + PayerStatus + CardType + Last4Digits.

NOTE: MD5 value is returned in UPPER CASE

There were two main reasons my recomputed hash did not match the hash returned by the SagePay simulator.

1: The vendorName should be in lowercase

2. The AVSCV2 returned by the simulator is urlencoded as "ALL+MATCH". However, the value used to compute the signature is pre the url encoding step and therefore is "ALL MATCH". In your computation of the hah therefore, you will have to urldecode values which may contain spaces.

Hopefully if you adhere to the above two conditions, your generated hash should match the signature generated by the simulator. The code snippets below illustrate the steps required to recompute the Signature correctly.

	
	/// 
        /// Generates the VPS Signature from the parameters of the POST.
        /// 
        public virtual string GenerateVPSSignature(string SavedSecurityKey, string vendorName)
        {
	
	//These are test values only, gleaned from the SagePay Simulator.
	//In your code, you might have your own method of obtaining the Server Registration Transaction
	//reponse HTTP Post values from the inputStream.
	string VPSTxId ="{6725B26A-64EA-4BD2-9FB0-A4443FAEFDD4}";
        string VendorTxCode = "SCC1895636952-VSP-937837270";
        string StatusText = "OK";
        string TxAuthNo = "6525";
	
	//make sure this is all lowercase
        string vendorName = "xxxxxxxxxxxx";            

	//make sure to change 'ALL+MATCH' below to 'ALL MATCH'
	//best way is to use urldecode to reverse the urlencoding from Sagepay
	//as is implemented in the computation below
	string AvsCv2Text ="ALL+MATCH"; 
        string securityKey = SavedSecurityKey;
        string AddressResult = "MATCHED";
        string PostCodeResult = "MATCHED";
        string Cv2Result = "MATCHED";
        string GiftAid = "0";
        string _3DStatus = "OK";
        string CAVV ="MNJ8TIR4URD75O77NI6D9K";

	//Two fields below only present in paypal Transactions
        string AddressStatus = "";
        string PayerStatus = "";

        string CardType = "VISA";
        string Last4Digits = "5357";

        var builder = new StringBuilder();
            builder.Append(VPSTxId);
            builder.Append(VendorTxCode);
            builder.Append(Status);
            builder.Append(TxAuthNo);
            builder.Append(vendorName);
            builder.Append(HttpUtility.UrlDecode(AvsCv2Text));
            builder.Append(SavedSecurityKey);
            builder.Append(HttpUtility.UrlDecode(AddressResult));
            builder.Append(HttpUtility.UrlDecode(PostCodeResult));
            builder.Append(HttpUtility.UrlDecode(Cv2Result));
            builder.Append(GiftAid);
            builder.Append(_3Dstatus);
            builder.Append(CAVV);            
            builder.Append(HttpUtility.UrlDecode(AddressStatus));
            builder.Append(HttpUtility.UrlDecode(PayerStatus));       
            builder.Append(this.Parameters.CardType);
            builder.Append(this.Last4Digits);
            var hash = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(builder.ToString(), "MD5");
            return hash;
    }


Remember to add a using statement to your class file to import the System.Web.Security.FormsAuthentication namespace. The HashPasswordForStoringInConfigFile functiomn used in the code above is in that namespace. Also add a using statement for System.Web since the HttpUtility class resides in that namesoace.

You may then compare the generated hash with your previously saved hash in this manner:

 string recomputedSignature = GenerateVPSSignature(securityKey, vendorName);

//Assert
Assert.AreEqual(ResponseVPSSignature, recomputedSignature); 

Hope you find this useful.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListkick it on DotNetKicks.comTwitThis

Comments are closed

About Me

When not scratching my head for solutions to software challenges, I spend my time playing with my little boy - Michael Jnr.

Quotations

"Every day I remind myself that my inner and outer life are based on the labors of other men, living and dead, and that I must exert myself in order to give in the same measure as I have received and am still receiving"
Albert Einstein

Donate with PayPal - it

Calendar

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2005 - 2012

Search