Implement PayPal's Instant Payment Notification IPN with .NET
PayPal's Instant Payment Notification (IPN) allows you to program your back-end
operations to automate transactions when you receive payments from your
customers. As of this initial writing, no samples on the entire searchable
Internet have illustrated how this simple task can be done with the .NET
Framework. If you are already familiar with the background of PayPal, then
jump right to the .NET code.
Background
Whenever purchases are made through PayPal by a customer, PayPal uses IPN to
post the transaction information to a resource that you specify:
Example (Fake Url):
https://www.myserver.com/PayPal/PaymentNotification.aspx
Once your resource receives the post, you must complete five steps:
-
Notification Validation
In this step, your job is to post the data that was posted to you back to
PayPal. This ensures that the original post indeed came from PayPal, and is not
sort of fraud. In the data that you post back, you must append the
"cmd=_notify-validate" value to the POST string. Once the data is posted back
to PayPal, PayPal will respond with a string of either "VERIFIED" or "INVALID".
"VERIFIED" means that PayPal sent the original post, and that you should
continue your automation process as part of the transaction. "INVALID" means
that PayPal did not send the original post, and it should probably be logged and
investigated for possible fraud.
-
Payment Status Check
In this step, you should check that the "payment_status" form field is
"Completed". This ensures that the customer's payment has been processed by
PayPal, and it has been added to the seller's account.
-
Transaction Duplication Check
In this step, you should check that the "txn_id" form field, transaction
ID, has not already been processed by your automation system. A good thing
to do is to store the transaction ID in a database or file for duplication
checking. If the transaction ID posted by PayPal is a duplicate, you should not
continue your automation process for this transaction. Otherwise, this could
result in sending the same product to a customer twice.
-
Seller Email Validation
In this step, you simply make sure that the transaction is for your account.
Your account will have specific email addresses assigned to it. You should
verify that the "receiver_email" field has a value that corresponds to an email
associated with your account.
-
Payment Validation
As of now, this step is not listed on other sites as a requirement, but it is
very important. Because any customer who is familiar with query strings
can modify the cost of a seller's product, you should verify that the
"payment_gross" field corresponds with the actual price of the item that the
customer is purchasing. It is up to you to determine the exact price of the
item the customer is purchasing using the form fields. Some common fields you
may use to lookup the item being purchased include "item_name" and
"item_number". To see for yourself, follow these steps:
-
Click on a button used to purchase one of your products.
-
Locate the "payment_gross" field in the query string and change its value.
-
Use the changed URL and perform a re-request, typically by hitting [ENTER] in
the browser Address Bar.
-
Notice the changed price for your product on the PayPal order page.
Posting Back with .NET
public class PaymentNotification : System.Web.UI.Page
{
// This helper method encodes a string correctly for an HTTP POST
private string Encode(string oldValue)
{
string newValue = oldValue.Replace("\"", "'");
newValue = System.Web.HttpUtility.UrlEncode(newValue);
newValue = newValue.Replace("%2f", "/");
return newValue;
}
private void Page_Load(object sender, System.EventArgs e)
{
// Step 1a: Modify the POST string.
string formPostData = "cmd = _notify-validate";
foreach (String postKey in Request.Form)
{
string postValue = Encode(Request.Form[postKey]);
formPostData += string.Format("&{0}={1}", postKey, postValue);
}
// Step 1b: POST the data back to PayPal.
WebClient client = new WebClient();
client.Headers.Add("Content-Type","application/x-www-form-urlencoded");
byte[] postByteArray = Encoding.ASCII.GetBytes(formPostData);
byte[] responseArray = client.UploadData("https://www.paypal.com/cgi-bin/webscr", "POST", postByteArray);
string response = Encoding.ASCII.GetString(responseArray);
// Step 1c: Process the response from PayPal.
switch (response)
{
case "VERIFIED":
{
// Perform steps 2-5 above.
// Continue with automation processing if all steps succeeded.
break
;
}
default:
{
// Possible fraud. Log for investigation.
break;
}
}
}
}
Posting Back With COM and IXmlHttpRequest
And for the .NET developers who still want to use COM and IXmlHttpRequest, here
is the equivalent code for Step 1b:
object msxml = Server.CreateObject("Msxml2.XMLHTTP");
Type type = msxml.GetType();
object[] args = {"POST", "https://www.paypal.com/cgi-bin/webscr", false};
object[] args2 = {formPostData};
type.InvokeMember("Open", System.Reflection.BindingFlags.InvokeMethod, null, msxml, args);
type.InvokeMember("Send", System.Reflection.BindingFlags.InvokeMethod, null, msxml, args2);
object responseObj = type.InvokeMember("responseText",
System.Reflection.BindingFlags.GetProperty,
null,
msxml,
null);
string response = (string) responseObj;
When using
Server.CreateObject in ASP.NET, however, you
must add
aspcompat=true to the Page directive, as shown:
<%@
Page aspcompat=true language="c#" Codebehind="PaymentNotification.aspx.cs"
AutoEventWireup="false" Inherits="PaymentNotification" %>
Back to Tips and Tricks