This week I was working on PayPal integration in Serial Key Manager(http://serialkeymanager.com/). Some time I spent trying to understand how this can be accomplished using a server language, ASP.NET, since I have not been able to find a clear tutorial that would be easy to follow, So, in this guide I am going to outline how the entire process works and provide you with working examples!

1. Setting up the development tools

The first thing you need is to create a PayPal developer account so that you can access this page https://developer.paypal.com/webapps/developer/applications/myapps. When you press on Sandbox accounts to the left of the page, you will have the option to create two types of accounts: Personal or Business

  • Personal account will be used when you buy your product
  • Business account is the account that will receive the payment from the Personal account.

Depending on how much your product will cost, you will have to insert an appropriate amount of money into the Personal account. I think you can have as much as you like since it is only for testing purposes.

2. Creating the button

Now, you will have to create two Views in a Controller, one where you have the button and one where you have the validation mechanism. Let’s start with the button! Put the code below into any View. My View name is IPN in the User controller.

<!-- When you are done with all testing and want to change to the real PayPal, use following instead-->
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<!-- end of Real PayPal example-->

<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
	<fieldset>
		<input class="full-width" type="hidden" name="business" value="<!--enter the Business account email here-->">
		<input type="hidden" name="cmd" value="_xclick">
		<input type="hidden" name="item_name" value="The unlimited music download subscription">
		<input type="hidden" name="amount" value="9">
		<input type="hidden" name="no_shipping" value="1">
		<input type=hidden name=RETURNURL
			   value="http://example.com/User/IPN">
		<input type="hidden" name="return" value="http://example.com/User/IPN">
		<input type="hidden" name="notify_url" value="http://example.com/User/IPN">

		<button type="submit">Order now!</button>
	</fieldset>
</form>

3. Processing the information returned by PayPal

The example code is based on this code. Please take a look at it to see how it is done with several variables.

Now you need to create a new View in the controller, called IPN. Insert the following code:

public ActionResult IPN()
{

    var order = new Order(); // this is something I have defined in order to save the order in the database

    // Receive IPN request from PayPal and parse all the variables returned
    var formVals = new Dictionary<string, string>();
    formVals.Add("cmd", "_notify-synch"); //notify-synch_notify-validate
    formVals.Add("at", "this is a long token found in Buyers account"); // this has to be adjusted
    formVals.Add("tx", Request["tx"]);

    // if you want to use the PayPal sandbox change this from false to true
    string response = GetPayPalResponse(formVals, false);

    if (response.Contains("SUCCESS"))
    {
        string transactionID = GetPDTValue(response, "txn_id"); // txn_id //d
        string sAmountPaid = GetPDTValue(response,"mc_gross"); // d
        string deviceID = GetPDTValue(response, "custom"); // d
        string payerEmail = GetPDTValue(response,"payer_email"); // d
        string Item = GetPDTValue(response,"item_name");

        //validate the order
        Decimal amountPaid = 0;
        Decimal.TryParse(sAmountPaid, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out amountPaid);

        if (amountPaid == 9 )  // you might want to have a bigger than or equal to sign here!
        {
            if (orders.Count(d => d.PayPalOrderRef == transactionID) < 1)
            {
				//if the transactionID is not found in the database, add it
				//then, add the additional features to the user account
            }
            else
            {
				//if we are here, the user must have already used the transaction ID for an account
				//you might want to show the details of the order, but do not upgrade it!
            }
            // take the information returned and store this into a subscription table
            // this is where you would update your database with the details of the tran

            //return View();

        }
        else
        {
            // let fail - this is the IPN so there is no viewer
            // you may want to log something here
            order.Comments = "User did not pay the right ammount.";

			// since the user did not pay the right amount, we still want to log that for future reference.

            _db.Orders.Add(order); // order is your new Order
            _db.SaveChanges();
        }

    }
    else
    {
        //error
    }
    return View();
}

string GetPayPalResponse(Dictionary<string, string> formVals, bool useSandbox)
{

    string paypalUrl = useSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr"
        : "https://www.paypal.com/cgi-bin/webscr";

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);

    // Set values for the request back
    req.Method = "POST";
    req.ContentType = "application/x-www-form-urlencoded";

    byte[] param = Request.BinaryRead(Request.ContentLength);
    string strRequest = Encoding.ASCII.GetString(param);

    StringBuilder sb = new StringBuilder();
    sb.Append(strRequest);

    foreach (string key in formVals.Keys)
    {
        sb.AppendFormat("&{0}={1}", key, formVals[key]);
    }
    strRequest += sb.ToString();
    req.ContentLength = strRequest.Length;

    //for proxy
    //WebProxy proxy = new WebProxy(new Uri("http://urlort#");
    //req.Proxy = proxy;
    //Send the request to PayPal and get the response
    string response = "";
    using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
    {

        streamOut.Write(strRequest);
        streamOut.Close();
        using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
        {
            response = streamIn.ReadToEnd();
        }
    }

    return response;
}
string GetPDTValue(string pdt, string key)
{

    string[] keys = pdt.Split('\n');
    string thisVal = "";
    string thisKey = "";
    foreach (string s in keys)
    {
        string[] bits = s.Split('=');
        if (bits.Length > 1)
        {
            thisVal = bits[1];
            thisKey = bits[0];
            if (thisKey.Equals(key, StringComparison.InvariantCultureIgnoreCase))
                break;
        }
    }
    return thisVal;

}

PayPal provides you with many variables, so I suggest that you take a look at how the response looks like. When you’ve found a value that you would like to save into the database, use the GetPDTValue function to retrieve that value.

Please note down the Activation token found in the Buyers account in Profile>My Selling Tools>Website preferences> Update. Then, enable auto return and payment data transfer. There, you will also find the Activation token which you should add to:

formVals.Add("at", "this is a long token found in Buyers account");