Developer Blog
Articles about Using Microsoft Developer Tools

Quick-and-Dirty, Buy-Now Buttons in ASP.NET

Sunday, February 14, 2010 6:34 AM by jonwood

NOTE: This article has been updated and moved to: Quick-and-Dirty, Buy-Now Buttons in ASP.NET.

I posted previously about an issue when incorporating PayPal Buy-Now buttons on an ASP.NET web form. Basically, after presenting a few hacks, I pointed out that you could simply place the form items directly within your ASP.NET form. (See that post for more info.)

However, for quick and dirty Buy Now buttons, there is a far simpler approach. You can simply use an anchor link and provide parameters as query arguments. Listing 1 demonstrates this technique.

<a href="https://www.paypal.com/cgi-bin/webscr
  ?cmd=_xclick&business=MyEmail
  &item_name=Widget
  &amount=29.00
  &undefined_quantity=1
  &currency_code=USD">
<img src="http://www.paypal.com/en_US/i/btn/x-click-but23.gif"
  border="0" alt="Buy Now Using PayPal" />
</a>

Listing 1: Simple Implementation of PayPal Buy Now Button

Note that the href value of the a tag should all go on a single line. I wrapped the text here only so it would fit within the page. MyEmail should be replaced with the email address associated with your PayPal account.

As you can see, we provided several bits of information. After our account email, we provide an item name, the price (amount), and I included the optional currency code.

The undefined_quantity parameter allows the user to enter the quantity, and PayPal will calculate the total based on the price you specified and the quantity entered by the user. Alternatively, you can instead say quantity=5 to fix the quantity so that the user cannot edit it.

Although that should be all you need for a simple Buy-Now button, Table 1 lists some additional arguments you can include.

Argument Description
business Email address associated with seller’s PayPal account
quantity Quantity of items being sold
undefined_quantity Allows user to edit quantity
item_name Name of item
item_number Optional item number
amount Price of each item (without currency symbol)
undefined_amount Allows user to edit the amount (good for donations)
shipping Price of shipping
currency_code Code for type of currency (Default appears to be USD)
first_name Customer’s first name
last_name Customer’s last name
address1 Customer’s first address line
address2 Customer’s second address line
city Customer’s city
state Customer’s state
zip Customer’s zip code
email Customer’s email address
night_phone_a Customers telephone area code
night_phone_b Customers telephone prefix
night_phone_c Remainder of customer’s telephone number

Table 1: Additional Query Arguments

The arguments listed in Table 1 are not exhaustive. Other arguments are available as well. For the simple task I’m describing, this list should be more than enough.

Of course, you also have the option of programmatically forming this link and then using code to redirect to it. This allows you, for example, to set the quantity based on a value entered by the user on your own site.

Note that there are some potential downsides to this technique. For starters, the link is fully visible for anyone to see. Of course, it won’t include your PayPal password so that type of information is not exposed. But your account email is visible.

Users can also save your web page to their computer, and then edit the link. So, for example, they could change the price, load the edited page, and click the link. So you need to verify the correct amount was paid when processing orders.

Nonetheless, for a simply Buy-Now button, this technique works great and couldn’t be simpler to implement.

Implementing Non-ASP.NET Posts in ASP.NET

Thursday, January 22, 2009 6:14 AM by jonwood

NOTE: This article has been updated and moved to: Quick-and-Dirty, Buy-Now Buttons in ASP.NET.

When interfacing with some resource on the Web, you are sometimes provided with a little snippet of HTML code. For example, to insert a PayPal button on your Website, PayPal provides HTML code that may look something like this.

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
  <input type="hidden" name="cmd" value="_xclick">
  <input type="hidden" name="business" value="bob@domain.com">
  <input type="hidden" name="lc" value="US">
  <input type="hidden" name="item_name" value="Widget">
  <input type="hidden" name="amount" value="100.00">
  <input type="hidden" name="currency_code" value="USD">
  <input type="hidden" name="bn"
    value="PP-BuyNowBF:btn_buynow_LG.gif:NonHostedGuest">
  <input type="image"
    src="https://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif"
    border="0" name="submit" alt="">
  <img alt="" border="0"
    src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1"
    height="1">
</form>

HTML code to insert PayPal button

This snippet defines a <form> and several <input> tags, including a submit button. When the submit button is clicked, the items in the form are posted to the URL specified in the action attribute of the <form> tag. The target URL can then inspect the values of those <input> tags and perform the required operation.

However, you may find that such snippets don’t work correctly when inserted into an ASP.NET page. The main problem is that ASP.NET pages already define a form. In order for ASP.NET’s post-back mechanism to work, all the controls are placed within a <form> tag. Since HTML doesn’t allow nested <form> tags, inserting code like that shown above into an ASP.NET page causes problems.

