Tuesday, February 17, 2009

Testing PayPal IPN

When working with PayPal as a payment provider, you'll inevitable wind up dealing with their Instant Payment Notification (or IPN). Its not always instant and not always payment related but is a notification.

An IPN is a simple HTTP form post that PayPal sends your server (to a url specified in your account settings or the "notify_url" parameter of the transaction) that provides information about the transaction that just took place. It contains information like item names and codes, price information, and the buyers shipping info. Because it is sent directly from PayPal's servers to yours without passing through the user's browser, it is much more secure and less susceptible to tampering.

However, when working behind a firewall or NATed router, PayPal's IPN has no way of reaching your development machine! Sure, you can forward a port to your PC but chances are, your port 80 is already being forwarded to a real web server (or you might not have access to the router).

To get around this problem, I built a simple ASP.Net page on our public facing test server (that has access to our internal network) that simply accepts the post from PayPal and reposts it to my development machine. The code is very simple:


_internalURL = "http://mydevmachineurlhere";

string postData = "a=" + "a";
for (int i = 0; i < Request.Form.Count; i++) {
postData += "&" + Request.Form.AllKeys[i] + "=" + Request.Form[Request.Form.AllKeys[i]];
}

encoding = new ASCIIEncoding();
byte[] data = encoding.GetBytes(postData);

// Prepare web request...
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(_internalURL); myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.ContentLength = data.Length;
Stream newStream = myRequest.GetRequestStream();

// Send the data. newStream.Write(data, 0, data.Length);
newStream.Close();


As you can see, this page will redirect the POST to an internal URL of your choice. You can even get fancier and have the repost go to different URL's based on any parameter you want (e.g. "payer_email", "item_name" etc.) to facilitate testing for multiple developers - without having to mess with your firewall.
Reblog this post [with Zemanta]