Wednesday, November 6, 2013

E-commerce integration: Part 2 - Dealing with the PayPal nested form problem

This is a series of blog posts covering the e-commerce features of Gallery Server Pro.
Part 1: Add ‘Add to cart’ and ‘View cart’ buttons to your gallery
Part 2: Dealing with the PayPal nested form problem
Part 3: Working with multiple prices and options
Part 4: Using multiple ‘Add to cart’ buttons on a page
Part 5: Selling access to media content
Part 6: Adding e-commerce integration without the Enterprise Edition

Dealing with the PayPal nested form problem

In Part 1 we used the built in e-commerce templates provided in the Enterprise Edition to enable shopping cart functionality in a gallery. In that post we didn’t look too closely at the structure of the PayPal code in the templates. If we did, we would have noticed that the HTML button code provided by PayPal differs in a few key respects from the button code used in the template. For example, here is the code PayPal provides for a simple button:
<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="JP2UFSSRLBSM8">
 <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
 <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>

It’s essentially an HTML form with a few input tags and an image. The HTML specs say forms cannot be nested within another form, yet that’s exactly what would happen if you pasted this code into your UI template. That’s because every ASP.NET Web Forms app contains a form element that encompasses the entire page. It’s there to help manage view state and provide other functions.

Since we can’t have nested forms, we must manually tweak the PayPal code to get rid of its form tag. Then we add a bit of JavaScript to the UI template to manually submit the form when the ‘add to cart’ or ‘view cart’ buttons are clicked. For example, we can turn the above HTML into this that is suitable for a UI template:

<input type='hidden' name='cmd' value='_s-xclick'>
<input type='hidden' name='hosted_button_id' value='JP2UFSSRLBSM8'>
<input type='hidden' name='item_name' value='Photograph - {{:MediaItem.Title}} (Item # {{:MediaItem.Id}})'>
<input id='{{:Settings.ClientId}}_addToCart' type='image' src='https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif' border='0' name='addToCart' alt='PayPal - The safer, easier way to pay online!' style='padding:5px;'>
<span style='display:inline-block;vertical-align:top;margin-top:10px;'>$1.00</span>
<img alt='' border='0' src='https://www.paypalobjects.com/en_US/i/scr/pixel.gif' width='1' height='1'>

Notice we also add an input tag that defines the item name. PayPal uses this element as the item’s description in the checkout process. Then we add this JavaScript to the script tab of the UI template:

var bindAddToCartEvent = function() {
 $('#{{:Settings.ClientId}}_addToCart').click(function() {
  var f = $('form')[0];
  f.action = 'https://www.paypal.com/cgi-bin/webscr';
  f.submit();
  return false;
 });
};

$('#{{:Settings.MediaClientId}}').on('next.{{:Settings.ClientId}} previous.{{:Settings.ClientId}}', function() {
 bindAddToCartEvent();
});

bindAddToCartEvent();

The script registers a click event handler for the ‘add to cart’ button that updates the ASP.NET form element to point to the PayPal URL and then submits it. The page may have additional form elements such as viewstate that end up getting sent, but PayPal ignores any tags it doesn’t recognize, so it works just fine.

The script also adds an event handler that fires each time the user clicks the next or previous button. The handler re-wires the add to cart click handler. Without this step, the add to cart button would work only when the page first loads, and not after clicking next or previous.

So why is this important? If you didn’t know this gotcha with nested forms, you might be tempted to copy embed code from PayPal into your template. But now you know the issue and how to deal with it. This will help you understand the more complicated e-commerce integration examples we’ll dig into in subsequent posts.

No comments: