Hack 43 Provide Options with ASP.NET Web Controls

figs/moderate.gif figs/hack43.gif

Create custom web controls in ASP.NET to allow customers to specify product options with their orders.

As described in [Hack #28], you can send option information to PayPal so that it appears as part of the transaction along with other item details, such as quantity and price. This information is vital to order fulfillment and also allows customers to review fully what they are buying.

Sending this information to PayPal is simple. You can do it in one of two ways:

  • Send the information through the URL as parameters.

  • Send the information through form submission using HTTP POST.

PayPal looks for four parameters when information is passed to it by its payment controls: option name one, option value one, option name two, and option value two. Geeks came up with the naming here, and to us geeks (you might be one and find comfort in this), traditional base-10 numeric series start with the number 0 and end with the number 9.[2] So, the first option is called option 0 and the second is called option 1, and when you pass this information to PayPal, it looks something like this:

[2] Actually, there are many numeric patterns that start at zero, such as the way we track the minutes in an hour.





This information can be passed to PayPal through a URL, like this:


or through an HTTP form POST:

<input type=hidden name="on0" value="Size">

<input type=hidden name="os0" value="Large">

PayPal will include this information in the description section of the item, so your user can view it at the time of the sale.

4.17.1 Using the .NET Payment Controls

Collecting order details is fairly straightforward with traditional scripting languages (e.g., ASP or PERL). Simply display the information for each product, with relevant options, in a single form for each product (this example uses Active Server Pages with VBScript):

<% while not rs.eof%>

<form action="https://www.paypal.com/cgi-bin/webscr" method=POST>

<!-- The product name and description go here -->

<input type=hidden name="on0" value="Size">

<select name=os0>

  <option value="Large">Large</option> 

  <option value="Medium"> Medium </option> 

  <option value="Small">Small </option> 


<input type=hidden name="on1" value="Color">

<select name=os1>

  <option value="Yellow"> Yellow </option> 

  <option value="Blue"> Blue </option> 

  <option value="Gold"> Gold </option> 


<input type=submit value="Add To Basket">






This code provides purchase options with drop-down listboxes [Hack #32] to restrict the inputs on the form.

Using product options with the .NET payment controls, however, offers a bit of a challenge, given that an ASP.NET page only lets you have one <form> tag per ASP.NET web page, thus allowing it to maintain page state properly.

To get around the single-form limitation, you can use the Click event of the Payment Controls to add the option controls at runtime. The first thing you must do is set the control to use the postback routine (UseFormGet=false) and disallow the pop-up command (UsePopUp=false), so that PayPal can glean the options from the postback.

This is a delicate process, especially when using the .NET-native data controls (e.g., DataList, Repeater, or DataGrid). You need to understand which events fire and in what order, because this can affect how your option controls are populated. You will be dealing with the Click event of the PayPal control, not one of the events of the data controls, which can get a little confusing. Thus, it's best to skip ahead to the good part: how to do it!

4.17.2 Creating Your Own PayPal Control

If you are a serious geek, you've probably already created your own Custom Server Control to handle the intricacies of gathering option information from the ViewState. Or, at the very least, you have something mapped out in your head. However, there might be something simpler in the following approach, and I appeal to you to quell your ADD for another five minutes. Custom Server Controls can be useful, but they can also (and often do) add a layer of complication (a.k.a. lots of code) to an otherwise simple task.

This approach starts with a user control and then populates its options from the product information you pass to it. User controls allow you to encapsulate functionality for individual UI components, which this is, so you don't need to write the same code twice or create spaghetti code in order to find the control you want hidden within your page.

This example uses PayPal's Shopping Cart and the Add to Cart Button, and it is written in C# using ASP.NET.

Create a user control called AddToCartOptions.ascx, and add the PayPal AddToCart server control, along with a RadioButtonList called radColors and a DropDownList called ddSize:



    <td><asp:dropdownlist id="ddSize" runat="server">

      <asp:ListItem Value="Small" Selected="True">Small</asp:ListItem>

      <asp:ListItem Value="Medium">Medium</asp:ListItem>

      <asp:ListItem Value="Large">Large</asp:ListItem>



    <td width="290">

      <asp:radiobuttonlist id="radColors"

                 runat="server" RepeatDirection="Horizontal" Width="280px"


      <asp:ListItem Value="Black">Black</asp:ListItem>

      <asp:ListItem Value="Blue">Blue</asp:ListItem>

      <asp:ListItem Value="Paisley">Paisley</asp:ListItem>

      <asp:ListItem Value="Polka Dots">Polka Dots</asp:ListItem>




      <cc1:addtocartbutton id="AddToCartButton1" runat="server"

             BusinessEmail="mybusinessemail" ItemNumber="xxxx" ItemName="Small Army Men"

             Amount="1.02" ReturnUrl="http://myserver/myhandler.aspx"


             Shipping=".01" Tax=".01" UsePopup="false" UseFormGet="false" 





Make sure to set the UseFormGet and UsePopup values to false, which will force a postback to the server. Next, in the code behind the page, add the properties or fields that will be set by the calling page:

public string ItemName;

public string ItemNumber;

public string Amount;

In the Page_Load event of the ASP.NET page, populate these values, as well as those of your options (in case you need to populate the option controls from the database):

//expose the properties as needed






  throw new Exception("Invalid value for a double: "   +Amount.ToString( ));


Add the event handler for the button Click event in the InitializeComponent() method:

this.AddToCartButton1.Click+=new System.EventHandler(this.AddClicked);

Finally, add the method to handle the PayPal button Click event, which reads the values of the controls and populates the AddToCartButton1 options accordingly:

private void AddClicked(object sender, System.EventArgs e) {






This method populates the control just before the output is rendered to the browser, which redirects the user to PayPal for the purchase.

It should be noted at this point that the geeks who created this control appear not to be the same geeks who created the aforementioned naming convention at PayPal: the geeks who created this control are not Zeroians but nonbelievers in the primary status of the Almighty Zero. Thus, Option1FieldName represents option number 1, which, in turn, corresponds to on0.

To reward those of you who are patient enough to have made it this far, here is a final piece of wisdom: .NET is notoriously tricky when it comes to marrying the concept of events to a stateless medium such as a web page. There is a mess of events that goes into every request and every object; adding more objects to a page only complicates matters, especially when those objects have event sets of their own.

If you have ever tried to run logic using the events in a user control?which is, itself, part of a DataList or Repeater?you have undoubtedly run into the Event Freak ShowTM, wherein you cannot get your events to work properly or fire in the correct order, despite using all the Page.Postback tests in existence. If not done properly, the options selected by your customer will be overwritten by the initialization routine of the page, and the same meaningless information will be passed to PayPal.

The solution to this problem lies in setting the DataSource property, not the DataBind( ) method, of your Repeater or DataList. Consider that the user control, discussed earlier in this hack, is in a Repeater called MyRepeater:

<asp:Repeater id=MyRepeater Runat="server">




  runat="server"   ItemName='<%#DataBinder.Eval(Container.DataItem, "ModelName")%>' 

  ItemNumber='<%#DataBinder.Eval(Container.DataItem,   "ModelNumber")%>'   Amount=

'<%#DataBinder.Eval(Container.DataItem, "UnitCost")%>'   >




To preserve the ViewState of the user control, be sure to DataBind the Repeater:

MyRepeater.DataSource = MyDataSource;


  MyRepeater.DataBind( );


The DataBind() method overwrites whatever state the user control was in when submitted by the customer, so you need to handle the population of this control and test for the postback. Setting the DataSource at runtime apparently helps the control remember the ViewState of its child controls.

Thus, your Repeater (or DataList) and all of its controls will maintain their ViewState, and your customer's option selections will be passed properly.

?Rob Conery