Howto use Paypal Instant Payment Notification (IPN) with character encoding

The C# sample at the IPN website, is a little bit outdated, since paypal currently supports international  character sets (great!) so if a chinese customer would enter his name in Chinese for instance :), it still would be kept ok. So to not corrupt the characters, we have to stick the same format as paypal sends us. This would be simple if the page would be a web service form, it is not, so we have to wrap the POST request and do the encoding all yourselves.

using System;

using System.Collections;

using System.Collections.Specialized;

using System.IO;

using System.Text;

using System.Net;

using System.ComponentModel;

using System.Data;

using System.Web;

 

using CSession;

namespace pp

{

      ///

      /// Summary description for ipn.

      ///

      public partial class ipn : System.Web.UI.Page

      {

            private bool isTesting;

            // this page runs as LCID 1033 english

            // and is tested on codepage 65001. you must –enable- the codepage

            // on your paypal preferences!

            // note that the shopping basket, is based on ISP Session from

            // http://www.nieropwebconsult.nl/asp_session_manager.htm

            // it integrates with classic asp as well as .NET

            protected void Page_Load(object sender, System.EventArgs e)

            {

                 

                  NameValueCollection rq = Request.Form;

                  //Response.BufferOutput = false;

                  string test_ipn = rq["test_ipn"];

                  isTesting = test_ipn != null && test_ipn == "1"? true: false;

                  // Create the request back

// instead of pp_url you could read https://www.paypal.com/cgi-bin/webscr

                  HttpWebRequest req = (HttpWebRequest) WebRequest.Create(

      System.Configuration.ConfigurationSettings.AppSettings["pp_url"]);

           

                  string version = rq["notify_version"]; // ' 2.1 currently

                  // Set values for the request back

                  req.Method = "POST";

                  string notifyCmd = "cmd=_notify-validate";

                  req.ContentType = "application/x-www-form-urlencoded";     

            // the charset will be utf-8 if set as preference on paypal!

            Encoding enc = System.Text.Encoding.GetEncoding(rq["charset"]);

           

            StreamWriter tw = new StreamWriter(new MemoryStream());

            int keyCount = rq.Count;

            // here goes our first command

            tw.Write(notifyCmd);

            //tricky part. If you do Request.Form.ToString() the & and the = is encoded as well!

            for (int xx = 0; xx < keyCount; xx++)

            {  

                tw.Write('&');

                tw.Write(HttpUtility.UrlEncodeToBytes(rq.GetKey(xx), enc));

                tw.Write('=');

                tw.Write(HttpUtility.UrlEncodeToBytes(rq.Get(xx), enc));

            }

           

            int contLen = (int)tw.BaseStream.Position;

            req.ContentLength = contLen;

            // Write the request back IPN strings in binary form

            // do not use a TextWriter since that would cause

            // encoding on encoding which obviously will fail

            byte[] temp = new byte[contLen];

            tw.BaseStream.Position = 0;

            tw.BaseStream.Read(temp, 0, contLen);

            Stream stOut = req.GetRequestStream();

            stOut.Write(temp, 0, contLen);

            stOut.Flush();           

 

            // Do the request to PayPal and get the response

            StreamReader stIn = new StreamReader(req.GetResponse().GetResponseStream(), enc);

                  string strResponse = stIn.ReadToEnd();

                  stIn.Close();

                  string Item_number, Payment_status, Txn_id, sessionId;

                  Item_number = rq["item_number"];

                  sessionId = rq["invoice"];

                  Txn_id = rq["txn_id"];

                 

                  db dbl = new db();

                  dbl.sqlConn.Open();

                 

                  DataSet1.tLicenseRow lic =  dbl.dataSet11.tLicense.NewtLicenseRow();

                  lic.CtryISO2Code = rq["address_country_code"];

                  lic.email = rq["payer_email"];

                  //TODO: lic should be fullfilled

                  lic.sessionID = new Guid(sessionId).ToByteArray();

                  lic.transactionID = Guid.NewGuid();

            lic.salesBlob = rq.ToString().Replace("&", "\r\n");

                  lic.saleDate = DateTime.Now;

                  lic.total_value = decimal.Parse(rq[

blog comments powered by Disqus