Checkout and Payment with PayPal
This tutorial describes how to modify the Wingtip Toys sample application to include user authorization, registration, and payment using PayPal. Only users who are logged in will have authorization to purchase products. The ASP.NET 4.5 Web Forms project template's built-in user registration functionality already includes much of what you need. To this you will add PayPal Express Checkout functionality. In this tutorial you be using the PayPal developer testing environment, so no actual funds will be transferred. At the end of the tutorial, you will test the application by selecting products to add to the shopping cart, clicking the checkout button, and transferring data to the PayPal testing web site. On the PayPal testing web site, you will confirm your shipping and payment information and then return to the local Wingtip Toys sample application to confirm and complete the purchase.
There are several experienced third-party payment processors that specialize in online shopping that address scalability and security. ASP.NET developers should consider the advantages of utilizing a third party payment solution before implementing a shopping and purchasing solution.
Note The Wingtip Toys sample application was designed to shown specific ASP.NET concepts and features available to ASP.NET web developers. This sample application was not optimized for all possible circumstances in regard to scalability and security.
What you'll learn:
- How to restrict access to specific pages in a folder.
- How to create a known shopping cart from an anonymous shopping cart.
- How to use PayPal to purchase products using the PayPal testing environment.
- How to display details from PayPal in a DetailsView control.
- How to update the database of the Wingtip Toys application with details obtained from PayPal.
Adding Order Tracking
In this tutorial, you’ll create two new classes to track data from the order a user has created. The classes will track data regarding shipping information, purchase total, and payment confirmation.
Add the Order and OrderDetail Model Classes
Earlier in this tutorial series, you defined the schema for categories, products, and shopping cart items by creating the Category, Product, and CartItem classes in the Models folder. Now you will add two new classes to define the schema for the product order and the details of the order.
- Right-click the Models folder and select Add -> New Item.
- The Add New Item dialog box is displayed. Select Code, and then select Class.
- Name this new class Order.cs.
- Click Add.
The new class file is displayed in the editor. - Replace the default code with the following:
using System.ComponentModel.DataAnnotations; using System.Collections.Generic; using System.ComponentModel; namespace WingtipToys.Models { public class Order { public int OrderId { get; set; } public System.DateTime OrderDate { get; set; } public string Username { get; set; } [Required(ErrorMessage = "First Name is required")] [DisplayName("First Name")] [StringLength(160)] public string FirstName { get; set; } [Required(ErrorMessage = "Last Name is required")] [DisplayName("Last Name")] [StringLength(160)] public string LastName { get; set; } [Required(ErrorMessage = "Address is required")] [StringLength(70)] public string Address { get; set; } [Required(ErrorMessage = "City is required")] [StringLength(40)] public string City { get; set; } [Required(ErrorMessage = "State is required")] [StringLength(40)] public string State { get; set; } [Required(ErrorMessage = "Postal Code is required")] [DisplayName("Postal Code")] [StringLength(10)] public string PostalCode { get; set; } [Required(ErrorMessage = "Country is required")] [StringLength(40)] public string Country { get; set; } [StringLength(24)] public string Phone { get; set; } [Required(ErrorMessage = "Email Address is required")] [DisplayName("Email Address")] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "Email is is not valid.")] [DataType(DataType.EmailAddress)] public string Email { get; set; } [ScaffoldColumn(false)] public decimal Total { get; set; } [ScaffoldColumn(false)] public string PaymentTransactionId { get; set; } [ScaffoldColumn(false)] public bool HasBeenShipped { get; set; } public List<OrderDetail> OrderDetails { get; set; } } } - Add an OrderDetail.cs class to the Models folder.
- Replace the default code with the following code:
using System.ComponentModel.DataAnnotations; namespace WingtipToys.Models { public class OrderDetail { public int OrderDetailId { get; set; } public int OrderId { get; set; } public string Username { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public double? UnitPrice { get; set; } } }
The Order and OrderDetail classes contain the schema to define the order information used for purchasing and shipping.
In addition, you will need to update the database context class that manages the entity classes and that provides data access to the database. To do this, you will add the newly created Order and OrderDetail model classes to ProductContext class.
- In Solution Explorer, find and open the ProductContext.cs file.
- Add the highlighted code to the ProductContext.cs file as shown below:
using System.Data.Entity; namespace WingtipToys.Models { public class ProductContext : DbContext { public ProductContext() : base("WingtipToys") { } public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } public DbSet<CartItem> ShoppingCartItems { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } }}
As mentioned previously in this tutorial series, the code in the ProductContext.cs file adds the System.Data.Entity namespace so that you have access to all the core functionality of the Entity Framework. This functionality includes the capability to query, insert, update, and delete data by working with strongly typed objects. The above code in the ProductContext class adds Entity Framework access to the newly added Order and OrderDetail classes.
Adding Checkout Access
The Wingtip Toys sample application allows anonymous users to review and add products to a shopping cart. However, when anonymous users choose to purchase the products they added to the shopping cart, they must log on to the site. Once they have logged on, they can access the restricted pages of the Web application that handle the checkout and purchase process. These restricted pages are contained in the Checkout folder of the application.
Add a Checkout Folder and Pages
You will now create the Checkout folder and the pages in it that the customer will see during the checkout process.
- Right-click the project name (Wingtip Toys) in Solution Explorer and select Add a New Folder.

- Name the new folder Checkout.
- Right-click the Checkout folder and then select Add a New Item.
The Add New Item dialog box is displayed.
- Select the Visual C# > Web templates group on the left. Then, from the middle pane, select Web Form using Master Page and name it CheckoutStart.aspx.

