0

I am using HttpWebRequest to communicate payment details to a payment processor and receive a response. When the method that encapsulates this request is hit, the model is passed in but suddenly becomes null as soon as the request occurs. Namely, this occurs in Google Chrome but I'm sure there is a deeper issue here. I have also observed this behavior only on a production server, yet I am unable to product is it debug under localhost. For those of you who have worked with payment processors, I have used a similar method to post payments to Authorize.Net without issue. Perhaps this is an issue related to how information is posted to another server and in what format.

View Model

public class PaymentModel
{
    // --------------------
    // Patient Information
    // --------------------

    [Required(ErrorMessage = "First Name is required")]
    public string PatientFirstName { get; set; }

    [Required(ErrorMessage = "Last Name is required")]
    public string PatientLastName { get; set; }


    public bool EmailReceipt { get; set; }

    [EmailAddress(ErrorMessage = "A valid email address is required if a receipt is to be emailed")]
    public string EmailAddress { get; set; }

    [DataType(DataType.Date, ErrorMessage = "A valid Date Of Service is required (mm/dd/yyyy)")]
    [Required(ErrorMessage = "A valid Date Of Service is required")]
    public DateTime DateOfService { get; set; }


    public string BillCode { get; set; }

    // Company Specific information
    public static string AccountNumberPrefix = "xx";
    public static string CompanyAccountNumber = "xxx";
    public static string CompanyName = "xxx";

    [Required(ErrorMessage = "Account Number is required")]
    public string AccountNumber { get; set; }


    public string PhoneNumber { get; set; }


    // ---------------------
    // Payment Information
    // ---------------------

    [Required(ErrorMessage = "Name on Card is required")]
    public string NameOnCard { get; set; }

    [Required(ErrorMessage = "Card Billing Address is required")]
    public string BillingAddress { get; set; }

    [Required(ErrorMessage = "Card Billing Zipcode in required")]
    public string BillingZipCode { get; set; }


    public string CardType { get; set; }


    [Required(ErrorMessage = "A valid Credit Card Number is required")]
    public string CardNumber { get; set; }

    [Required(ErrorMessage = "CSC/CVN is required")]
    public string SecurityCode { get; set; }


    public string ExpireMonth { get; set; }

    public string ExpireYear { get; set; }


    [Required(ErrorMessage = "A valid payment amount is required")]
    [RegularExpression(@"^(?=.*\d)\d{0,6}(\.\d{1,2})?$", ErrorMessage = "Invalid format - Allowed: x.xx (Number/Decimal)")]
    public string PaymentAmount { get; set; }


    // -----------------
    // Static Options
    // -----------------

    // CC Expire Month Options
    public static List<SelectListItem> ExpMonthOptions = new List<SelectListItem>()
    {
        new SelectListItem() {Text="01", Value="01"},
        new SelectListItem() {Text="02", Value="02"},
        new SelectListItem() {Text="03", Value="03"},
        new SelectListItem() {Text="04", Value="04"},
        new SelectListItem() {Text="05", Value="05"},
        new SelectListItem() {Text="06", Value="06"},
        new SelectListItem() {Text="07", Value="07"},
        new SelectListItem() {Text="08", Value="08"},
        new SelectListItem() {Text="09", Value="09"},
        new SelectListItem() {Text="10", Value="10"},
        new SelectListItem() {Text="11", Value="11"},
        new SelectListItem() {Text="12", Value="12"}
    };

