Request.form variables are not found even though they should be there

I'm trying to get the form variables from an http post but they do not appear to be there.

I'm coding a payment pages solution using sagepay. After payment on sagepay website they redirect the user to my notification page. The page is loading but the form variables are not present as I'm told they should be. Hundreds of others use this payment gateway so im expecting the problem to be my side but cant figure out if im doing anything wrong.

SagePay are loading my page called checkout_notification.aspx. The are sending a post that contains the following in the http header.

VPSProtocol=2.23&TxType=PAYMENT&VendorTxCode=astechnology-201043221-82305&VPSTxId={6AFBCDE2-C5AE-431D
-AB3D-2216AF81AC0F}&Status=OK&TxAuthNo=4523&AVSCV2=ALL+MATCH&AddressResult=MATCHED&PostCodeResult
=MATCHED&CV2Result=MATCHED&GiftAid=0&3DSecureStatus=OK&CAVV=MNNU1KFL0FSZ9BF9BYD1NB&CardType=VISA&
Last4Digits=8832&VPSSignature=E2A7AF68D7CF3FA01CBC76BAEAD920BC

My page has the html

<%@ Page Language="C#" AutoEventWireup="true" ValidateRequest="false" Debug="false" CodeFile="Checkout_Notification.aspx.cs"
Inherits="MB.ASTechnology.UI.Checkout_Notification" %>

<html>
<head id="Head1" runat="server" />
<body />
</html>


My code behind is as follows.

Please not that the form variables of status, VendorTxCode and VPSTxId are all blank.




using System;
using System.Data;
using System.Configuration;
using System.Collections.Specialized;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using MB.ASTechnology.BLL.Store;
using MB.ASTechnology.BLL.SiteContent;

namespace MB.ASTechnology.UI
{

    public partial class Checkout_Notification : BasePage
   {

