Hello, I am trying to get cancelled subscriptions to process through my IPN listener but it always fails with 500 internal server error. I can process subscr_payment without any problems, but any time PayPal sends a subscr_cancel IPN message I get the 500 error. This is happening in the sandbox environment. Any help would be greatly appreciated. EDIT: I put in some logging before I parse the query string into a dictionary object. The error I'm getting is "The specified string is not in the form required for an e-mail address." I'm not sure what this means. Any ideas? EDIT2: Nevermind, I'm dumb... I'm trying email an html encoded email address... It's working now, ignore me 🙂 I don't see anywhere to attach files, so I will paste my listener code below (C#) : using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;
using System.Web;
using System.Data.SqlClient;
using System.Data;
namespace CSTracker.IPNListener
{
public partial class Listener : System.Web.UI.Page
{
string strSandbox = "https://www.sandbox.paypal.com/cgi-bin/webscr";
string strLive = "https://www.paypal.com/cgi-bin/webscr";
bool useSandbox = true;
protected void Page_Load(object sender, EventArgs e)
{
try {
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//Post back to either sandbox or live
HttpWebRequest req;
if(useSandbox)
{
req = (HttpWebRequest)WebRequest.Create(strSandbox);
}
else
{
req = (HttpWebRequest)WebRequest.Create(strLive);
}
//Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] Param = Request.BinaryRead(HttpContext.Current.Request.ContentLength);
StringBuilder strRequest = new StringBuilder(Encoding.ASCII.GetString(Param));
strRequest.Append("&cmd=_notify-validate");
req.ContentLength = strRequest.ToString().Length;
//Send the request to PayPal and get the response
StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), Encoding.ASCII);
streamOut.Write(strRequest);
streamOut.Close();
StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream());
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
// Create a dictionary for the query strings
string[] parms = strRequest.ToString().Split('&');
Dictionary<string, string> args = new Dictionary<string, string>();
foreach (string parm in parms)
{
string key = parm.Split('=')[0];
string value = parm.Split('=')[1];
args.Add(key, value);
}
if (strResponse == "VERIFIED")
{
bool validPayment = true;
// Check the receiver_email is my primary paypal email
if(DecodeString(args["receiver_email"]) != "email")
{
validPayment = false;
}
// Process payment
if (validPayment)
{
switch(args["txn_type"])
{
case "subscr_payment":
// Check the txn_id has not been previously processed.
string result = SQL.Get(args["txn_id"]);
if (args["payment_status"] == "Completed" &&
(args["mc_gross"] == "14.99" && args["mc_currency"] == "USD") &&
result == "VALID"
)
{
Dictionary<string, string> customArgs = new Dictionary<string, string>();
string[] customs = DecodeString(args["custom"]).Split(',');
customArgs.Add("username", customs[0]);
customArgs.Add("password", customs[1]);
customArgs.Add("email", customs[2]);
customArgs.Add("firstname", customs[3]);
customArgs.Add("lastname", customs[4]);
// send me an email
SendMail(
"email",
"Payment Completed",
$"A payment has been made and sent back as VERIFIED from PayPal. Transaction ID: {args["txn_id"]}"
);
// database stuff
// add transaction id, username and current time to the transactions table.
SQL.InsertTransaction(args["txn_id"], customArgs["username"], args["payer_id"]);
SQL.InsertTransactionLog("Transaction successful", null, args["txn_id"]);
var registerObj = new Controllers.UserController.RegisterObject();
registerObj.Username = customArgs["username"];
registerObj.Password = customArgs["password"];
registerObj.Email = customArgs["email"];
registerObj.FirstName = customArgs["firstname"];
registerObj.LastName = customArgs["lastname"];
string insertUserResult = Controllers.UserController.SQL.Put(registerObj);
if (insertUserResult != "success")
{
SendMail(
"email",
"Error creating user account",
$"<p>Transaction ID: {args["txn_id"]}</p><p>Username: {customArgs["username"]}</p>"
);
}
else
{
SendMail(
"email",
"Successfully created user account",
$"<p>Transaction ID: {args["txn_id"]}</p><p>Username: {customArgs["username"]}</p>"
);
if (registerObj.Email != "")
{
SendConfirmationEmail(registerObj.Email, registerObj.Username);
}
}
}
break;
case "subscr_cancel":
SendCancellationEmail(args["payer_email"]);
break;
case "subscr_eot":
SQL.CancelSubscription(args["payer_id"]);
SendFinalCancellationEmail(args["payer_email"]);
break;
}
}
else
{
SQL.InsertTransactionLog("args", strRequest.ToString(), strResponse);
Dictionary<string, string> customArgs = new Dictionary<string, string>();
string[] customs = DecodeString(args["custom"]).Split(',');
customArgs.Add("username", customs[0]);
SendMail(
"email",
"Payment submitted",
$"<p>Transaction ID: {args["txn_id"]}</p><p>Payment status: {args["payment_status"]}</p><p>Username: {customArgs["username"]}</p>"
);
SQL.InsertTransactionLog("Transaction submitted", strRequest.ToString(), strResponse);
}
}
else if (strResponse == "INVALID")
{
//log for manual investigation
SQL.InsertTransactionLog("INVALID", strRequest.ToString(), strResponse);
SendMail("email", "Payment Invalid", $"A payment has been made that was sent back as INVALID from PayPal. Transaction ID: {args["txn_id"]}");
}
else {
//Response wasn't VERIFIED or INVALID, log for manual investigation
SQL.InsertTransactionLog("", strRequest.ToString(), strResponse);
SendMail("email", "Payment Error", $"A payment has been made that was neither VERIFIED nor INVALID by PayPal. Transaction ID: {args["txn_id"]}");
}
}
catch (Exception ex)
{
SQL.InsertTransactionLog(ex.Message, ex.InnerException.Message, "");
}
}
public Listener()
{
Load += Page_Load;
}
public string DecodeString(string encodedString)
{
string decodedString;
decodedString = encodedString.Replace("%2C", ",");
decodedString = decodedString.Replace("%3A", ":");
decodedString = decodedString.Replace("%3D", "=");
decodedString = decodedString.Replace("%40", "@");
return decodedString;
}
public bool SendMail(string recipient, string subject, string message)
{
Helper.SendMail(recipient, subject, message);
SQL.LogEmail(subject, message, recipient);
return true;
}
public bool SendConfirmationEmail(string recipient, string username)
{
string template = "removed for brevity";
template = template.Replace("##Introduction##", "Payment confirmation");
template = template.Replace("##Header##", "Child Support Tracker");
template = template.Replace("##Subheader##", "Payment confirmation");
template = template.Replace("##HeaderMessage##", "If you believe you've received this email in error, please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.");
template = template.Replace("##MessageTitle##", "Payment confirmation");
template = template.Replace("##Message##", "<p>You've received this email because you recently subscribed to the Child Support Tracker service.</p><p>Your username is " + username + "</p><p>Your password is the one you chose during registration.</p><p>If you received this email in error please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.</p>");
template = template.Replace("##ButtonURL##", "https://www.childsupporttracker.com/app/Login.aspx");
template = template.Replace("##ButtonText##", "Login");
template = template.Replace("##Footer##", "This email has been sent because you subscribed to the CST service.");
SendMail(recipient, "Payment Confirmation", template);
return true;
}
public void SendCancellationEmail(string recipient)
{
string template = "removed for brevity";
template = template.Replace("##Introduction##", "Subscription cancellation");
template = template.Replace("##Header##", "Child Support Tracker");
template = template.Replace("##Subheader##", "Subscription cancellation");
template = template.Replace("##HeaderMessage##", "If you believe you've received this email in error, please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.");
template = template.Replace("##MessageTitle##", "Subscription cancellation");
template = template.Replace("##Message##", "<p>You've received this email because you requested to cancel your subscription to the Child Support Tracker service.</p><p>Your service will remain active until the end of the term. You will still have full access to the app until then.</p><p>If you received this email in error please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.</p>");
template = template.Replace("##Footer##", "This email has been sent because you unsubscribed from the CST service.");
SendMail(recipient, "Subscription cancellation", template);
}
public void SendFinalCancellationEmail(string recipient)
{
string template = "removed for bevity";
template = template.Replace("##Introduction##", "Subscription cancellation");
template = template.Replace("##Header##", "Child Support Tracker");
template = template.Replace("##Subheader##", "Subscription cancellation");
template = template.Replace("##HeaderMessage##", "If you believe you've received this email in error, please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.");
template = template.Replace("##MessageTitle##", "Subscription cancellation");
template = template.Replace("##Message##", "<p>You've received this email because your subscription to the Child Support Tracker service has been cancelled by your request.</p><p>If you received this email in error please contact us <a href='https://www.childsupporttracker.com/Contact.aspx'>here</a>.</p>");
template = template.Replace("##Footer##", "This email has been sent because you unsubscribed from the CST service.");
SendMail(recipient, "Subscription cancellation", template);
}
public class SQL : SQLBase
{
public static string Get(string txn_id)
{
string retval = "";
try
{
var parms = new SqlParameterList();
parms.Add(new SqlParameter("@txn_id", txn_id));
retval = ExecCmdScalar("Transactions_GetID", CommandType.StoredProcedure, parms).ToString();
}
catch (SqlException ex)
{
retval = ex.Message;
}
return retval;
}
public static string InsertTransaction(string txn_id, string username, string payerid)
{
string retval = "";
try
{
var parms = new SqlParameterList();
parms.Add(new SqlParameter("@txn_id", txn_id));
parms.Add(new SqlParameter("@Username", username));
parms.Add(new SqlParameter("@PayerID", payerid));
ExecCmdNonQuery("Transactions_Insert", CommandType.StoredProcedure, parms);
retval = "success";
}
catch(SqlException ex)
{
retval = ex.Message;
}
return retval;
}
public static string InsertTransactionLog(string message, string request, string response)
{
string retval = "";
try
{
var parms = new SqlParameterList();
parms.Add(new SqlParameter("@Message", message));
parms.Add(new SqlParameter("@Request", request));
parms.Add(new SqlParameter("@Response", response));
ExecCmdNonQuery("Transactions_Log_Insert", CommandType.StoredProcedure, parms);
retval = "success";
}
catch(SqlException ex)
{
retval = ex.Message;
}
return retval;
}
public static string CancelSubscription(string PayerID)
{
string retval = "";
try
{
var parms = new SqlParameterList();
parms.Add(new SqlParameter("@PayerID", PayerID));
ExecCmdNonQuery("Subscription_Cancel", CommandType.StoredProcedure, parms);
retval = "success";
}
catch (SqlException ex)
{
retval = ex.Message;
}
return retval;
}
public static string LogEmail(string subject, string message, string recipient)
{
string retval = "";
try
{
var parms = new SqlParameterList();
parms.Add(new SqlParameter("@Subject", subject));
parms.Add(new SqlParameter("@Message", message));
parms.Add(new SqlParameter("@Recipient", recipient));
ExecCmdNonQuery("EmailHistory_Insert", CommandType.StoredProcedure, parms);
retval = "success";
}
catch(SqlException ex)
{
retval = ex.Message;
}
return retval;
}
}
}
}
... View more