    // CC Expire Year Options
    public static List<SelectListItem> ExpYearOptions = new List<SelectListItem>() 
    {
        new SelectListItem() {Text=DateTime.Now.Year.ToString(), Value=DateTime.Now.Year.ToString()},
        new SelectListItem() {Text=DateTime.Now.AddYears(1).ToString("yyyy"), Value=DateTime.Now.AddYears(1).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(2).ToString("yyyy"), Value=DateTime.Now.AddYears(2).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(3).ToString("yyyy"), Value=DateTime.Now.AddYears(3).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(4).ToString("yyyy"), Value=DateTime.Now.AddYears(4).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(5).ToString("yyyy"), Value=DateTime.Now.AddYears(5).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(6).ToString("yyyy"), Value=DateTime.Now.AddYears(6).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(7).ToString("yyyy"), Value=DateTime.Now.AddYears(7).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(8).ToString("yyyy"), Value=DateTime.Now.AddYears(8).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(9).ToString("yyyy"), Value=DateTime.Now.AddYears(9).ToString("yyyy")},
        new SelectListItem() {Text=DateTime.Now.AddYears(10).ToString("yyyy"), Value=DateTime.Now.AddYears(10).ToString("yyyy")}
    };


    // ------------------
    // Payment Processor
    // ------------------
    public string referenceNumber { get; set; }
    public string processorError { get; set; }

}

Controller

    // POST: /Payment/Confirmation
    [HttpPost]
    public ActionResult Confirmation(string restartBtn, string prevBtn, string nextBtn)
    {

        try
        {
            // Get current payment session
            PaymentModel paymentSession = GetPayment();

            // Return to previous step
            if (prevBtn != null)
            {
                // Ensure there are no false processor errors
                paymentSession.processorError = string.Empty;


                return View("index", paymentSession);
            }

            // Proceed to process and next step
            if (nextBtn != null)
            {

                // Initialize Transaction Reference
                paymentSession.referenceNumber = PaymentUtilities.GenerateReferenceNumber(15, false);

                // Initialize new transaction object for Authorize.Net
                MerchantOne merchantName = new MerchantOne(
                    "xxxx",               // User Name
                    "xxxx"                // Password
                    );


                // Perform the transaction and get the response
                var response = merchantName.Charge(paymentSession);

                // Store the initial response
                _repository.InsertTransaction(response);


                // Was the payment declined?
                if (response.IsError)
                {
                    paymentSession.processorError = response.Message;
                    return View("index", paymentSession);
                }

                // Store successful payment details
                _repository.InsertPayment(paymentSession, response);

                // Did the user elect to receive a receipt?
                if (paymentSession.EmailReceipt)
                {
                    // Email receipt
                    EmailReceipt(paymentSession, response);
                }

                return View("receipt", paymentSession);
            }

            // Clear payment session and return to first step
            if (restartBtn != null)
            {
                RemovePayment();
                return View("index");
            }
        }
        catch (Exception ex)
        {
            EmailError(ex);
            return View("Error");
        }

        return View();
    }

Class that handles transactions (sends the HttpWebRequest)

public class MerchantOne
{

    private readonly string _userName;
    private readonly string _password;


    // POST URLS
    private const string postURL = "https://secure.merchantonegateway.com/api/transact.php?";
    private const string testURL = "";


    public MerchantOne(string username, string password)
    {
        _userName = username;
        _password = password;
    }