      protected void Page_Load(object sender, EventArgs e)
      {
       
        string strStatus = "";
        string strVendorTxCode = "";
        string strVPSTxId = "";
        string strSecurityKey = "";
        string strStatusDetail = "";
        string strTxAuthNo = "";
        string strAVSCV2 = "";
        string strAddressResult = "";
        string strPostCodeResult = "";
        string strCV2Result = "";
        string strGiftAid = "";
        string str3DSecureStatus = "";
        string strCAVV = "";
        string strAddressStatus = "";
        string strPayerStatus = "";
        string strCardType = "";
        string strLast4Digits = "";
        string strMySignature = "";
        string strVPSSignature = "";
        string strMessage = "";
        string strDBStatus = "";
        string strRedirectPage = "";
        Int32 orderid = 0;

        SitePage.InsertDevLog("Checkout_notification code entered");

        if (!String.IsNullOrEmpty(Request.Form["Status"]))
            strStatus = SagePay.CleanInput(Request.Form["Status"].ToString(), "Text");

        if (!String.IsNullOrEmpty(Request.Form["VendorTxCode"]))
        {
            strVendorTxCode = SagePay.CleanInput(Request.Form["VendorTxCode"].ToString(), "VendorTxCode");
            orderid = OrderTx.GetOrderTxOrderIDByVendorTxID(strVendorTxCode);
        }

        if (!String.IsNullOrEmpty(Request.Form["VPSTxId"]))
            strVPSTxId = SagePay.CleanInput(Request.Form["VPSTxId"].ToString(), "Text");

        SitePage.InsertDevLog("Checkout_notification: Got form variables" + "Status=" + strStatus);

        int loop1;
        NameValueCollection coll;
        string req = "";

        //Load Form variables into NameValueCollection variable.
        coll = Request.Form;
        // Get names of all forms into a string array.
        SitePage.InsertDevLog("Checkout_notification: Request.Form.Count=" + Request.Form.Count.ToString());
        String[] arr1 = coll.AllKeys;
        for (loop1 = 0; loop1 < arr1.Length; loop1++)
        {
            req = req + ", " + arr1[loop1];
        }

       SitePage.InsertDevLog("Checkout_notification: Getting order object");
       Order order = Order.GetOrderByID(orderid);
               
       OrderTx OTx3 = new OrderTx();
       OTx3.OrderID = orderid;
       OTx3.VPSTxId = SagePay.SQLSafe(strVPSTxId);
       OTx3.TransactionID = SagePay.SQLSafe(strVendorTxCode);
       OTx3.DBstatus = strStatus;
       OTx3.Post = req;
       Order.InsertTx(OTx3);
       
        string hostAddress1 = "";

        if (this.Request != null)
            hostAddress1 = this.Request.UserHostAddress.ToString();
        else
            hostAddress1 = "";

        SitePage.InsertDevLog("Checkout_notification: Saving tracking info 1");
        string username = this.Profile.UserName.ToString();
        string s = "Status=" + strStatus + ":VendorTXCode=" + strVendorTxCode + ":VPSTxID=" + strVPSTxId;
        CustTracking.InsertCustTrackFirstPage(s,username,hostAddress1,"Checkout_notification.aspx");
       
        //** Using the VPSTxId and VendorTxCode, we can retrieve our SecurityKey from our database **
        //** This enables us to validate the POST to ensure it came from the Sage Pay Systems **
        strSecurityKey=OrderTx.GetSecurityCode(strVendorTxCode,strVPSTxId);
        SitePage.InsertDevLog("Checkout_notification: strVendorTxCode=" + strVendorTxCode);
        SitePage.InsertDevLog("Checkout_notification: strVPSTxId=" + strVPSTxId);

        if (strSecurityKey.Length == 0)
        {
            //** We cannot find a record of this order in the database, so something isn't right **
            //** To protect the customer, we should send back an INVALID response.  This will prevent **
            //** the systems from settling any authorised transactions.  We will also send a **
            //** RedirectURL that points to our orderFailure page, passing details of the error **
            //** in the Query String so that the page knows how to respond to the customer **
            Response.Clear();
            Response.ContentType = "text/plain";
            Response.Write("Status=INVALID" + (char)13 + (char)10);

            Response.Write("RedirectURL=" + SagePay.RootURL1() + "Checkout_OrderFailed.aspx?reasonCode=001" + (char)13 + (char)10);

            Response.Write("StatusDetail=Unable to find the transaction in our database." + (char)13 + (char)10);
            Response.End();
        }
        else
        {
            //** We've found the order in the database, so now we can validate the message **
            //** First blank out our result variables **
            strStatusDetail = "";
            strTxAuthNo = "";
            strAVSCV2 = "";
            strAddressResult = "";
            strPostCodeResult = "";
            strCV2Result = "";
            strGiftAid = "";
            str3DSecureStatus = "";
            strCAVV = "";
            strAddressStatus = "";
            strPayerStatus = "";
            strCardType = "";
            strLast4Digits = "";
            strMySignature = "";

            //** Now get the VPSSignature value from the POST, and the StatusDetail in case we need it **
            if (!String.IsNullOrEmpty(Request.Form["VPSSignature"]))
                strVPSSignature = SagePay.CleanInput(Request.Form["VPSSignature"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["StatusDetail"]))
            strStatusDetail = SagePay.CleanInput(Request.Form["StatusDetail"].ToString(), "Text");

            //** Retrieve the other fields, from the POST if they are present **
        if (!String.IsNullOrEmpty(Request.Form["TxAuthNo"]))          
                strTxAuthNo = SagePay.CleanInput(Request.Form["TxAuthNo"].ToString(), "Number");

            if (!String.IsNullOrEmpty(Request.Form["AVSCV2"]))          
                strAVSCV2 = SagePay.CleanInput(Request.Form["AVSCV2"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["AddressResult"]))          
                strAddressResult = SagePay.CleanInput(Request.Form["AddressResult"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["PostCodeResult"]))
                strPostCodeResult = SagePay.CleanInput(Request.Form["PostCodeResult"].ToString().ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["CV2Result"]))          
                strCV2Result = SagePay.CleanInput(Request.Form["CV2Result"], "Text");

            if (!String.IsNullOrEmpty(Request.Form["GiftAid"]))
                strGiftAid = SagePay.CleanInput(Request.Form["GiftAid"].ToString(), "Number");

            if (!String.IsNullOrEmpty(Request.Form["3DSecureStatus"]))
                str3DSecureStatus = SagePay.CleanInput(Request.Form["3DSecureStatus"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["CAVV"]))          
                strCAVV = SagePay.CleanInput(Request.Form["CAVV"], "Text");

            if (!String.IsNullOrEmpty(Request.Form["AddressStatus"]))
                strAddressStatus = SagePay.CleanInput(Request.Form["AddressStatus"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["PayerStatus"]))
                strPayerStatus = SagePay.CleanInput(Request.Form["PayerStatus"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["CardType"]))
                strCardType = SagePay.CleanInput(Request.Form["CardType"].ToString(), "Text");

            if (!String.IsNullOrEmpty(Request.Form["Last4Digits"]))
                strLast4Digits = SagePay.CleanInput(Request.Form["Last4Digits"].ToString(), "Number");

            //** Now we rebuilt the POST message, including our security key, and use the MD5 Hash **
            //** component that ships with the kit to create our own signature to compare with **
            //** the contents of the VPSSignature field in the POST.  Check the Server protocol **
            //** if you need clarification on this process **
            strMessage = strVPSTxId + strVendorTxCode + strStatus + strTxAuthNo + SagePay.VSPVendorName + strAVSCV2 + strSecurityKey +
               strAddressResult + strPostCodeResult + strCV2Result + strGiftAid + str3DSecureStatus + strCAVV +
               strAddressStatus + strPayerStatus + strCardType + strLast4Digits;

            strMySignature = FormsAuthentication.HashPasswordForStoringInConfigFile(strMessage, "MD5");

            OrderTx OTx = new OrderTx();
            OTx.OrderID = orderid;
            OTx.TXAuthNo = SagePay.SQLSafe(strTxAuthNo);
            OTx.AVSC2 = SagePay.SQLSafe(strAVSCV2);
            OTx.AddressResult = SagePay.SQLSafe(strAddressResult);
            OTx.PostCodeResult = SagePay.SQLSafe(strPostCodeResult);
            OTx.CV2Result = SagePay.SQLSafe(strCV2Result);
            OTx.D3secureStatus = SagePay.SQLSafe(str3DSecureStatus);
            OTx.CAVV = SagePay.SQLSafe(strCAVV);
            OTx.TransactionID = SagePay.SQLSafe(strVendorTxCode);
            OTx.Response = SagePay.SQLSafe(Request.Form.ToString());
            OTx.DBstatus = strDBStatus;
            Order.InsertTx(OTx);

            //** We can now compare our MD5 Hash signature with that from Server **
            if (strMySignature != strVPSSignature)
            {

                //** If the signatures DON'T match, we should mark the order as tampered with, and **
                //** send back a Status of INVALID and failure page RedirectURL **
                OrderTx OTx2 = new OrderTx();
                OTx2.OrderID = orderid;
                OTx2.TransactionID=strVendorTxCode;
                OTx2.DBstatus="*INVALID* - Signatures dont match. Order tampered with";
                Order.InsertTx(OTx2);

                Response.Clear();
                Response.ContentType = "text/plain";
                Response.Write("Status=INVALID" + (char)13 + (char)10);

                //** Only use the Internal FQDN value during development.  In LIVE systems, always use the actual FQDN **
                Response.Write("RedirectURL=" + SagePay.RootURL1() + "Checkout_OrderFailed.aspx?reasonCode=002" + (char)13 + (char)10);
               
                Response.Write("StatusDetail=Cannot match the MD5 Hash. Order might be tampered with." + (char)13 + (char)10);
                Response.End();
            }
            else
            {
                //** Great, the signatures DO match, so we can update the database and redirect the user appropriately **
                if (strStatus == "OK")
                    strDBStatus = "AUTHORISED - The transaction was successfully authorised with the bank.";
                else if (strStatus == "NOTAUTHED")
                    strDBStatus = "DECLINED - The transaction was not authorised by the bank.";
                else if (strStatus == "ABORT")
                    strDBStatus = "ABORTED - The customer clicked Cancel on the payment pages, or the transaction was timed out due to customer inactivity.";
                 else if (strStatus == "REJECTED")
                    strDBStatus = "REJECTED - The transaction was failed by your 3D-Secure or AVS/CV2 rule-bases.";
                 else if (strStatus == "AUTHENTICATED")
                    strDBStatus = "AUTHENTICATED - The transaction was successfully 3D-Secure Authenticated and can now be Authorised.";
                 else if (strStatus == "REGISTERED")
                    strDBStatus = "REGISTERED - The transaction was could not be 3D-Secure Authenticated, but has been registered to be Authorised.";
                 else if (strStatus == "ERROR")
                    strDBStatus = "ERROR - There was an error during the payment process.  The error details are: " + SagePay.SQLSafe(strStatusDetail);
                else
                    strDBStatus = "UNKNOWN - An unknown status was returned from Sage Pay.  The Status was: " + SagePay.SQLSafe(strStatus) +
                    ", with StatusDetail:" + SagePay.SQLSafe(strStatusDetail);
               
                             

                //** New reply to Server to let the system know we've received the Notification POST **
                Response.Clear();
                Response.ContentType = "text/plain";

                //** Always send a Status of OK if we've read everything correctly.  Only INVALID for messages with a Status of ERROR **
                if (strStatus == "ERROR")
                    Response.Write("Status=INVALID" + (char)13 + (char)10);
                else
                    Response.Write("Status=OK" + (char)13 + (char)10);


                //** Now decide where to redirect the customer **
                if (strStatus == "OK")
                {
               
                    order.SetAsPaid1(this.Profile.ShoppingCart.VoucherCode);
                    this.Profile.ShoppingCart.VoucherCode = "";
                    this.Profile.Save();

                    this.Profile.ShoppingCart.Clear();        //clear the cart so they cant do the order again
                    //orderid is passed as need to reload the order
                    int emailid = DAL.SiteProvider.Emails.SendOrderReceipt(order.ID);
                    DAL.SiteProvider.Emails.SendEmails(emailid);

                    //logging
                    if (!this.User.IsInRole("Administrators") && !this.User.IsInRole("Editors"))
                    {
                        string hostAddress = "";

                        if (this.Request != null)
                            hostAddress = this.Request.UserHostAddress.ToString();
                        else
                            hostAddress = "";

                        string username1 = this.Profile.UserName.ToString();

                        //customer tracking
                        CustTracking.UpdateCustTrackPay1Success(username1, hostAddress);
                    }

                   

                    //** If a transaction status is OK, AUTHENTICATED or REGISTERED, we should send the customer to the success page **
                    strRedirectPage = SagePay.RootURL1() + "Checkout_PaymentSuccessful.aspx?VendorTxCode=" + strVendorTxCode;
                }
                else
                {
                    order.StatusID = 2;
                    order.Update();

                    //logging
                    if (!this.User.IsInRole("Administrators") && !this.User.IsInRole("Editors"))
                    {
                        string hostAddress = "";

                        if (this.Request != null)
                            hostAddress = this.Request.UserHostAddress.ToString();
                        else
                            hostAddress = "";

                        string username2 = this.Profile.UserName.ToString();

                        //customer tracking
                        CustTracking.UpdateCustTrackPay1Fail(username2, hostAddress);
                    }

                    //** The status indicates a failure of one state or another, so send the customer to orderFailed instead **
                    strRedirectPage = SagePay.RootURL1() + "Checkout_OrderFailed.aspx?VendorTxCode=" + strVendorTxCode;
                }

                //** Only use the Internal FQDN value during development.  In LIVE systems, always use the actual FQDN **
                Response.Write("RedirectURL=" + strRedirectPage + (char)13 + (char)10);          

                //** No need to send a StatusDetail, since we're happy with the POST **
                Response.End();


            }




        }
       
         

      }
   }
}
nigelstephensAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ambienceCommented:
What do you mean by http header? Can that be beacuse the request is being sent as a GET request? Can you try Request.QueryString?
 
