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.