Saturday, June 6, 2009

PayPal Generated Buttons and .Net Forms

I have used PayPal for some web work I have done in the past. I would use a custom cart and then just pass the entire cart to PayPal. That's not so bad.

Recently I have had the chance to put up a simple site that is a lite e-commerce site. I didn't want to write a custom cart and all the other pages and logic that go with it. So I decided to use PayPal's cart. If you are not familiar with it here is a quick overview, you first generate some PayPal "Add to Cart" buttons then copy and paste the code on your site. And you have yourself a simple e-commerce site.

So as I started to build all my buttons I noticed that they have a form tag in them which means if you are using ASP.NET like me when you go to post to PayPal you will simply see your page refresh, that's because the default action of a aspx page is to post back to itself. Well there is the problem that and I will show you how I got around it.

First let me show you what the PayPal generated button HTML looks like:

<form target="paypal" action="https://www.paypal.com/cgi-bin/webscr"
method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="999999">
<table>
<tr><td><input type="hidden" name="on0" value="Quantity">
Quantity</td></tr><tr><td><select name="os0">
<option value="25">25 $50.00
<option value="50">50 $100.00
<option value="75">75 $112.00
<option value="100">100 $150.00
</select> </td></tr>
</table>
<input type="hidden" name="currency_code" value="USD">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_cart_SM.gif"
border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif"
width="1" height="1">
</form>

And let me show you my modified version (actually a user control):

Here is what my control looks like on my page (notice the use of the custom properties):
<crd:Card1Day ID="Card_999999" runat="server" ButtonId="999999"
OptionDays="30" />
Here is the html to my control:
<form target="_self" action="https://www.paypal.com/cgi-bin/webscr"
method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value='<%= ButtonId %>'>
<input type="hidden" name="on0" value="Quantity">
Quantity - Price
<br /><br />
<select name="os0">
<asp:Literal ID="ltOptions" runat="server"></asp:Literal>
</select>
<br /><br />
<input type="hidden" name="currency_code" value="USD">
<asp:ImageButton ID="imbAddToCart" runat="server"
PostBackUrl="https://www.paypal.com/cgi-bin/webscr"
ImageUrl="https://www.paypal.com/en_US/i/btn/btn_cart_SM.gif" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif"
width="1" height="1">
</form>
Two things to note about my control, the hidden input "hosted_button_id" is populated via 1 of 2 public properties of this control, in this case the property name is "ButtonId" and you can see how I am setting that property when I use the control on my aspx page. The other property "OptionDays" controls what options get rendered out.

The last and most important part of my control is my use of the ASP.NET ImageButton control inplace of the standard image input PayPal generated. I use this so that I can take advantage of the "PostBackUrl" property. This allow us to post to PayPal and allow users to add items to their PayPal cart.

Also because this is a control we can reuse it as many times as needed and it will be simple to change in the future.

And just for extra points here is my code-behind for the control to show how I am populating the options:

public partial class Card_1Day : System.Web.UI.UserControl
{
public string OptionDays { get; set; }
public string ButtonId { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
switch (OptionDays)
{
case "1":
ltOptions.Text = "<option value=\"25\">25 - $50.00";
ltOptions.Text += "<option value=\"50\">50 - $100.00";
ltOptions.Text += "<option value=\"75\">75 - $112.00";
ltOptions.Text += "<option value=\"100\">100 - $150.00";
break;
case "3":
ltOptions.Text = "<option value=\"25\">25 - $75.00";
ltOptions.Text += "<option value=\"50\">50 - $125.00";
ltOptions.Text += "<option value=\"75\">75 - $150.00";
ltOptions.Text += "<option value=\"100\">100 - $200.00";
break;
case "10":
ltOptions.Text = "<option value=\"25\">25 - $190.00";
ltOptions.Text += "<option value=\"50\">50 - $325.00";
ltOptions.Text += "<option value=\"75\">75 - $415.00";
ltOptions.Text += "<option value=\"100\">100 - $500.00";
break;
case "30":
ltOptions.Text = "<option value=\"25\">25 - $375.00";
ltOptions.Text += "<option value=\"50\">50 - $650.00";
ltOptions.Text += "<option value=\"75\">75 - $825.00";
ltOptions.Text += "<option value=\"100\">100 - $1,000.00";
break;
}
}
}