    public MerchantResponse Charge(PaymentModel paymentSession)
    {
        // Prepare and assign post values
        Dictionary<string, string> postValues = new Dictionary<string, string>();

        // Merchant Information
        postValues.Add("username", _userName);
        postValues.Add("password", _password);
        postValues.Add("type", "sale");

        // Transaction Information
        postValues.Add("orderid", paymentSession.referenceNumber);
        postValues.Add("ccnumber", paymentSession.CardNumber);
        postValues.Add("ccexp", paymentSession.ExpireMonth + paymentSession.ExpireYear.Substring(2, 2));
        postValues.Add("cvv", paymentSession.SecurityCode);
        postValues.Add("amount", paymentSession.PaymentAmount.Replace("$", "").Replace(",", "").Trim());
        postValues.Add("address1", paymentSession.BillingAddress);
        postValues.Add("zip", paymentSession.BillingZipCode);

        // Convert the fields into http POST format
        String postString = String.Empty;
        foreach (KeyValuePair<string, string> postValue in postValues)
        {
            postString += postValue.Key + "=" + HttpUtility.UrlEncode(postValue.Value) + "&";
        }
        postString = postString.TrimEnd('&');

        // Create an HttpWebRequest object for communication with Merchant One
        HttpWebRequest objRequest;


        objRequest = (HttpWebRequest)WebRequest.Create(postURL);

        objRequest.Method = "POST";
        objRequest.ContentLength = postString.Length;
        objRequest.ContentType = "application/x-www-form-urlencoded";

        // POST the data as stream
        StreamWriter streamWriter = null;
        streamWriter = new StreamWriter(objRequest.GetRequestStream());
        streamWriter.Write(postString);
        streamWriter.Close();

        // Returned values are returned as a stream, then read into a string
        String postResponse;
        HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();
        using (StreamReader responseStream = new StreamReader(objResponse.GetResponseStream()))
        {
            postResponse = responseStream.ReadToEnd();
            responseStream.Close();
        }

        // Prepare new response object
        MerchantResponse response = new MerchantResponse();

        // ----------------------------------------------------------------
        // Fill response properties with gateway response array indexes
        //-----------------------------------------------------------------
        response.RawResponseData = postResponse; // Capture the entire response string in raw format

        string[] responseArray = postResponse.Split('&');

        response.MOresponse = responseArray[0].Split('=')[1];
        response.MOresponseText = responseArray[1].Split('=')[1];
        response.MOauthCode = responseArray[2].Split('=')[1];
        response.MOtransactionId = responseArray[3].Split('=')[1];
        response.MOavsResponse = responseArray[4].Split('=')[1];
        response.MOcvvResponse = responseArray[5].Split('=')[1];
        response.MOorderId = responseArray[6].Split('=')[1];
        response.MOtype = responseArray[7].Split('=')[1];
        response.MOresponse_Code = responseArray[8].Split('=')[1];

        // Add payment method to response based on CC number
        response.MOpaymentType = ResponsePaymentTypePerCC(paymentSession.CardNumber);

        // Add name on card from payment field for transaction storage
        response.MOnameOnCard = paymentSession.NameOnCard;

        // Add Transaction Amount from payment field for transaction storage
        response.MOtransactionAmt = paymentSession.PaymentAmount.Replace("$", "").Replace(",", "").Trim();


        // Transaction result
        response.IsTransactionApproved = response.MOresponse == "1";                                        // Transaction Approved?
        response.IsError = response.MOresponse != "1";                                                      // Transaction Failed (i.e. Declined)?
        response.Message = ResponseReasonPerCode(response.MOresponse_Code, response.MOresponseText);        // Reason for response

        // Return response object
        return response;
    }



    private string ResponseReasonPerCode(string responseCode, string responseText)
    {
        string responseReason;
        switch (responseCode)
        {
            case "200":
                responseReason = "Transaction was declined by processor.";
                break;
            case "201":
                responseReason = "Transaction denied.  Do not honor.";
                break;
            case "202":
                responseReason = "Insufficient Funds.";
                break;
            case "203":
                responseReason = "Over Limit.";
                break;
            case "204":
                responseReason = "Transaction not allowed.";
                break;
            case "220":
                responseReason = "Incorrect payment data.";
                break;
            case "221":
                responseReason = "No such card issuer.";
                break;
            case "222":
                responseReason = "No such card number on file with issuer.";
                break;
            case "223":
                responseReason = "Card has expired.";
                break;
            case "224":
                responseReason = "Invalid expiration date.";
                break;
            case "225":
                responseReason = "Invalid card security code (CVV).";
                break;
            case "240":
                responseReason = "Call issuer for further information.";
                break;
            case "250":
                responseReason = "Pick up card.";
                break;
            case "251":
                responseReason = "Lost card.";
                break;
            case "252":
                responseReason = "Stolen card.";
                break;
            case "253":
                responseReason = "Fraudulent card.";
                break;
            case "260":
                responseReason = "Transaction declined (260): " + responseText;
                break;
            case "430":
                responseReason = "Duplicate transaction.";
                break;
            default:
                responseReason = "Unable to process payment.  Error code " + responseText;
                break;
        }

        return responseReason;
    }

