Code - PayPal PayFlowPro Express Checkout

PayPal PayFlowPro ExpressCheckout for MVC/C#

I know everyone has lot's of fun wiring up PayPal due their excellent documentation (cough, cough), so I thought I would share what I put together for MVC. Before you start, create sandbox accounts and make sure your PayFlow Pro account is setup to accept Express Checkout AND set to test with the sandbox.

First, the controller (I'm assuming you know how to make a form to submit to it). When the form is submitted, the model will validate and get a token from Paypal. If all is good, will redirect.


        [HttpGet]
        public ActionResult PayPalCheckout()
        { 
          PayPalSetCheckoutModel model = new PayPalSetCheckoutModel();
           return View(model);
        }

        [HttpPost]
        public ActionResult PayPalCheckout(PayPalSetCheckoutModel model)
        {          
            model.GetToken();
            if(model.Errors.Count == 0 && !string.IsNullOrEmpty(model.RedirectURL))
            {
              //redirect to PayPal
              return Redirect(model.RedirectURL);
            }
            
            //show errors
            return View(model);

        }
Next, Paypal will want some URLs. You need a cancel and return URLs on your site. You will also need the PayPal url (there are separate production and test Urls). You can database, config file or static them as properties in a class or something - your call. I made them props for example simplicity.


private string CurrentBase
    {
      get
      {
        return Request.Url.Scheme + "://" + Request.Url.DnsSafeHost;
      }
    }

    /// <summary>
    /// sets the url paypal returns to if the buyers cnxs - Required
    /// </summary>
    private string CancelUrl
    {
      get
      {
        return CurrentBase + "/Checkout/PayPalCancel";
      }
    }

    /// <summary>
    /// sets the url PayPal will return to when done - Required
    /// </summary>
    private string ReturnUrl
    {
      get
      {
        return CurrentBase + "/Checkout/PayPalComplete";
      }
    }

    private string ConnectionUrl
    {
      get
      {
        if (IsTest)
        {
          return "pilot-payflowpro.paypal.com";
        }
        else
        {
          return "payflowpro.paypal.com";
        }
      }
    }

    /// <summary>
    /// the target PayPal Url
    /// </summary>
    private string PayPalUrl
    {
      get
      {
        if (IsTest)
        {
          return "https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=";
        }
        else
        {
          return "https://www.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=";
        }
      }
    }

    public string RedirectURL { get; set; }
      
    
Setup the information to fire off the Payal. In this instance I am not doing any shipping. There are options for that which you'll need to refer to the documentation for that. This is pretty basic. Once this is done, you're firing off the the redirect in the controller to send the user to PayPal.


public void GetToken()
    {
      if (Amount > 0)
      {
        SetExpressCheckoutPP();
        if (string.IsNullOrEmpty(RedirectURL) && Errors.Count == 0)
        {
          Errors.Add("Order could not be processed. Try again or call customer support at " + SiteSettings.GetHelpTelephone() + ".");
        }

      }
      else
      {
        Errors.Add("Your order amount is $0.");
      }

    }

    private void SetExpressCheckout()
    {

      UserInfo User = UserInfo("payflowprousername", "payflowprovendor", "payflowpropartner", "payflowpropassword");

      PayflowConnectionData Connection = new PayflowConnectionData(ConnectionUrl, "443", "30");

      Invoice Inv = new Invoice();
      Inv.Amt = new Currency(Amount.Value, "USD");

      Inv.OrderDesc = PrimarySku.Name;
      Inv.Desc = PrimarySku.Name;

      //note - this must be unqiue
      Inv.CustRef = "OrderId";

      Inv.Comment1 = "User's Id";

      CustomerInfo Cust = new CustomerInfo();
      Cust.CustCode = "User's Id";
      Cust.CustIP = Request.ServerVariables["REMOTE_ADDR"].ToString();

      //if you wan to detail the invoive with multiple items, add here
      if (Items.Count() > 0)
      {
        foreach (Item item in Items)
        {
          LineItem lineItem = new LineItem();
          lineItem.Desc = item.Name;
          lineItem.Amt = new Currency(item.Price, "USD");
          Inv.AddLineItem(lineItem);
        }
      }

      //if you want to show a discount, add as an item
      if (Discount.HasValue)
      {
        LineItem lineItem = new LineItem();
        lineItem.Desc = "Promotional Discount";
        lineItem.Amt = new Currency((Discount > 0) ? Discount * -1 : Discount, "USD");
        Inv.AddLineItem(lineItem);
      }

      //path to logo
      ECSetRequest SetRequest = new ECSetRequest(ReturnUrl, CancelUrl);
      SetRequest.NoShipping = "1";
      SetRequest.HeaderImage = "https://www.domain.com/logo.jpg";

      PayPalTender Tender = new PayPalTender(SetRequest);

      SaleTransaction Trans = new SaleTransaction(User, Connection, Inv, Tender, PayflowUtility.RequestId);
      Response Resp = Trans.SubmitTransaction();

      if (Resp != null)
      {

        TransactionResponse TrxnResponse = Resp.TransactionResponse;

        if (TrxnResponse != null && TrxnResponse.Result == 0)
        {
          //create the url with the token
          RedirectURL = PayPalUrl + Trans.Response.ExpressCheckoutSetResponse.Token;
        }
        else
        {
          Errors.Add("TrxnResponse is null");
        }

      }
      else
      {
        Errors.Add("Response is null");
      }

    }
Once the user is done at PayPal, PayPal will return them to your site using the Url you previously provided. You then can get the user's information using a "Get" call to Paypal. To finalize the order, you MUST make a "Do" call. Let's look at the controller first. Nothing special here.


 public ActionResult PayPalComplete(PayPalDoCheckoutModel model)
        {
            //check responses from PayPal
            model.CheckPayPal();

            if (model.Success && model.Errors.Count == 0)
            {
                //add happy happy thank you message to confirm completed (or show fail).
            }

            return View("PayPalComplete", model);

        }
      
    
In the model, you need to make the calls for "get" and "do" and manage the information how you choose. The documentation lists all the items that are returned, I'll leave that to you. When calling the "Get", it returns information on the buyer.


         public void CheckPayPal()
        {
      
          //first, grab the getcheckout to get customer info
          GetExpressCheckout();

          //run the docheckout to finalize the sale                
          DoExpressCheckout();

        }
      
    
The "Get"


      private void GetExpressCheckout()
      {

      UserInfo User = UserInfo("payflowprousername", "payflowprovendor", "payflowpropartner", "payflowpropassword");

      PayflowConnectionData Connection = new PayflowConnectionData(ConnectionUrl, "443", "30");

      ECGetRequest GetRequest = new ECGetRequest(Token);

      PayPalTender Tender = new PayPalTender(GetRequest);

      SaleTransaction Trans = new SaleTransaction(User, Connection, null, Tender, PayflowUtility.RequestId);

      Response Resp = Trans.SubmitTransaction();
      if (Resp != null)
      {
        TransactionResponse TrxnResponse = Resp.TransactionResponse;
        if (TrxnResponse != null)
        {

          var expressCheckoutGetResponse = Trans.Response.ExpressCheckoutGetResponse;

          PayPalCheckoutInfo.TrxnResult = TrxnResponse.Result;
          PayPalCheckoutInfo.RespMsg = TrxnResponse.RespMsg;
          PayPalCheckoutInfo.correlationID = TrxnResponse.CorrelationId;
          PayPalCheckoutInfo.payer_email = expressCheckoutGetResponse.EMail;
          PayPalCheckoutInfo.payer_id = expressCheckoutGetResponse.PayerId;
          PayPalCheckoutInfo.payer_status = expressCheckoutGetResponse.PayerStatus;

          if (!string.IsNullOrEmpty(PayerID))
          {
            PayerID = PayPalCheckoutInfo.payer_id;
          }

          if (Trans.Response.ExpressCheckoutSetResponse.Token != null && Trans.Response.ExpressCheckoutSetResponse.Token.StartsWith("EC"))
          {
             string firstname = expressCheckoutGetResponse.FirstName;
             string lastname = expressCheckoutGetResponse.LastName;
             string countrycode = expressCheckoutGetResponse.CountryCode;
          }

        }

      }

    }
      
    
The "Do". A successful transaction will return 0 as the result. Anything else should be checked against the PayPal error codes (for PayFlow Pro) and the user should presented with a friendly error manage and how to handle it.


      private void DoExpressCheckout()
    {

      UserInfo User = UserInfo("payflowprousername", "payflowprovendor", "payflowpropartner", "payflowpropassword");

      PayflowConnectionData Connection = new PayflowConnectionData(ConnectionUrl, "443", "30");

      ECDoRequest DoRequest = new ECDoRequest(Token, PayerID);
      Invoice Inv = new Invoice();
      Inv.Amt = new Currency(new decimal(Amount), "USD");
      Inv.Comment1 = "Testing Express Checkout";

      PayPalTender Tender = new PayPalTender(DoRequest);

      SaleTransaction Trans = new SaleTransaction(User, Connection, null, Tender, PayflowUtility.RequestId);

      Response Resp = Trans.SubmitTransaction();
      if (Resp != null)
      {
        TransactionResponse TrxnResponse = Resp.TransactionResponse;

        if (TrxnResponse != null)
        {

          var expressCheckoutGetResponse = Trans.Response.ExpressCheckoutGetResponse;          

          //TrxnResponse.Pnref;
          //TrxnResponse.PPref;
          //TrxnResponse.RespMsg;
          //TrxnResponse.Result;

          //TrxnResponse.CorrelationId;
          //Token;
          //expressCheckoutGetResponse.PayerId;
          //expressCheckoutGetResponse.PayerStatus;
          //expressCheckoutGetResponse.PhoneNum;
          //Trans.Response.TransactionResponse.PaymentType;
          //Trans.Response.TransactionResponse.PendingReason;
          //TrxnResponse.Result;

          if (TrxnResponse.Result == 0)
          {

            //Success

            decimal fee;
            if (decimal.TryParse(TrxnResponse.FeeAmt, out fee))
            {
              //paypal fee;
            }

          }

        }
      }

      //for debugging
      //    PayflowUtility.GetStatus(Resp);

    }