- As before, select the Site.Master file as the master page.
- Add the following additional pages to the Checkout folder using the same steps above:
- CheckoutReview.aspx
- CheckoutComplete.aspx
- CheckoutCancel.aspx
- CheckoutError.aspx
- Right-click the Checkout folder and select Add à New Item.
The Add New Item dialog box is displayed. - Select the Visual C# > Web templates group on the left. Then, from the middle pane, select Web Configuration File, accept the default name of Web.config, and then select Add.
- Replace the existing XML content in the Web.config file with the following:
<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </configuration> - Save the Web.config file.
- In Solution Explorer, find and open the App_Start folder.
- Open the file named AuthConfig.cs.
- Uncomment the a single line of code to allow Google OpenID accounts as follows:
- Save the AuthConfig.cs file.
- In Solution Explorer, find and open the Account folder.
- Open the page named Login.aspx.
- Modify the Login control so that it appears as follows:
<asp:Login runat="server" ViewStateMode="Disabled" RenderOuterTable="false" ID="LoginCtrl" OnLoggedIn="LoginCtrl_LoggedIn"> - Modify the code-behind page name Login.aspx.cs to include the code in yellow, so that it appears as follows:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WingtipToys.Account { public partial class Login : Page { protected void Page_Load(object sender, EventArgs e) { RegisterHyperLink.NavigateUrl = "Register.aspx"; OpenAuthLogin.ReturnUrl = Request.QueryString["ReturnUrl"]; var returnUrl = HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]); if (!String.IsNullOrEmpty(returnUrl)) { RegisterHyperLink.NavigateUrl += "?ReturnUrl=" + returnUrl; } } protected void LoginCtrl_LoggedIn(object sender, EventArgs e) { WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions(); String cartId = usersShoppingCart.GetCartId(); usersShoppingCart.MigrateCart(cartId, User.Identity.Name.ToString()); } } } - Save the Login.aspx and Login.aspx.cs files.
- In the Account folder, open the code-behind file named Register.aspx.cs.
- Modify the code-behind file by including the code in yellow, so that it appears as follows:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.AspNet.Membership.OpenAuth; namespace WingtipToys.Account { public partial class Register : Page { protected void Page_Load(object sender, EventArgs e) { RegisterUser.ContinueDestinationPageUrl = Request.QueryString["ReturnUrl"]; } protected void RegisterUser_CreatedUser(object sender, EventArgs e) { FormsAuthentication.SetAuthCookie(RegisterUser.UserName, createPersistentCookie: false); WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions(); String cartId = usersShoppingCart.GetCartId(); usersShoppingCart.MigrateCart(cartId, RegisterUser.UserName.ToString()); string continueUrl = RegisterUser.ContinueDestinationPageUrl; if (!OpenAuth.IsLocalUrl(continueUrl)) { continueUrl = "~/"; } Response.Redirect(continueUrl); } } } - Save the Register.aspx.cs file. Once again, ignore the warning about the MigrateCart method.
- In Solution Explorer, find the Logic folder and open the ShoppingCartActions.cs class file.
- Add the code highlighted in yellow to the existing code in the ShoppingCartActions.cs file, so that the code in the ShoppingCartActions.cs
file appears as follows:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; namespace WingtipToys.Logic { public class ShoppingCartActions { public string ShoppingCartId { get; set; } private ProductContext _db = new ProductContext(); public const string CartSessionKey = "CartId"; public void AddToCart(int id) { // Retrieve the product from the database. ShoppingCartId = GetCartId(); var cartItem = _db.ShoppingCartItems.SingleOrDefault( c => c.CartId == ShoppingCartId && c.ProductId == id); if (cartItem == null) { // Create a new cart item if no cart item exists. cartItem = new CartItem { ItemId = Guid.NewGuid().ToString(), ProductId = id, CartId = ShoppingCartId, Product = _db.Products.SingleOrDefault( p => p.ProductID == id), Quantity = 1, DateCreated = DateTime.Now }; _db.ShoppingCartItems.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity. cartItem.Quantity++; } _db.SaveChanges(); } public string GetCartId() { if (HttpContext.Current.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name)) { HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class. Guid tempCartId = Guid.NewGuid(); HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString(); } } return HttpContext.Current.Session[CartSessionKey].ToString(); } public List<CartItem> GetCartItems() { ShoppingCartId = GetCartId(); return _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId).ToList(); } public decimal GetTotal() { ShoppingCartId = GetCartId(); // Multiply product price by quantity of that product to get // the current price for each of those products in the cart. // Sum all product price totals to get the cart total. decimal? total = decimal.Zero; total = (decimal?)(from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity * cartItems.Product.UnitPrice).Sum(); return total ?? decimal.Zero; } public ShoppingCartActions GetCart(HttpContext context) { var cart = new ShoppingCartActions(); cart.ShoppingCartId = cart.GetCartId(); return cart; } public void UpdateShoppingCartDatabase(String cartId, ShoppingCartUpdates[] CartItemUpdates) { using (var db = new WingtipToys.Models.ProductContext()) { try { int CartItemCount = CartItemUpdates.Count(); List<CartItem> myCart = GetCartItems(); foreach (var cartItem in myCart) { // Iterate through all rows within shopping cart list for (int i = 0; i < CartItemCount; i++) { if (cartItem.Product.ProductID == CartItemUpdates[i].ProductId) { if (CartItemUpdates[i].PurchaseQuantity < 1 || CartItemUpdates[i].RemoveItem == true) { RemoveItem(cartId, cartItem.ProductId); } else { UpdateItem(cartId, cartItem.ProductId, CartItemUpdates[i].PurchaseQuantity); } } } } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Database - " + exp.Message.ToString(), exp); } } } public void RemoveItem(string removeCartID, int removeProductID) { using (var db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in db.ShoppingCartItems where c.CartId == removeCartID && c.Product.ProductID == removeProductID select c).FirstOrDefault(); if (myItem != null) { // db.DeleteObject(myItem); db.ShoppingCartItems.Remove(myItem); db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Remove Cart Item - " + exp.Message.ToString(), exp); } } } public void UpdateItem(string updateCartID, int updateProductID, int quantity) { // using (webformsstorefrontEntities db = new webformsstorefrontEntities()) using (var db = new WingtipToys.Models.ProductContext()) { try { var myItem = (from c in db.ShoppingCartItems where c.CartId == updateCartID && c.Product.ProductID == updateProductID select c).FirstOrDefault(); if (myItem != null) { myItem.Quantity = quantity; db.SaveChanges(); } } catch (Exception exp) { throw new Exception("ERROR: Unable to Update Cart Item - " + exp.Message.ToString(), exp); } } } public void EmptyCart() { ShoppingCartId = GetCartId(); var cartItems = _db.ShoppingCartItems.Where( c => c.CartId == ShoppingCartId); foreach (var cartItem in cartItems) { _db.ShoppingCartItems.Remove(cartItem); } // Save changes. _db.SaveChanges(); } public int GetCount() { ShoppingCartId = GetCartId(); // Get the count of each item in the cart and sum them up int? count = (from cartItems in _db.ShoppingCartItems where cartItems.CartId == ShoppingCartId select (int?)cartItems.Quantity).Sum(); // Return 0 if all entries are null return count ?? 0; } public struct ShoppingCartUpdates { public int ProductId; public int PurchaseQuantity; public bool RemoveItem; } public void MigrateCart(string cartId, string userName) { var shoppingCart = _db.ShoppingCartItems.Where(c => c.CartId == cartId); foreach (CartItem item in shoppingCart) { item.CartId = userName; } HttpContext.Current.Session[CartSessionKey] = userName; _db.SaveChanges(); } } } - Open the Web.config file at the root of the project.
- Update the default connection string so that it appears as follows:
<<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-WingtipToys;Integrated Security=True" providerName="System.Data.SqlClient" />< - In a browser, navigate to the PayPal developer testing site:
https://developer.paypal.com - If you don’t have a PayPal developer account, create a new account by clicking Sign Up Now and following the sign up steps.
You will need the PayPal developer account to test the Wingtip Toys sample application later in this tutorial. - Once you have created and verified your PayPal developer account via email, log on to the PayPal developer testing site.
- To create a buyer test account, look for the heading Test Accounts and then click preconfigured account
to create a preconfiged account. Choose a fictitious buyer test account email and a password of your choice.
Note that the buyer email account that you choose will have a generated PayPal string added to the account name.
You will need the buyer generated email addresses and password to test the Wingtip Toys sample application at the end of this tutorial.
- Click Create Account to create the buyer test account.
- Click on preconfigured to create a seller test account.

- Create a seller test account by choosing the Seller account type. Choose a fictitious seller test account login email and a password of your choice.
The seller email account that you choose will also have a generated PayPal string added to the account name.

- Click Create Account to create the seller test account.
- Click Test Accounts on the Sandbox menu, if it is not already selected. In the Payment Review column, click Disabled
to enable each account.

- On the Sandbox menu, click Home to return to the main page of the PayPal testing site.
- On the Sandbox menu, click API and Payment Card Credentials to view your API credentials.
- In Wingtip Toys sample application within Visual Studio, right-click the Logic folder and then select Add à New Item.
The Add New Item dialog box is displayed.
- Under Visual C# from the Installed pane on the left, select Code.
- From the middle pane, select Class. Name this new class PayPalFunctions.cs.
- Click Add.
The new class file is displayed in the editor.
- Replace the default code with the following code:
using System; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Net; using System.Text; using System.Data; using System.Configuration; using System.Web; using WingtipToys; using WingtipToys.Models; using System.Collections.Generic; using System.Linq; public class NVPAPICaller { //Flag that determines the PayPal environment (live or sandbox) private const bool bSandbox = true; private const string CVV2 = "CVV2"; // Live strings. private string pEndPointURL = "https://api-3t.paypal.com/nvp"; private string host = "www.paypal.com"; // Sandbox strings. private string pEndPointURL_SB = "https://api-3t.sandbox.paypal.com/nvp"; private string host_SB = "www.sandbox.paypal.com"; private const string SIGNATURE = "SIGNATURE"; private const string PWD = "PWD"; private const string ACCT = "ACCT"; //Replace <Your API Username> with your API Username //Replace <Your API Password> with your API Password //Replace <Your Signature> with your Signature public string APIUsername = "<Your API Username>"; private string APIPassword = "<Your API Password>"; private string APISignature = "<Your Signature>"; private string Subject = ""; private string BNCode = "PP-ECWizard"; //HttpWebRequest Timeout specified in milliseconds private const int Timeout = 15000; private static readonly string[] SECURED_NVPS = new string[] { ACCT, CVV2, SIGNATURE, PWD }; public void SetCredentials(string Userid, string Pwd, string Signature) { APIUsername = Userid; APIPassword = Pwd; APISignature = Signature; } public bool ShortcutExpressCheckout(string amt, ref string token, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; host = host_SB; } string returnURL = "http://localhost:1234/Checkout/CheckoutReview.aspx"; string cancelURL = "http://localhost:1234/Checkout/CheckoutCancel.aspx"; NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "SetExpressCheckout"; encoder["RETURNURL"] = returnURL; encoder["CANCELURL"] = cancelURL; encoder["BRANDNAME"] = "Wingtip Toys Sample Application"; encoder["PAYMENTREQUEST_0_AMT"] = amt; encoder["PAYMENTREQUEST_0_ITEMAMT"] = amt; encoder["PAYMENTREQUEST_0_PAYMENTACTION"] = "Sale"; encoder["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD"; // Get the Shopping Cart Products WingtipToys.Logic.ShoppingCartActions myCartOrders = new WingtipToys.Logic.ShoppingCartActions(); List<CartItem> myOrderList = myCartOrders.GetCartItems(); for (int i = 0; i < myOrderList.Count; i++) { encoder["L_PAYMENTREQUEST_0_NAME" + i] = myOrderList[i].Product.ProductName.ToString(); encoder["L_PAYMENTREQUEST_0_AMT" + i] = myOrderList[i].Product.UnitPrice.ToString(); encoder["L_PAYMENTREQUEST_0_QTY" + i] = myOrderList[i].Quantity.ToString(); } string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); NVPCodec decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { token = decoder["TOKEN"]; string ECURL = "https://" + host + "/cgi-bin/webscr?cmd=_express-checkout" + "&token=" + token; retMsg = ECURL; return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public bool GetCheckoutDetails(string token, ref string PayerID, ref NVPCodec decoder, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; } NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "GetExpressCheckoutDetails"; encoder["TOKEN"] = token; string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { PayerID = decoder["PAYERID"]; return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public bool DoCheckoutPayment(string finalPaymentAmount, string token, string PayerID, ref NVPCodec decoder, ref string retMsg) { if (bSandbox) { pEndPointURL = pEndPointURL_SB; } NVPCodec encoder = new NVPCodec(); encoder["METHOD"] = "DoExpressCheckoutPayment"; encoder["TOKEN"] = token; encoder["PAYERID"] = PayerID; encoder["PAYMENTREQUEST_0_AMT"] = finalPaymentAmount; encoder["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD"; encoder["PAYMENTREQUEST_0_PAYMENTACTION"] = "Sale"; string pStrrequestforNvp = encoder.Encode(); string pStresponsenvp = HttpCall(pStrrequestforNvp); decoder = new NVPCodec(); decoder.Decode(pStresponsenvp); string strAck = decoder["ACK"].ToLower(); if (strAck != null && (strAck == "success" || strAck == "successwithwarning")) { return true; } else { retMsg = "ErrorCode=" + decoder["L_ERRORCODE0"] + "&" + "Desc=" + decoder["L_SHORTMESSAGE0"] + "&" + "Desc2=" + decoder["L_LONGMESSAGE0"]; return false; } } public string HttpCall(string NvpRequest) { string url = pEndPointURL; string strPost = NvpRequest + "&" + buildCredentialsNVPString(); strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode); HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url); objRequest.Timeout = Timeout; objRequest.Method = "POST"; objRequest.ContentLength = strPost.Length; try { using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream())) { myWriter.Write(strPost); } } catch (Exception e) { // No logging for this tutorial. } //Retrieve the Response returned from the NVP API call to PayPal. HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse(); string result; using (StreamReader sr = new StreamReader(objResponse.GetResponseStream())) { result = sr.ReadToEnd(); } return result; } private string buildCredentialsNVPString() { NVPCodec codec = new NVPCodec(); if (!IsEmpty(APIUsername)) codec["USER"] = APIUsername; if (!IsEmpty(APIPassword)) codec[PWD] = APIPassword; if (!IsEmpty(APISignature)) codec[SIGNATURE] = APISignature; if (!IsEmpty(Subject)) codec["SUBJECT"] = Subject; codec["VERSION"] = "88.0"; return codec.Encode(); } public static bool IsEmpty(string s) { return s == null || s.Trim() == string.Empty; } } public sealed class NVPCodec : NameValueCollection { private const string AMPERSAND = "&"; private const string EQUALS = "="; private static readonly char[] AMPERSAND_CHAR_ARRAY = AMPERSAND.ToCharArray(); private static readonly char[] EQUALS_CHAR_ARRAY = EQUALS.ToCharArray(); public string Encode() { StringBuilder sb = new StringBuilder(); bool firstPair = true; foreach (string kv in AllKeys) { string name = HttpUtility.UrlEncode(kv); string value = HttpUtility.UrlEncode(this[kv]); if (!firstPair) { sb.Append(AMPERSAND); } sb.Append(name).Append(EQUALS).Append(value); firstPair = false; } return sb.ToString(); } public void Decode(string nvpstring) { Clear(); foreach (string nvp in nvpstring.Split(AMPERSAND_CHAR_ARRAY)) { string[] tokens = nvp.Split(EQUALS_CHAR_ARRAY); if (tokens.Length >= 2) { string name = HttpUtility.UrlDecode(tokens[0]); string value = HttpUtility.UrlDecode(tokens[1]); Add(name, value); } } } public void Add(string name, string value, int index) { this.Add(GetArrayName(index, name), value); } public void Remove(string arrayName, int index) { this.Remove(GetArrayName(index, arrayName)); } public string this[string name, int index] { get { return this[GetArrayName(index, name)]; } set { this[GetArrayName(index, name)] = value; } } private static string GetArrayName(int index, string name) { if (index < 0) { throw new ArgumentOutOfRangeException("index", "index cannot be negative : " + index); } return name + index; } } - Add the API credentials (API Username, API Password, and Signature) that you displayed earlier in this tutorial so that you can make function calls to the PayPal testing environment.
public string APIUsername = "<Your API Username>"; private string APIPassword = "<Your API Password>"; private string APISignature = "<Your Signature>"; - SetExpressCheckout function
- GetExpressCheckoutDetails function
- DoExpressCheckoutPayment function
- 1. Right-click the project name (WingtipToys) in Solution Explorer and select Properties.
- 2. In the left column, select the Web tab.
- 3. Retrieve the port number from the Project Url box.
- 4. Update the returnURL and cancelURL in the PayPal class (NVPAPICaller) in the PayPalFunctions.cs file
to use the port number of your web application:
string returnURL = "http://localhost:<Your Port Number>/Checkout/CheckoutReview.aspx"; string cancelURL = "http://localhost:<Your Port Number>/Checkout/CheckoutCancel.aspx"; - Open the ShoppingCart.aspx file.
- Scroll to the bottom of the file and find the
<!--Checkout Placeholder -->comment. - Replace the comment with an
ImageButtoncontrol so that the mark up appears as follows: - In the ShoppingCart.aspx.cs file after the
UpdateBtn_Clickevent handler, add theCheckOutBtn_Clickevent handler: - In the ShoppingCart.aspx.cs file, add a reference to the
CheckoutBtn, so that the new image button is referenced as follows: - Save your changes to both the ShoppingCart.aspx file and the ShoppingCart.aspx.cs file.
- From the menu, select Build -> Build Wingtip Toys.
The project will be rebuilt with the newly added ImageButton control. - 1. From the Checkout folder, open the code-behind file named CheckoutStart.aspx.cs.
- 2. Replace the existing code with the following:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WingtipToys.Checkout { public partial class CheckoutStart : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; if (Session["payment_amt"] != null) { string amt = Session["payment_amt"].ToString(); bool ret = payPalCaller.ShortcutExpressCheckout(amt, ref token, ref retMsg); if (ret) { Session["token"] = token; Response.Redirect(retMsg); } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } else { Response.Redirect("CheckoutError.aspx?ErrorCode=AmtMissing"); } } } } - 1. In the Checkout folder, open the page named CheckoutReview.aspx.
- 2. Replace the existing markup with the following:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutReview.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutReview" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server"> <h1>Order Review</h1> <p></p> <h3 style="padding-left: 33px">Products:</h3> <asp:GridView ID="OrderItemList" runat="server" AutoGenerateColumns="False" GridLines="Both" CellPadding="10" Width="500" BorderColor="#efeeef" BorderWidth="33"> <Columns> <asp:BoundField DataField="ProductId" HeaderText=" Product ID" /> <asp:BoundField DataField="Product.ProductName" HeaderText=" Product Name" /> <asp:BoundField DataField="Product.UnitPrice" HeaderText="Price (each)" DataFormatString="{0:c}"/> <asp:BoundField DataField="Quantity" HeaderText="Quantity" /> </Columns> </asp:GridView> <asp:DetailsView ID="ShipInfo" runat="server" AutoGenerateRows="false" GridLines="None" CellPadding="10" BorderStyle="None" CommandRowStyle-BorderStyle="None"> <Fields> <asp:TemplateField> <ItemTemplate> <h3>Shipping Address:</h3> <br /> <asp:Label ID="FirstName" runat="server" Text='<%#: Eval("FirstName") %>'></asp:Label> <asp:Label ID="LastName" runat="server" Text='<%#: Eval("LastName") %>'></asp:Label> <br /> <asp:Label ID="Address" runat="server" Text='<%#: Eval("Address") %>'></asp:Label> <br /> <asp:Label ID="City" runat="server" Text='<%#: Eval("City") %>'></asp:Label> <asp:Label ID="State" runat="server" Text='<%#: Eval("State") %>'></asp:Label> <asp:Label ID="PostalCode" runat="server" Text='<%#: Eval("PostalCode") %>'></asp:Label> <p></p> <h3>Order Total:</h3> <br /> <asp:Label ID="Total" runat="server" Text='<%#: Eval("Total", "{0:C}") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> </Fields> </asp:DetailsView> <p></p> <hr /> <asp:Button ID="CheckoutConfirm" runat="server" Text="Complete Order" OnClick="CheckoutConfirm_Click" /> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server"> </asp:Content> - Open the code-behind page named CheckoutReview.aspx.cs and replace the existing code with the following:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; namespace WingtipToys.Checkout { public partial class CheckoutReview : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; string PayerID = ""; NVPCodec decoder = new NVPCodec(); token = Session["token"].ToString(); bool ret = payPalCaller.GetCheckoutDetails(token, ref PayerID, ref decoder, ref retMsg); if (ret) { Session["payerId"] = PayerID; var myOrder = new Order(); myOrder.OrderDate = Convert.ToDateTime(decoder["TIMESTAMP"].ToString()); myOrder.Username = User.Identity.Name; myOrder.FirstName = decoder["FIRSTNAME"].ToString(); myOrder.LastName = decoder["LASTNAME"].ToString(); myOrder.Address = decoder["SHIPTOSTREET"].ToString(); myOrder.City = decoder["SHIPTOCITY"].ToString(); myOrder.State = decoder["SHIPTOSTATE"].ToString(); myOrder.PostalCode = decoder["SHIPTOZIP"].ToString(); myOrder.Country = decoder["SHIPTOCOUNTRYCODE"].ToString(); myOrder.Email = decoder["EMAIL"].ToString(); myOrder.Total = Convert.ToDecimal(decoder["AMT"].ToString()); // Verify total payment amount as set on CheckoutStart.aspx. try { decimal paymentAmountOnCheckout = Convert.ToDecimal(Session["payment_amt"].ToString()); decimal paymentAmoutFromPayPal = Convert.ToDecimal(decoder["AMT"].ToString()); if (paymentAmountOnCheckout != paymentAmoutFromPayPal) { Response.Redirect("CheckoutError.aspx?" + "Desc=Amount%20total%20mismatch."); } } catch (Exception ex) { Response.Redirect("CheckoutError.aspx?" + "Desc=Amount%20total%20mismatch."); } // Get DB context. ProductContext _db = new ProductContext(); // Add order to DB. _db.Orders.Add(myOrder); _db.SaveChanges(); // Get the shopping cart items. WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions(); List<CartItem> myOrderList = usersShoppingCart.GetCartItems(); // Add OrderDetail information to the DB for each product purchased. for (int i = 0; i < myOrderList.Count; i++) { // Create a new OrderDetail object. var myOrderDetail = new OrderDetail(); myOrderDetail.OrderId = myOrder.OrderId; myOrderDetail.Username = User.Identity.Name; myOrderDetail.ProductId = myOrderList[i].ProductId; myOrderDetail.Quantity = myOrderList[i].Quantity; myOrderDetail.UnitPrice = myOrderList[i].Product.UnitPrice; // Add OrderDetail to DB. _db.OrderDetails.Add(myOrderDetail); _db.SaveChanges(); } // Set OrderId. Session["currentOrderId"] = myOrder.OrderId; // Display Order information. List<Order> orderList = new List<Order>(); orderList.Add(myOrder); ShipInfo.DataSource = orderList; ShipInfo.DataBind(); // Display OrderDetails. OrderItemList.DataSource = myOrderList; OrderItemList.DataBind(); } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } } protected void CheckoutConfirm_Click(object sender, EventArgs e) { Session["userCheckoutCompleted"] = "true"; Response.Redirect("~/Checkout/CheckoutComplete.aspx"); } } } - 1. In the Checkout folder, open the page named CheckoutComplete.aspx.
- 2. Replace the existing markup with the following:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutComplete.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutComplete" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server"> <h1>Checkout Complete</h1> <p></p> <h3>Payment Transaction ID:</h3> <asp:Label ID="TransactionId" runat="server"></asp:Label> <p></p> <h3>Thank You!</h3> <p></p> <hr /> <asp:Button ID="Continue" runat="server" Text="Continue Shopping" OnClick="Continue_Click" /> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server"> </asp:Content> - 3. Open the code-behind page named CheckoutComplete.aspx.cs and replace the existing code with the following:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using WingtipToys.Models; namespace WingtipToys.Checkout { public partial class CheckoutComplete : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // Verify user has completed the checkout process. if (Session["userCheckoutCompleted"] != "true") { Session["userCheckoutCompleted"] = ""; Response.Redirect("CheckoutError.aspx?" + "Desc=Unvalidated%20Checkout."); } NVPAPICaller payPalCaller = new NVPAPICaller(); string retMsg = ""; string token = ""; string finalPaymentAmount = ""; string PayerID = ""; NVPCodec decoder = new NVPCodec(); token = Session["token"].ToString(); PayerID = Session["payerId"].ToString(); finalPaymentAmount = Session["payment_amt"].ToString(); bool ret = payPalCaller.DoCheckoutPayment(finalPaymentAmount, token, PayerID, ref decoder, ref retMsg); if (ret) { // Retrieve PayPal confirmation value. string PaymentConfirmation = decoder["PAYMENTINFO_0_TRANSACTIONID"].ToString(); TransactionId.Text = PaymentConfirmation; ProductContext _db = new ProductContext(); // Get the current order id. int currentOrderId = -1; if (Session["currentOrderId"] != "") { currentOrderId = Convert.ToInt32(Session["currentOrderID"]); } Order myCurrentOrder; if (currentOrderId >= 0) { // Get the order based on order id. myCurrentOrder = _db.Orders.Single(o => o.OrderId == currentOrderId); // Update the order to reflect payment has been completed. myCurrentOrder.PaymentTransactionId = PaymentConfirmation; // Save to DB. _db.SaveChanges(); } // Clear shopping cart. WingtipToys.Logic.ShoppingCartActions usersShoppingCart = new WingtipToys.Logic.ShoppingCartActions(); usersShoppingCart.EmptyCart(); // Clear order id. Session["currentOrderId"] = ""; } else { Response.Redirect("CheckoutError.aspx?" + retMsg); } } } protected void Continue_Click(object sender, EventArgs e) { Response.Redirect("~/Default.aspx"); } } } - 1. Open the page named CheckoutCancel.aspx in the Checkout folder.
- 2. Replace the existing markup with the following:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutCancel.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutCancel" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server"> <h1>Checkout Cancelled</h1> <p></p> <h3>Your purchase has been cancelled.</h3> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server"> </asp:Content> - 1. Open the page named CheckoutError.aspx in the Checkout folder.
- 2. Replace the existing markup with the following:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CheckoutError.aspx.cs" Inherits="WingtipToys.Checkout.CheckoutError" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server"> <h1>Checkout Error</h1> <p></p> <table id="ErrorTable"> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("ErrorCode")%></td> </tr> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("Desc")%></td> </tr> <tr> <td class="field"></td> <td><%=Request.QueryString.Get("Desc2")%></td> </tr> </table> <p></p> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server"> </asp:Content> - Open a Web browser and navigate to https://developer.paypal.com.
- Login with your PayPal developer account that you created earlier in this tutorial.
For PayPal’s developer sandbox, you need to be logged in at https://developer.paypal.com to test express checkout. This only applies to PayPal’s sandbox testing, not to PayPal’s live environment. - In Visual Studio, press Ctrl+F5 to run the Wingtip Toys sample application.
After the database rebuilds, the browser will open and show the Default.aspx page. - Add three different products to the shopping cart by selecting the product category, such as “Cars” and then clicking Add to Cart next to each product.
The shopping cart will display the product you have selected. - Click the PayPal button to checkout.
Checking out will require that you have a user account for the Wingtip Toys sample application.
- Click the Google link on the right of the page to log in with an existing gmail.com email account.
If you do not have a gmail.com account, you can create one for testing purposes at www.gmail.com.

- Sign in with your gmail account and password.

- Click Allow to allow the Wingtip Toys sample application to work with your gmail account.

- Click the Log in button to register your gmail account with your Wingtip Toys sample application user name.

- On the PayPal test site, add your buyer email address and password that you created earlier in this tutorial, then click the Log In button.

- Agree to the PayPal policy and click the Agree and Continue button.

- Review the order information on the PayPal testing environment review page and click Continue.

- On the CheckoutReview.aspx page, verify the order amount and view the generated shipping address. Then, click the Complete Order button.

The CheckoutComplete.aspx page is displayed with a payment transaction ID.

- In Visual Studio, select the Show All Files icon at the top of Solution Explorer to allow you to expand the App_Data folder.
- Expand the App_Data folder.
- Right-click the Wingtiptoys.mdf database file and select Open.
Database Explorer is displayed. - Expand the Tables folder.
- Right-click the Orders table and select Show Table Data.
The Orders table is displayed. - Review the PaymentTransactionID column to confirm successful transactions.

- Close the Orders table window.
- In the Database Explorer, right-click the OrderDetails table and select Show Table Data.
- Review the OrderId and Username values in the OrderDetails table. Note that these values match the OrderId and Username values included in the Orders table.
- Close the OrderDetails table window.
- Right-click the Wingtip Toys database file (Wingtiptoys.mdf) and select Close Connection.
- If you do not see the Solution Explorer window, click Solution Explorer at the bottom of the Database Explorer window to show the Solution Explorer again.
Add a Web.config File
By adding a new Web.config file to the Checkout folder, you will be able to restrict access to all the pages contained in the folder.
The Web.config file specifies that all unknown users of the Web application must be denied access to the pages contained in the Checkout folder. However, if the user has registered an account and is logged on, they will be a known user and will have access to the pages in the Checkout folder.
Enabling Logins from Other Sites Using OAuth and OpenID
ASP.NET Web Forms provides enhanced options for membership and authentication. These enhancements include the new OAuth and OpenID providers. Using these providers, you can let users log into your site using their existing credentials from Facebook, Twitter, Windows Live, and Google. For example, to log in using a Facebook account, users can just choose a Facebook option, which redirects them to the Facebook login page where they enter their user credentials. They can then associate the Facebook login with their account on your site. A related enhancement to the ASP.NET Web Forms membership features is that users can associate multiple logins (including logins from social networking sites) with a single account on your website.
When you add an OAuth provider (Facebook, Twitter, or Windows Live) to your ASP.NET Web Forms application, you must set the application ID (key) value and an application secret value. You add these values to the AuthConfig.cs file in your Web Forms application. Additionally, you must create an application on the external site (Facebook, Twitter, or Windows Live). When you create the application on the external site you can get the application keys that you'll need in order to invoke the login feature for those sites.
Note Windows Live applications only accept a live URL for a working website, so you cannot use a local website URL for testing logins.
For sites that use an OpenID provider (Google), you do not have to create an application on the external site.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Membership.OpenAuth;
namespace WingtipToys
{
internal static class AuthConfig
{
public static void RegisterOpenAuth()
{
// See http://go.microsoft.com/fwlink/?LinkId=252803 for details on setting up this ASP.NET
// application to support logging in via external services.
//OpenAuth.AuthenticationClients.AddTwitter(
// consumerKey: "your Twitter consumer key",
// consumerSecret: "your Twitter consumer secret");
//OpenAuth.AuthenticationClients.AddFacebook(
// appId: "your Facebook app id",
// appSecret: "your Facebook app secret");
//OpenAuth.AuthenticationClients.AddMicrosoft(
// clientId: "your Microsoft account client id",
// clientSecret: "your Microsoft account client secret");
OpenAuth.AuthenticationClients.AddGoogle();
}
}
}
When you run the Wingtip Toys sample application, you will have the option to login to your Google account and associate your Wingtip Toys account with the Google account.
Modifying Login Functionality
As previously mentioned in this tutorial series, much of the user registration functionality has been included in the ASP.NET Web Forms template by default. Now you will modify the default Login.aspx and Register.aspx pages to call the MigrateCart method. The MigrateCart method associates a newly logged in user with an anonymous shopping cart. By associating the user and shopping cart, the Wingtip Toys sample application will be able to maintain the shopping cart of the user between visits.
For now, you can ignore the warning that there is no definition for the MigrateCart method. You will be adding it a bit later in this tutorial.
The Login control supports an OnLoggedIn handler. You modified the markup of the Login control on the Login.aspx page by adding the OnLoggedIn attribute. When a user has finished logging on to the web site using the Login control, the event handler in the Login.aspx.cs file that the OnLoggedIn attribute points to, LoginCtrl_LoggedIn, is triggered.
When the LoginCtrl_LoggedIn event handler that you added to Login.aspx.cs is called, a new instance of the shopping cart named usersShoppingCart is created. The ID of the shopping cart (a GUID) is retrieved and set to the cartId variable. Then, the MigrateCart method is called, passing both the cartId and the name of the logged-on user to the method. When the shopping cart is migrated, the GUID used to identify the anonymous shopping cart is replaced with the user name.
In addition to modifying the Login.aspx page to migrate the shopping cart when the user logs in,
you must also modify the Register.aspx page to migrate the shopping cart when the user creates a new account and logs in.
Notice that you used the same code in the RegisterUser_CreatedUser event handler that you used in the LoginCtrl_LoggedIn event handler. When the user registers or logs on, a call to the MigrateCart method will be made.
Migrating the Shopping Cart
Now that you have the log-in and registration process updated, you can add the code to migrate the shopping cart—the MigrateCart method.
The MigrateCart method uses the existing cartId to find the shopping cart of the user. Next, the code loops through all the shopping cart items and replaces the CartId property (as specified by the CartItem schema) with the logged-in user name.
Updating the Database Connection
If you are following this tutorial using the prebuilt Wingtip Toys sample application, you must recreate the default membership database. By modifying the default connection string, the membership database will be created the next time the application runs.
Integrating PayPal
PayPal is a web-based billing platform that accepts payments by online merchants. This tutorial next explains how to integrate PayPal’s Express Checkout functionality into your application. Express Checkout allows your customers to use PayPal to pay for the items they have added to their shopping cart.
Create PaylPal Test Accounts
To use the PayPal testing environment, you must create and verify a developer test account. You will use the developer test account to create a buyer test account and a seller test account. The developer test account credentials also will allow the Wingtip Toys sample application to access the PayPal testing environment.
You will need the displayed API credentials (API Username, API Password, and Signature) to make API calls from the Wingtip Toys sample application to the PayPal testing environment. You will add the credentials in the next step.
Add PayPal Class and API Credentials
You will place the majority of the PayPal code into a single class. This class contains the methods used to communicate with PayPal. Also, you will add your PayPal credentials to this class.
The NVPAPICaller class contains the majority of the PayPal functionality. The code in the class provides the methods needed to make a test purchase from the PayPal testing environment. The following three PayPal functions are used to make purchases:
The ShortcutExpressCheckout method collects the test purchase information and product details from the shopping cart and calls the SetExpressCheckout PayPal function. The GetCheckoutDetails method confirms purchase details and calls the GetExpressCheckoutDetails PayPal function before making the test purchase. The DoCheckoutPayment method completes the test purchase from the testing environment by calling the DoExpressCheckoutPayment PayPal function. The remaining code supports the PayPal methods and process, such as encoding strings, decoding strings, processing arrays, and determining credentials.
Note PayPal allows you to include optional purchase details based on PayPal’s API specification. By extending the code in the Wingtip Toys sample application, you can include localization details, product descriptions, tax, a customer service number, as well as many other optional fields.
Notice that the return and cancel URLs that are specified in the ShortcutExpressCheckout method use a port number.
string returnURL = "http://localhost:1234/Checkout/CheckoutReview.aspx";
string cancelURL = "http://localhost:1234/Checkout/CheckoutCancel.aspx";
When Visual Web Developer runs a web project, a random port is used for the web server. As shown above, the port number is 1234. When you run the application, you'll probably see a different port number. Your port number needs to be set in the above code so that you can successful run the Wingtip
Toys sample application at the end of this tutorial. The next section of this tutorial explains how to retrieve the local host port number and update the PayPal class.
Update the LocalHost Port Number in the PayPal Class
The Wingtip Toys sample application purchases products by navigating to the PayPal testing site and returning to your local instance of the Wingtip Toys sample application. In order to have PayPal return to the correct URL, you need to specify the port number of the locally running sample application in the PayPal code mentioned above.
Now the code that you added will match the expected port for your local Web application. PayPal will be able to return to the correct URL on your local machine.
Add the PayPal Checkout Button
Now that the primary PayPal functions have been added to the sample application, you can begin adding the markup and code needed to call these functions. First, you must add the checkout button that the user will see on the shopping cart page.
<asp:ImageButton ID="CheckoutImageBtn" runat="server"
ImageUrl="https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif"
Width="145" AlternateText="Check out with PayPal"
OnClick="CheckoutBtn_Click"
BackColor="Transparent" BorderWidth="0" />
protected void CheckoutBtn_Click(object sender, ImageClickEventArgs e)
{
ShoppingCartActions usersShoppingCart = new ShoppingCartActions();
Session["payment_amt"] = usersShoppingCart.GetTotal();
Response.Redirect("Checkout/CheckoutStart.aspx");
}
protected void Page_Load(object sender, EventArgs e)
{
ShoppingCartActions usersShoppingCart = new ShoppingCartActions();
decimal cartTotal = 0;
cartTotal = usersShoppingCart.GetTotal();
if (cartTotal > 0)
{
// Display Total.
lblTotal.Text = String.Format("{0:c}", cartTotal);
}
else
{
LabelTotalText.Text = "";
lblTotal.Text = "";
ShoppingCartTitle.InnerText = "Shopping Cart is Empty";
UpdateBtn.Visible = false;
CheckoutImageBtn.Visible = false;
}
}
Send Purchase Details to PayPal
When the user clicks the Checkout button on the shopping cart page (ShoppingCart.aspx), they’ll begin the purchase process. The following code calls the first PayPal function needed to purchase products.
When the user of the application clicks the Checkout button on the shopping cart page, the browser will navigate to the CheckoutStart.aspx page. When the CheckoutStart.aspx page loads, the ShortcutExpressCheckout method is called. At this point, the user is transferred to the PayPal testing web site. On the PayPal site, the user enters their PayPal credentials, reviews the purchase details, accepts the PayPal agreement and returns to the Wingtip Toys sample application where the ShortcutExpressCheckout method completes. When the ShortcutExpressCheckout method is complete, it will redirect the user to the CheckoutReview.aspx page specified in the ShortcutExpressCheckout method. This allows the user to review the order details from within the Wingtip Toys sample application.
Review Order Details
After returning from PayPal, the CheckoutReview.aspx page of the Wingtip Toys sample application displays the order details. This page allows the user to review the order details before purchasing the products. The CheckoutReview.aspx page must be created as follows:
The DetailsView control is used to display the order details that have been returned from PayPal. Also, the above code saves the order details to the Wingtip Toys database as an OrderDetail object. When the user clicks on the Complete Order button, they are redirected to the CheckoutComplete.aspx page.
Complete Purchase
CheckoutComplete.aspx page makes the purchase from PayPal. As mentioned above, the user must click on the Complete Order button before the application will navigate to the CheckoutComplete.aspx page.
When the CheckoutComplete.aspx page is loaded, the DoCheckoutPayment method is called. As mentioned earlier, the DoCheckoutPayment method completes the purchase from the PayPal testing environment. Once PayPal has completed the purchase of the order, the CheckoutComplete.aspx page displays a payment transaction ID to the purchaser.
Handle Cancel Purchase
If the user decides to cancel the purchase, they will be directed to the CheckoutCancel.aspx page where they will see that their order has been cancelled.
Handle Purchase Errors
Errors during the purchase process will be handled by the CheckoutError.aspx page. The code-behind of the CheckoutStart.aspx page, the CheckoutReview.aspx page, and the CheckoutComplete.aspx page will each redirect to the CheckoutError.aspx page if an error occurs.
The CheckoutError.aspx page is displayed with the error details when an error occurs during the checkout process.
Running the Application
Run the application to see how to purchase products.
Reviewing the Database
By reviewing the updated data in the Wingtip Toys sample application database after running the application, you can confirm that the application successfully recorded the purchase of the products.
You can inspect the data contained in the Wingtiptoys.mdf database file by using the Database Explorer window (Server Explorer window in Visual Studio) as you did earlier in this tutorial series.
Summary
In this tutorial you added order and order detail schemas to track the purchase of products. You also integrated PayPal functionality into the Wingtip Toys sample application.
Disclaimer
This tutorial contains sample code. Such sample code is provided “as is” without warranty of any kind. Accordingly, Microsoft does not guarantee the accuracy, integrity, or quality of the sample code. You agree to use the sample code at your own risk. Under no circumstances will Microsoft be liable to you in any way for any sample code, content, including but not limited to, any errors or omissions in any sample code, content, or any loss or damage of any kind incurred as a result of the use of any sample code. You are hereby notified and do hereby agree to indemnify, save and hold Microsoft harmless from and against any and all loss, claims of loss, injury or damage of any kind including, without limitation, those occasioned by or arising from material that you post, transmit, use or rely on including, but not limited to, the views expressed therein.

Comments (0) RSS Feed