There are a number of possible approaches to resolving this. Let me start with a couple that I think are less than ideal. The first approach is to simply move the inserted HTML code after the ending </form> tag of the ASP.NET form. Although you cannot nest <form> tags, it is okay to include multiple, non-nested <form> tags on the same page.

The result of this is that the page now has two separate <form> tags and you’ll find this generally works as expected. However, the problem here is that you lose some ASP.NET functionality. For example, the inserted HTML code comes after your ASP.NET form and so it is not really embedded within your ASP.NET content. And if you are using master pages, you cannot do this in content pages because everything in a content page is placed inside of the <form> tag defined in the master page.

The second approach is kind of an ugly hack and is shown below.

protected void btnPayPal_Click(object sender, ImageClickEventArgs e)
{
  System.Web.HttpContext.Current.Response.Clear();
  System.Web.HttpContext.Current.Response.Write("<html><head>");
  System.Web.HttpContext.Current.Response.Write(
    "</head><body onload='document.form1.submit()'>");
  System.Web.HttpContext.Current.Response.Write(
    "<form action='https://www.paypal.com/cgi-bin/webscr' " +
    "name='form1' method='post'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='cmd' value='_xclick'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='business' " +
    "value='bob@domain.com'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='lc' value='US'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='item_name' value='Widget'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='amount' value='100.00'>");
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='currency_code' value='USD'>
  System.Web.HttpContext.Current.Response.Write(
    "<input type='hidden' name='bn' " +
    "value='PP-BuyNowBF:btn_buynow_LG.gif:NonHostedGuest'>
  System.Web.HttpContext.Current.Response.Write(
    "<input type='image' " +
    "src='https://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif' " +
    "border='0' name='submit' alt=''>");
  System.Web.HttpContext.Current.Response.Write(
    "<img alt='' border='0' " +
    "src='https://www.paypal.com/en_US/i/scr/pixel.gif' " +
    "width='1' height='1'>");
  System.Web.HttpContext.Current.Response.Write("</form>");
  System.Web.HttpContext.Current.Response.Write("</body></html>");
  System.Web.HttpContext.Current.Response.End();
}

Ugly hack to perform non-ASP.NET postback

This code runs in response to the user clicking a button named btnPayPal. It dynamically creates an HTML page and then serves it to the user’s browser. The page created duplicates the form in our first listing but adds an onload attribute to the page’s <body> tag that causes the form to be submitted as soon as it loads and the user never sees this temporary page.

This is definitely an ugly hack. And, while it works, it has some problems. The main problem is if, after running the code, the user presses the Back button in their browser, the browser will reload this temporary page, which will cause the same action to happen again, which definitely has the potential to cause some nasty problems.

After exploring both of these options, I finally settled on a third approach that seems more straight forward and doesn’t seem to have any major problems. Beginning with ASP.NET 2.0, buttons have a property called PostBackUrl, which can be used to have that button post back to a page other than the page that contains the button.

At first glance, this didn’t seem helpful. ASP.NET buttons are designed to post back to ASP.NET pages. And I wanted to post back to an non-ASP.NET page. However, the post-back mechanism is the same in both cases. If we post back to a non-ASP.NET page, we’re going to send a lot of additional data—all of the form items on our ASP.NET form—to the target page. But, as long as their is no conflicting item names, which is unlikely, this really isn’t a problem.

Armed with this knowledge then, we can rewrite our original code block and insert it into an ASP.NET page.

<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="bob@domain.com">
<input type="hidden" name="lc" value="US">
<input type="hidden" name="item_name" value="Widget">
<input type="hidden" name="amount" value="100.00">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="bn"
  value="PP-BuyNowBF:btn_buynow_LG.gif:NonHostedGuest">
<asp:ImageButton ID="ImageButton1" runat="server"
  ImageUrl="https://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif"
  PostBackUrl="https://www.paypal.com/cgi-bin/webscr" />

PayPal button inserted into ASP.NET page

I’ve removed the <form> tag from this code. We don’t need it since our ASP.NET form already defines a <form> tag. I’ve removed the <img> and submit <input> tags and I’ve replaced them with an ImageButton. I set the ImageButton’s ImageUrl property to the same PayPal button image used in the original code and, finally, I set the PostBackUrl property to the URL specified in the action attribute of the <form> tag in our original HTML code.

The result is that, when the ImageButton is clicked, the entire form is posted to the PayPal URL. As I mentioned before, this includes all our other ASP.NET values, including the page’s ViewState data. But the ViewState data is encrypted and the PayPal site will only look at those values it is interested in.

The result is that I’ve inserted a PayPal button at any location within my ASP.NET form, I’m still able to make use of all ASP.NET features, PayPal gets all the values it needs to do its job, and there is no funny business if the user happens to hit the browser’s Back button from the PayPal site.