    private string ResponsePaymentTypePerCC(string ccNumber)
    {
        string paymentType = string.Empty;
        switch (ccNumber.Trim().Substring(0, 1))
        {
            case "3":
                paymentType = "AMEX";
                break;
            case "4":
                paymentType = "VISA";
                break;
            case "5":
                paymentType = "MC";
                break;
            case "6":
                paymentType = "DISC";
                break;
        }

        return paymentType;
    }

}

The error message is this.. System.NullReferenceException: Object reference not set to an instance of an object. at namespace.MerchantOne.Charge(PaymentModel paymentSession) at namespace.Controllers.PaymentController.Confirmation(String restartBtn, String prevBtn, String nextBtn)

Honestly, it could be another object becoming null or not being initialized but right now, all I can think is that my PaymentModel is getting lost after use.

Any help is appreciated.

Cody Hicks
  • 420
  • 9
  • 24
  • I had a similar issue, but this was because a child model was null and I didn't properly handle it – VisualBean Aug 31 '15 at 13:18
  • @VisualBean perhaps there is a similar issue here. I am digging around to see if maybe there is something not being fulfilled in my 'MerchantOne' class. – Cody Hicks Aug 31 '15 at 13:22
  • Oddly, it seems I'm getting a response and I'm able to parse the response. My repository is plugging the data into my database. Beyond that, I'm showing something being null but the exception always points to the method that posts the payment, even though that occurs before _repository.InsertTransaction(response) – Cody Hicks Aug 31 '15 at 13:31
  • first all did you check if GetPayment() didn't lost his session also you might need to check how the application pool is configure in your server might be the recycling is not set correctly. – DarkVision Aug 31 '15 at 13:43
  • @DarkVision That very well could be part of the problem. Could be that the session is getting lost during the post. I have a session that holds the `PaymentModel` throughout the process. – Cody Hicks Aug 31 '15 at 13:47
  • @DarkVision What would be an appropriate setting in IIS for recycling in this scenario? – Cody Hicks Aug 31 '15 at 13:48
  • by default the interval is set to 1740 mins if i remember correctly but i suggest you to check if your application hold correctly the session for PaymentModel can be relate to many problem – DarkVision Aug 31 '15 at 13:53
  • you can also disable recycling and Idle Time-out to 0 or higher than 20 by default – DarkVision Aug 31 '15 at 13:56

2 Answers2

0

I had the similar issue. I was getting a null response from paypal for the carts items and other amounts. Turned out when receiving back the response I was using the IP address but when sending the request I was using localhost. can you please check if this is not the issue.

user3509208
  • 531
  • 1
  • 5
  • 15
  • Oddly, it seems I'm getting a response and I'm able to parse the response. My repository is plugging the data into my database. Beyond that, I'm showing something being null but the exception always points to the method that posts the payment, even though that occurs before `_repository.InsertTransaction(response);` – Cody Hicks Aug 31 '15 at 13:30
  • On the IP address, I understand how that might be an issue but both my local machine (locahost) and our production server are both on the same LAN, essentially producing the same IP address beyond the gateway. – Cody Hicks Aug 31 '15 at 13:35
0

So, as it turns out... something totally stupid with the Chrome browser. I've been banging my head with this all day. My code is fine, the session settings are fine on the server.

404 not found in for woff/woff2 files. I use font-awesome in my projects and two of the resources it was looking for threw a 404/not found and this was killing my asp.net session.

Here is what finally solved my issue... adding the .woff and .woff2 MIME types to my server.

Why is @font-face throwing a 404 error on woff files?

Thanks to all who tried to help though.

Community
  • 1
  • 1
Cody Hicks
  • 420
  • 9
  • 24