I'm implementing an IPN listener in ASP.NET, but whenever I try to validate a transaction, the paypal webscr script returns an html document instead of VERIFIED or INVALID. This is the code:
private void Validate(Transaction transaction)
{
string content = String.Concat(transaction.IpnMessage, "&cmd=_notify-validate");
byte[] rawContent = Encoding.ASCII.GetBytes(content);
HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(postBackUrl);
request.Method = "POST";
request.ProtocolVersion = HttpVersion.Version11;
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
writer.Write(rawContent);
transaction.Save();
try
{
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseContent = reader.ReadToEnd();
if (responseContent.Equals("VERIFIED"))
this.Approve(transaction);
else
transaction.SetOperation("Rejected", responseContent);
}
catch (Exception ex)
{
transaction.SetOperation("WebError", ex.Message);
}
}
And here is how a Transaction object is built from an incoming request:
public static Transaction FromContext(HttpContext context)
{
StreamReader reader = new StreamReader(context.Request.InputStream);
Transaction result = new Transaction();
string ipnMessage;
reader.BaseStream.Position = 0;
ipnMessage = reader.ReadToEnd();
string[] pairs = ipnMessage.Split('&');
result.data = new Dictionary<string, string>();
foreach (string pair in pairs)
{
if (String.IsNullOrEmpty(pair))
continue;
string[] parts = pair.Split('=');
string field = parts[0];
string value = (parts.Length > 1) ? (parts[1]) : String.Empty;
result.data.Add(HttpContext.Current.Server.UrlDecode(field), HttpContext.Current.Server.UrlDecode(value));
}
result.ID = result.data["txn_id"];
result.IpnMessage = ipnMessage;
if (String.IsNullOrEmpty(result.ID))
throw new ArgumentException("This is not a valid transaction.");
return result;
}
If I send the same message via a REST console (Google Chrome extension), it works. Well, kind of: it returns INVALID but should be VERIFIED. I am not using the sandbox, I made a payment to myself of 5 cent.
By the way, I cannot set the Content-Length of the stream. If I Do, an exception is thrown saying that I have not written enough bytes to the stream before sending out the request (while I wrote exactly rawContent.Length bytes).
The postBackUrl is https://www.paypal.com/cgi-bin/webscr. I am testing this by sending a copy of the IPN message manually to the web handler. I copied that from my PayPal account IPN history. I also tried to prepend cmd=_notify-validate instead of appending it, but the result is the same.
I cannot figure out why I receive an HTML document in response.