PayPal with ASP.NET MVC

PayPal is a cheap and easy solution for taking payments and is quick to implement in ASP.NET MVC. We will look at a simple scenario of taking a user to a PayPal payment page to checkout. There is not much information on the PayPal website about using MVC, so hopefully this should get you started. This post deals with the express checkout, but the same principle applies to the normal checkout process.

Creating a PayPal Account

You will need a sandbox account to test your transactions. The sandbox account is separate from a regular account and is all you need to sign up to before you develop and test your code.

PayPal Account Settings

It's good practice to put things like account settings in the web.config. The code in this article will access these settings. You must change the sandbox setting to False when using regular account details:

<appSettings>
   <add key="PayPal:Sandbox" value="True" />
   <add key="PayPal:Username" value="*" />
   <add key="PayPal:Password" value="*" />
   <add key="PayPal:Signature" value="*" />
   <add key="PayPal:ReturnUrl" value="http://www.superstarcoders.com" />
   <add key="PayPal:CancelUrl" value="http://www.superstarcoders.com" />
</appSettings>

PayPal Checkout Button

We won’t discuss the ins and outs of implementing the shopping cart functionality for adding/removing products, and will get straight onto the “buy” button. You will need to place a checkout or buy button on your page (you must use the ones PayPal provide).

Here’s how this should look in ASP.NET MVC using the Razor engine (which I’m a big fan of):

@using (Html.BeginForm("Pay", "Cart"))
{
   <input type='image' name='submit'
      src='https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif' />
}

The checkout button posts back to your controller and then you do a server side call to PayPal. This is the only API call you need to make to get the simplest of transactions going and in fact you don’t need to make this call if you're not using the express checkout, however, the nice part about doing this server side interaction is you get some feedback from PayPal which you can log (logging the interaction with a payment provider in this way for trouble shooting purposes is heavily advised) before taking the user to the PayPal website. The MVC redirect mechanism makes this quite easy in your controller. Once this redirect happens the user can pay and the transaction is complete.

public class CartController : Controller
{
   public ActionResult Index()
   {
      return View();
   }

   public ActionResult Pay()
   {
      PayPalRedirect redirect = PayPal.ExpressCheckout(new PayPalOrder { Amount = 50 });

      Session["token"] = redirect.Token;

      return new RedirectResult(redirect.Url);
   }
}

Here is the PayPal API code that you can include in your solution or a separate library:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Net;
using System.IO;
using System.Globalization;

public class PayPal
{
   public static PayPalRedirect ExpressCheckout(PayPalOrder order)
   {
      NameValueCollection values = new NameValueCollection();

      values["METHOD"] = "SetExpressCheckout";
      values["RETURNURL"] = PayPalSettings.ReturnUrl;
      values["CANCELURL"] = PayPalSettings.CancelUrl;
      values["AMT"] = "";
      values["PAYMENTACTION"] = "Sale";
      values["CURRENCYCODE"] = "GBP";
      values["BUTTONSOURCE"] = "PP-ECWizard";
      values["USER"] = PayPalSettings.Username;
      values["PWD"] = PayPalSettings.Password;
      values["SIGNATURE"] = PayPalSettings.Signature;
      values["SUBJECT"] = "";
      values["VERSION"] = "2.3";
      values["AMT"] = order.Amount.ToString(CultureInfo.InvariantCulture);

      values = Submit(values);

      string ack = values["ACK"].ToLower();

      if (ack == "success" || ack == "successwithwarning")
      {
         return new PayPalRedirect
         {
            Token = values["TOKEN"],
            Url = String.Format("https://{0}/cgi-bin/webscr?cmd=_express-checkout&token={1}",
               PayPalSettings.CgiDomain, values["TOKEN"])
         };
      }
      else
      {
         throw new Exception(values["L_LONGMESSAGE0"]);
      }
   }

   private static NameValueCollection Submit(NameValueCollection values)
   {
      string data = String.Join("&", values.Cast<string>()
        .Select(key => String.Format("{0}={1}", key, HttpUtility.UrlEncode(values[key]))));

      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
         String.Format("https://{0}/nvp",PayPalSettings.ApiDomain));

      request.Method = "POST";
      request.ContentLength = data.Length;

      using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
      {
         writer.Write(data);
      }

      using (StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream()))
      {
         return HttpUtility.ParseQueryString(reader.ReadToEnd());
      }
   }
}

The PayPal order class (which has some details left for you to fill in):

public class PayPalOrder
{
  public decimal Amount { get; set; }
}

The PayPal redirect result class:

public class PayPalRedirect
{
  public string Url { get; set; }
  public string Token { get; set; }
}

Here’s the wrapper around the ASP.NET application settings used by the PayPal API code:

using System; using System.Configuration; using System.ComponentModel; using System.Globalization;

public static class PayPalSettings
{
   public static string ApiDomain
   {
      get
      {
         return Setting<bool>("PayPal:Sandbox") ? "api-3t.sandbox.paypal.com"
            : "api-3t.paypal.com";
      }
   }

   public static string CgiDomain
   {
      get
      {
         return Setting<bool>("PayPal:Sandbox") ? "www.sandbox.paypal.com" : "www.paypal.com";
      }
   }

   public static string Signature
   {
      get
      {
         return Setting<string>("PayPal:Signature");
      }
   }

   public static string Username
   {
      get
      {
         return Setting<string>("PayPal:Username");
      }
   }

   public static string Password
   {
      get
      {
         return Setting<string>("PayPal:Password");
      }
   }

   public static string ReturnUrl
   {
      get
      {
         return Setting<string>("PayPal:ReturnUrl");
      }
   }

   public static string CancelUrl
   {
      get
      {
         return Setting<string>("PayPal:CancelUrl");
      }
   }

   private static T Setting<T>(string name)
   {
      string value = ConfigurationManager.AppSettings[name];

      if (value == null)
      {
         throw new Exception(String.Format("Could not find setting '{0}',", name));
      }

      return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
   }
}

Further Investigation

There are facilities for PayPal to notify your website that the transaction was successful (you get emailed so this isn’t entirely necessary) and there are numerous other features, but hopefully this should get you started on the path to using PayPal with ASP.NET MVC.