0
nigelstephensAuthor Commented:
Its is definately a post.

I have tried a test page with the following code

protected void Page_Load(object sender, EventArgs e)
        {

            Response.Clear();
            Response.ContentType = "text/plain";
            Response.Write("Status=OK" + (char)13 + (char)10);

            Response.Write("VendorTxCode=123456abc" + (char)13 + (char)10);

            Response.Write("VSPTxID=aasdd3333" + (char)13 + (char)10);
            Response.Redirect("~/checkout_notification.aspx");
            Response.End();
           


        }

On running this and stepping through in debug my checkout_notification still has no form variables in the collection. I'm really stumped.
0
ambienceCommented:
I don't think a client-side Redirect will preserve submitted data and therefore the need of Transfer() methods when you want to redirect internally.
Can you enable Trace? Also if possible can you pleas post the HTTP data received including headers? (You may have to use Wireshark for that)
 
0
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

nigelstephensAuthor Commented:
Just changed my test page to
<html  xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server" />
<body>
<form runat="server" id="form1" >

    <asp:TextBox runat="server" ID="txt1" Text="12345"></asp:TextBox>


    <asp:Button runat="server" ID="btnSubmit" PostBackUrl="~/Checkout_Notification.aspx" />
</form>


</body>
</html>

This works and the form variables are shown.
So either the original post from SagePay is not formatted correctly or there is some other problem with my page accepting posts from an external source.
Could it be anything to do with forms authentication?
0
ambienceCommented:
I dont think it has to do with Forms authentication. Somehow I suspect that SagePay may be issuing a GET request.
>> So either the original post from SagePay is not formatted correctly or there is some other problem with my page accepting posts from an external source.

Need data for that.
How about this. Just dump the contents of all collections, like QueryString, Form etc.  and that would give a clue.
0
nigelstephensAuthor Commented:
I have dumped the forms and querystring collections to by database after testing locally. Both are blank with the SagePay post.
I guess I need to go back to them and see what they have to say. Unfortunately, they take ages to get anywhere with and will probably deny its there fault.

Is there anything left you can think of ambience. Thanks for your prompt help.
0
ambienceCommented:
yeah, if you can provide actual HTTP data, using a network sniffer like Wireshark that would help a lot - at least would find out whether the problem is with Sagepay or not.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
nigelstephensAuthor Commented:
Just done a trace on my test site. It shows no form variables from SagePay. Will take it up with them.
0
nigelstephensAuthor Commented:
Very helpful
0
ambienceCommented:
thats sad
0
firthy1966Commented:
I had the same issue. I fixed it by turning off "AspxAutoDetectCookieSupport" as it was redirecting and loosing the initial request from sagepay.

set cookieless property of sessionState in your web.config to false.

<sessionState mode="InProc" cookieless="false" timeout="20" />

hope this helps someone.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Development Software

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.