I am trying to create a IPN listener in C# MVC website. After looking online, I have managed to create the ActionResult below.
The listener receives a "VERIFIED" response, but unfortunately it does not seems to be receiving any other information about the payment.
[HttpPost]
public ActionResult IPN()
{
var log = new LogMessage();
log.LogMessageToFile("IPN recieved!");
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string response = GetPayPalResponse(formVals, true);
if (response == "VERIFIED")
{
log.LogMessageToFile("IPN VERIFIED!");
//validate the order
string sAmountPaid = Request.QueryString["amt"];
string sPayment = ConfigurationManager.AppSettings["amount"].ToString();
Decimal amountPaid = 0;
Decimal Payment = 0;
Decimal.TryParse(sAmountPaid, out amountPaid);
Decimal.TryParse(sPayment, out Payment);
if (Payment <= amountPaid)
{
log.LogMessageToFile("IPN Correct amount");
//process it
try
{
string GUID = Request.QueryString["cm"];
string strGatewayResponse = Request.QueryString["tx"];
var data = new Datalayer();
data.AddPayment(GUID, amountPaid, strGatewayResponse, true);
log.LogMessageToFile("IPN Commplete");
return Redirect("/Payment/Success");
}
catch
{
log.LogMessageToFile("IPN Error");
return Redirect("/Payment/Error");
}
}
else
{
log.LogMessageToFile("IPN Incorrect amount!");
log.LogMessageToFile("IPN amount:" + Request.QueryString["payment_gross"]);
log.LogMessageToFile("IPN GUID:" + Request.QueryString["custom"]);
log.LogMessageToFile("IPN ID:" + Request.QueryString["txn_id"]);
return Redirect("/Payment/Error");
}
}
log.LogMessageToFile("IPN not verified!");
return View("/Payment/Error");
}
string GetPayPalResponse(Dictionary<string, string> formVals, bool useSandbox)
{
string paypalUrl = useSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr"
: "https://www.paypal.com/cgi-bin/webscr";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);
// Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param = Request.BinaryRead(Request.ContentLength);
string strRequest = Encoding.ASCII.GetString(param);
StringBuilder sb = new StringBuilder();
sb.Append(strRequest);
foreach (string key in formVals.Keys)
{
sb.AppendFormat("&{0}={1}", key, formVals[key]);
}
strRequest += sb.ToString();
req.ContentLength = strRequest.Length;
string response = "";
using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
{
streamOut.Write(strRequest);
streamOut.Close();
using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
{
response = streamIn.ReadToEnd();
}
}
return response;
}
I am most likely missing something very simple.
Any help would be amazing.
I had to do this same thing, but in Web Forms. It should apply just the same since we're both dealing with HttpWebRequest
objects. Look at the below code- this is my Page_Load()
method of my listener URL (Listener.aspx.cs). Note the section that says if (strResponse == "VERIFIED")
and look inside that where it says string[] responseArray = strRequest.Split('&');
- this is where you get all of your response variables. You will have to back track a little to the definition of strRequest
which is being used to get the values (delimited by the ampersand).
And also, look at the IPN response on your PayPal account. The response will be a long string delimited by ampersands- this will be the same value as strRequest
. This is the string where you extract other information about the payment:
protected void Page_Load(object sender, EventArgs e)
{
//string strSandbox = "https://www.sandbox.paypal.com/cgi-bin/webscr";
//string strLive = "https://www.paypal.com/cgi-bin/webscr";
string url = ConfigurationManager.AppSettings["PayPalUrl"];
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
//Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param = Request.BinaryRead(HttpContext.Current.Request.ContentLength);
string strRequest = Encoding.ASCII.GetString(param);
strRequest += "&cmd=_notify-validate";
req.ContentLength = strRequest.Length;
//Send the request to PayPal and get the response
StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII);
streamOut.Write(strRequest);
streamOut.Close();
StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream());
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
if (strResponse == "VERIFIED")
{
// strRequest is a long string delimited by '&'
string[] responseArray = strRequest.Split('&');
List<KeyValuePair<string, string>> lkvp = new List<KeyValuePair<string, string>>();
string[] temp;
// for each key value pair
foreach (string i in responseArray)
{
temp = i.Split('=');
lkvp.Add(new KeyValuePair<string, string>(temp[0], temp[1]));
}
// now we have a list of key value pairs
string firstName = string.Empty;
string lastName = string.Empty;
string address = string.Empty;
string city = string.Empty;
string state = string.Empty;
string zip = string.Empty;
string payerEmail = string.Empty;
string contactPhone = string.Empty;
foreach (KeyValuePair<string, string> kvp in lkvp)
{
switch (kvp.Key)
{
case "payer_email":
payerEmail = kvp.Value.Replace("%40", "@");
break;
case "first_name":
firstName = kvp.Value;
break;
case "last_name":
lastName = kvp.Value;
break;
case "address_city":
city = kvp.Value.Replace("+", " ");
break;
case "address_state":
state = kvp.Value.Replace("+", " ");
break;
case "address_street":
address = kvp.Value.Replace("+", " ");
break;
case "address_zip":
zip = kvp.Value;
break;
case "contact_phone":
contactPhone = kvp.Value;
break;
default:
break;
}
}
string userName = payerEmail;
string password = Membership.GeneratePassword(8, 0);
MembershipCreateStatus status = new MembershipCreateStatus();
MembershipUser newUser = Membership.CreateUser(userName, password, userName, null, null, true, out status);
ProfileCommon pc = ProfileCommon.Create(userName) as ProfileCommon;
pc.Address.PostalCode = zip;
pc.Address.Address = address;
pc.Address.City = city;
pc.Address.State = state;
pc.Personal.FirstName = firstName;
pc.Personal.LastName = lastName;
pc.Contacts.DayPhone = contactPhone;
pc.Save();
if (status == MembershipCreateStatus.Success)
{
Roles.AddUserToRole(userName, "User");
//send email to user indicating username and password
SendEmailToUser(userName, password, firstName, lastName, payerEmail);
}
// need to figure out a way to catch unwanted responses here... redirect somehow
}
else if (strResponse == "INVALID")
{
//log for manual investigation
}
else
{
//log response/ipn data for manual investigation
}
}